时间: 2020-08-30|39次围观|0 条评论

  在1.4版本之前,Blade有一个很大的弊端,就是注入源和注入目标有耦合。其中注入目标必须有一个@Module的注解,这个导致同一个注入目标不能被复用。我举一个简单的例子:

Blade – 1.4版本重大更新插图

  这两个页面有一部分相同的内容需要加载,所以对于注入目标来说,数据提供者(数据源)都是同一个。但是在1.4版本之前,每个注入目标必须带一个@Module注解,会导致在不同的界面,针对相同的一部分内容,会有一部分代码会被重新的书写,这种问题非常不好。
  而在1.4版本开始,去掉了@Module注解,如此就不会死板从一个注入源里面去寻找数据,换句话说,在注入的时候,我们给的是哪个数据源,注入时就从哪个数据源去寻找数据。而1.4版本是怎么实现的呢?接下来我们将简单的看一下整个Blade实现原理。
  基本使用请参考:Blade - 基本使用

1. Provider接口

  从1.4版本开始新增了一个接口--Provider接口,在编译时,每个Context都会对应生成一个叫Context类名ProviderImpl名字的类,这个类实现了Provider接口。然后在这个生成类里面,进行了数据的存储,主要是将对应的Context数据放在这个生成类的一个HashMap成员中。例如,如下的Context

public class Context {    @Provides(deepProvides = true, value = "contextString1")    public Demo string1 = new Demo();    @Provides(value = "contextString2")    public String string2;    @Provides(value = "int")    public int code = 2;}

  对应生成的ProviderImpl类是:

public class ContextProviderImpl implements Provider {  private Map<String, Object> pathMap;  public ContextProviderImpl() {    pathMap = new HashMap<>();  }  @Override  public Object find(String id) {    return pathMap.get(id);  }  @Override  public void put(Map<String, Object> map) {    pathMap.putAll(map);  }  public void init(Context source) {    pathMap.put("contextString1", source.string1);;    pathMap.put("contextString2", source.string2);;    pathMap.put("strings", source.string1.string3.strings);;    pathMap.put("demoDemoString", source.string1.string3.demo.demoDemoString);;    pathMap.put("demoString", source.string1.string3);;    pathMap.put("com.example.pby.injectdemo.demo.Demo", source.string1.string3.demo);;    pathMap.put("demo3String", source.string1.string3.string);;    pathMap.put("java.lang.String[]", source.string1.string3.string1);;    pathMap.put("int", source.code);;  }}

  在生成的ProviderImpl类中,主要实现了Provider的两个方法--put方法和find方法。
  其中put方法表示可以将我们自定义的某些数据存在一个Map里面,然后在注入的时候可以从这个数据源来寻找数据。这样做的好处就是,一个注入目标没必要再次定义一个Context类,只需要在基类里面定义一个Context类就行了,然后子类只需要维护一个Map数组就OK了。
  find方法在这里就没必要解释了,很明显就是从存储数据的Map去寻找数据,这个方法在生成的Inject类里面会被调用。
  在生成的ProviderImpl类中,还有一个方法比较重要,那就是init方法。这个方法主要将Context里面的数据放在Map数组里面去,需要注意的是,这里进行了递归,也就是说将子节点的数据也放在了Map数组里面的。

2. Inject类

  在整个框架当中,每个注入目标都会生成一个Inject类,而对注入目标的数据初始化就是这个Inject类里面完成的。我们来看看一个例子:

public class MainActivity_Inject {  public void inject(MainActivity target, Provider source) {    target.strings = (java.lang.String[])(source.find("strings"));    target.demoDemoString = (java.lang.String)(source.find("demoDemoString"));    target.pby = (java.lang.String)(source.find("pby"));    target.string1 = (java.lang.String)(source.find("demo3String"));    target.strings1 = (java.lang.String[])(source.find("java.lang.String[]"));    target.code = (int)(source.find("int"));  }}

  在1.4版本中,彻底的重构了Inject类的结构。在上面的例子当中,我们会发现数据源参数直接是一个Provider接口,初始化代码调用的是Provider接口的find方法。所以从这里,我们可以看出来,在1.4版本中,Context不再持有数据,进而让Provider接口的实现类来持有数据。

3. Blade类

  Blade类也进行了重构,其中,重构之后的代码如下:

public class Blade {    public static void inject(Object target, Object source) {        inject(target, source, null);    }    private static void inject(Object target, Map<String, ?> extraMap) {        inject(target, new EmptyProviderImpl(), extraMap);    }    public static void inject(Object target, Object source, Map<String, ?> extraMap) {        try {            Object targetObject = Class.forName(target.getClass().getName() + "_Inject").newInstance();            Provider sourceObject = (Provider) Class.forName(source.getClass().getName() + "ProviderImpl").newInstance();            if (extraMap != null && !extraMap.isEmpty()) {                sourceObject.getClass().getMethod("put", Map.class).invoke(sourceObject, extraMap);            }            sourceObject.getClass().getMethod("init", source.getClass()).invoke(sourceObject, source);            targetObject.getClass().getMethod("inject", target.getClass(), Provider.class).invoke(targetObject, target, sourceObject);        } catch (ClassNotFoundException e) {            e.printStackTrace();        } catch (IllegalAccessException e) {            e.printStackTrace();        } catch (InstantiationException e) {            e.printStackTrace();        } catch (NoSuchMethodException e) {            e.printStackTrace();        } catch (InvocationTargetException e) {            e.printStackTrace();        }    }}

  在Blade类里面主要做了如下几件事:

  1. 根据传递进来的注入源找到对应的ProviderImpl类, 然后创建它的对象,初始化数据。
  2. 根据传递进来的注入目标找到对应的Inject类,然后调用它的inject方法,同时将ProviderImpl对象传递进去。

  经过如上两步就完成了整个注入过程。

文章转载于:https://www.jianshu.com/p/65002b459042

原著是一个有趣的人,若有侵权,请通知删除

本博客所有文章如无特别注明均为原创。
复制或转载请以超链接形式注明转自起风了,原文地址《Blade – 1.4版本重大更新
   

还没有人抢沙发呢~