Dubbo源码篇08---依赖注入和AOP在Dubbo中的实现
- 引言
- 依赖注入
- 使用实践
- Wrapper机制
- 使用实践
- 注意
引言
前面三篇文章,我们从使用到原理,详细分析了一遍Dubbo SPI机制的实现原理:
- Dubbo源码篇05—SPI神秘的面纱—使用篇
- Dubbo源码篇06—SPI神秘的面纱—原理篇—上
- Dubbo源码篇07—SPI神秘的面纱—原理篇—下
有了前面的铺垫,本文理解起来将会十分的轻松,对于依赖注入,我们首先想到的就是Spring中的@Autowired和@Resource注解,而AOP功能,则会首先联想到@Aspect注解。
对于Dubbo而言,其采用的是微核心+插件式
架构,通常微核心都会采用 Factory、IoC、OSGi 等方式管理插件生命周期。考虑 Dubbo 的适用面,不想强依赖 Spring 等 IoC 容器,自已造一个小的 IoC 容器,也觉得有点过度设计,所以采用最简单的 Factory 方式管理插件。
所以对于Dubbo而言,其依赖注入和AOP也都是在其内部IOC基础上实现的,实现相比于Spring而言简单许多,所以废话不多说,我们直接开始Dubbo 依赖注入和AOP实现原理研究。
本文以普通扩展类的加载为总线,从使用层面验证之前原理篇中分析过的,关于依赖注入和Wrapper机制的代码。
依赖注入
我们先来简单回顾一下依赖注入部分的源代码:
createExtension方法是创建普通扩展类的核心方法:
injectExtension依赖注入的核心代码如下所示:
private T injectExtension(T instance) {
// 这里的扩展注入器不为空,在ExtensionLoader创建时会获取ExtensionInjector的自适应扩展类
// 这里的injector即是ExtensionInjector扩展接口的的自适应扩展类AdaptiveExtensionInjector
// 如果为空则直接返回当前实例对象,不进行依赖注入
if (injector == null) {
return instance;
}
try {
// 遍历所有方法 --- 只包括本类和父类的public方法
for (Method method : instance.getClass().getMethods()) {
// 如果当前方法不是一个setXXXX()方法则继续处理下一个方法
// public + set开头 + 只有一个参数
if (!isSetter(method)) {
continue;
}
//校验当前方法上携带了@DisableInject注解吗,即禁止注入的当前属性,符合则跳过
if (method.isAnnotationPresent(DisableInject.class)) {
continue;
}
// 第一个参数是原生类型(String、Boolean、Integer ...) 跳过
Class<?> pt = method.getParameterTypes()[0];
if (ReflectUtils.isPrimitives(pt)) {
continue;
}
try {
// 获取set方法对应的成员变量如setProtocol 属性为protocol
String property = getSetterProperty(method);
// 根据参数类型如Protocol和属性名字如protocol获取应该注入的对象
Object object = injector.getInstance(pt, property);
if (object != null) {
method.invoke(instance, object);
}
}...
}
}...
return instance;
}
扩展依赖注入默认情况下为AdaptiveExtensionInjector:
AdaptiveExtensionInjector作为默认的扩展依赖注入自适应扩展点,当其被初始化时,会通过getExtensionLoader方法拿到ExtensionInjector扩展类型的所有扩展实现:
@Override
public void initialize() throws IllegalStateException {
ExtensionLoader<ExtensionInjector> loader = extensionAccessor.getExtensionLoader(ExtensionInjector.class);
List<ExtensionInjector> list = new ArrayList<ExtensionInjector>();
for (String name : loader.getSupportedExtensions()) {
list.add(loader.getExtension(name));
}
injectors = Collections.unmodifiableList(list);
}
@Override
public <T> T getInstance(Class<T> type, String name) {
// 遍历所有的扩展注入器并调用getinstance()方法,并取第一个返回
for (ExtensionInjector injector : injectors) {
T extension = injector.getInstance(type, name);
if (extension != null) {
return extension;
}
}
return null;
}
ExtensionInjector是 Dubbo 源码中为数不多的自适应扩展实现实例,ExtensionInjector扩展接口的自适应扩展实现就是AdaptiveExtensionInjector,接口实现类默认有三个
- SpiExtensionInjector: 根据实例 class 从 ExtensionLoader 中获取实例
- ScopeBeanExtensionInjector: 从 Dubbo 自定义的beanfactory中获取实例
- SpringExtenisonInjector: 从 Spring 的beanfactory中获取实例
这个AdaptiveExtensionInjector在初始化的时候会获取所有的ExtensionInjector的扩展,非自适应的,它本身是自适应的扩展。
使用实践
测试环境:
@SPI("spring")
public interface FrameWork {
String getName(URL url);
String getInfo();
}
public class Spring implements FrameWork {
private FrameWork springBoot;
public void setSpringBoot(FrameWork springBoot) {
this.springBoot = springBoot;
}
@Override
public String getName(URL url) {
return "spring";
}
@Override
public String getInfo() {
return springBoot.getInfo()+" 流行的Spring框架";
}
}
public class SpringBoot implements FrameWork{
@Override
public String getName(URL url) {
return "springBoot";
}
@Override
public String getInfo() {
return "自动化的SpringBoot框架";
}
}
SPI文件:
spring=com.adaptive.Spring
springBoot=com.adaptive.SpringBoot
guice=com.adaptive.Guice
测试类:
ApplicationModel applicationModel = ApplicationModel.defaultModel();
ExtensionLoader<FrameWork> extensionLoader = applicationModel.getExtensionLoader(FrameWork.class);
FrameWork frameWork = extensionLoader.getExtension("spring");
System.out.println(frameWork.getInfo());
直接运行上面的测试用例,会抛出异常,因为我们期望的是借助SpiExtensionInjector获取别名为springBoot的扩展实例进行注入,但是SpiExtensionInjector默认的行为是获取当前类型的自适应扩展点:
我们的扩展接口FrameWork 中并没有使用@Adaptive注解标注需要自适应扩展的接口方法,所以会因为找不到扩展标记点而抛出异常。
为了达到我们的预期,我们可以自定义一个CustomSpiExtensionInjector:
public class CustomSpiExtensionInjector implements ExtensionInjector {
private ExtensionAccessor extensionAccessor;
@Override
public <T> T getInstance(Class<T> type, String name) {
return extensionAccessor.getExtension(type,name);
}
@Override
public void setExtensionAccessor(ExtensionAccessor extensionAccessor) {
this.extensionAccessor = extensionAccessor;
}
}
对应SPI文件:
customSpiExtensionInjector=com.adaptive.CustomSpiExtensionInjector
但是直接向上面这样写还是会存在问题,根本原因在于返回的ExtensionInjector集合中的顺序问题:
loader.getSupportedExtensions()方法返回的是经过字母表排序过的扩展类集合:
所以我们目前无法直接对ExtensionInjector进行排序,只能通过扩展实现类的别名来间接控制顺序。
为了防止我们自定义的ExtensionInjector把dubbo内部默认的依赖注入过程搅乱,需要通过注解打标记,限制我们自定义的ExtensionInjector所能处理的依赖注入范围:
public class CustomSpiExtensionInjector implements ExtensionInjector {
private ExtensionAccessor extensionAccessor;
@Override
public <T> T getInstance(Class<T> type, String name) {
if (!type.isAnnotationPresent(CustomInjector.class)) {
return null;
}
return extensionAccessor.getExtension(type, name);
}
@Override
public void setExtensionAccessor(ExtensionAccessor extensionAccessor) {
this.extensionAccessor = extensionAccessor;
}
}
在扩展类接口上打标记:
@CustomInjector
@SPI("spring")
public interface FrameWork {
String getName(URL url);
String getInfo();
}
再次运行测试用例,可以得到期望输出:
扩展依赖注入器(ExtensionInjector)集合无法自定义排序规则 #12402
Wrapper机制
wrapper机制核心代码就在扩展实例依赖注入处理过后,源码如下,我们来简单复习一下:
private T createExtension(String name, boolean wrap) {
Class<?> clazz = getExtensionClasses().get(name);
...
try {
T instance = (T) extensionInstances.get(clazz);
if (instance == null) {
extensionInstances.putIfAbsent(clazz, createExtensionInstance(clazz));
instance = (T) extensionInstances.get(clazz);
instance = postProcessBeforeInitialization(instance, name);
injectExtension(instance);
instance = postProcessAfterInitialization(instance, name);
}
//和自适应扩展点创建的不同逻辑: 判断是否需要对当前扩展实例进行装饰
if (wrap) {
List<Class<?>> wrapperClassesList = new ArrayList<>();
//当前扩展类相关wrapper类型搜集工作在getExtensionClasses中完成
if (cachedWrapperClasses != null) {
wrapperClassesList.addAll(cachedWrapperClasses);
wrapperClassesList.sort(WrapperComparator.COMPARATOR);
Collections.reverse(wrapperClassesList);
}
//wrapper class搜集是满足存在一个单参数的拷贝构造函数,并且参数类型为当前扩展类类型
if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
//不断循环,套娃创建一层层的装饰器对象
for (Class<?> wrapperClass : wrapperClassesList) {
//Wrapper注解用于实现按条件装饰
Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
//如果wrapper class类上不存在Wrapper注解,那么表示装饰不需要满足任何条件
//否则,需要判断条件是否满足,满足才会进行装饰
boolean match = (wrapper == null) ||
((ArrayUtils.isEmpty(wrapper.matches()) || ArrayUtils.contains(wrapper.matches(), name)) &&
!ArrayUtils.contains(wrapper.mismatches(), name));
if (match) {
//满足则进入装饰流程
//1.实例化当前装饰类,采用的是单参的拷贝构造函数
//2.执行依赖注入流程
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
//3.执行后置处理流程
instance = postProcessAfterInitialization(instance, name);
}
}
}
}
// Warning: After an instance of Lifecycle is wrapped by cachedWrapperClasses, it may not still be Lifecycle instance, this application may not invoke the lifecycle.initialize hook.
//调用初始化接口---注意上面警告信息,也就是说经过包装后,我们的包装对象未必继承lifecycle接口,因此初始化调用也就不会发生了
initExtension(instance);
return instance;
} ...
}
这里简单说明一下装饰条件指的是什么:
- 首先,如果某个扩展类型存在某个扩展实现,该扩展实现类中存在一个拷贝构造函数,类型为当前扩展类型,则该扩展实现类会被搜集作为当前扩展实现的wrapper装饰类
- 如果我们想限制当前wrapper对象只对满足条件的扩展实现类进行装饰,可以在wrapper对象类上标注@Wrapper注解,利用Wrapper注解中的属性作为装饰条件
@Retention(RetentionPolicy.RUNTIME)
public @interface Wrapper {
/**
* 只对扩展别名存在于matches数组中的扩展实现进行装饰
*/
String[] matches() default {};
/**
* 如果扩展别名存在于matches数组中,则不会对当前扩展实现进行装饰
*/
String[] mismatches() default {};
/**
* 用于扩展类型的多个wrapper实现类进行排序
*/
int order() default 0;
}
使用实践
更改上面测试用例中扩展实现类:
@Wrapper(matches = "spring")
public class SpringBoot implements FrameWork{
private FrameWork wrapper;
public SpringBoot(FrameWork frameWork) {
this.wrapper = frameWork;
}
@Override
public String getName(URL url) {
return "springBoot";
}
@Override
public String getInfo() {
return wrapper.getInfo()+" 自动化的SpringBoot框架";
}
}
@Wrapper(matches = "springBoot")
public class Guice implements FrameWork{
private FrameWork wrapper;
public Guice(FrameWork frameWork) {
this.wrapper = frameWork;
}
@Override
public String getName(URL url) {
return "guice";
}
@Override
public String getInfo() {
return wrapper.getInfo()+" google 开源的轻量级IOC框架";
}
}
public class Spring implements FrameWork {
@Override
public String getName(URL url) {
return "spring";
}
@Override
public String getInfo() {
return "流行的Spring框架";
}
}
测试类:
ApplicationModel applicationModel = ApplicationModel.defaultModel();
ExtensionLoader<FrameWork> extensionLoader = applicationModel.getExtensionLoader(FrameWork.class);
FrameWork frameWork = extensionLoader.getExtension("spring");
System.out.println(frameWork.getInfo());
很明显,只有SpringBoot对Spring进行了装饰,而Guice没有对Spring进行装饰,因为其类上的@Wrapper注解限制了其只会对扩展别名为springBoot的扩展实现进行装饰。
注意
如果我们更改测试用例,尝试获取扩展别名为springBoot的扩展实现,则会抛出扩展不存在的异常:
ApplicationModel applicationModel = ApplicationModel.defaultModel();
ExtensionLoader<FrameWork> extensionLoader = applicationModel.getExtensionLoader(FrameWork.class);
FrameWork frameWork = extensionLoader.getExtension("springBoot");
System.out.println(frameWork.getInfo());
这个原因是FrameWork的Wrapper装饰类会被单独搜集起来,而不会作为普通扩展实现类保存起来:
loadClass方法是在dubbo加载当前扩展类型所有SPI文件流程中被调用的:(如有遗忘,回看前面两篇原理篇)
所以,当我们尝试从extensionClasses集合中获取别名为springBoot的普通扩展类型时,自然会找不到,而抛出异常。