Blade - 1.4版本重大更新

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


  这两个页面有一部分相同的内容需要加载,所以对于注入目标来说,数据提供者(数据源)都是同一个。但是在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对象传递进去。

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容