建议先阅读上一篇文章Dubbo之@Activate,效果更佳。
关键词:依赖注入
参数注入?大部分的说法是依赖注入。因为Dubbo是通过方法参数来实现注入的,我就叫它参数注入了吧。
我将Dubbo中的参数注入分成两类,一类是通过set方法注入,另一类是包装类,是通过构造器注入,只不过这里的构造器是包装类的构造器。那什么是Set方法注入,什么又是包装注入呢,两者有啥区别呢?
█ 如何使用
先来看看Set方法注入
①修改DogService,在类中添加下面的内容。
private AnimalService animalService;
// com.alibaba.dubbo.common.compiler.Compiler
private Compiler compiler;
public void setDog(AnimalService animalService) {
this.animalService = animalService;
}
public void setJavassist(Compiler compiler) {
this.compiler = compiler;
}
public void showResult() {
System.out.println(animalService.getClass().getName());
System.out.println(compiler.getClass().getName());
}②运行代码。
public class DITest {
public static void main(String[] args) {
ExtensionLoader<AnimalService> loader = ExtensionLoader.getExtensionLoader(AnimalService.class);
AnimalService animalService = loader.getExtension("dog");
// 因为要调用showResult方法,强转成DogService
DogService dogService = (DogService)animalService;
// 输出结果:
// study.rui.dubbo.AnimalService$Adaptive
// com.alibaba.dubbo.common.compiler.support.AdaptiveCompiler
dogService.showResult();
}
}结果输出的是:
study.rui.dubbo.AnimalService$Adaptive
com.alibaba.dubbo.common.compiler.support.AdaptiveCompiler
█ 源码解读
看见上面的输出结果中类的名称,是否有点眼熟。看过《Dubbo之@Adaptive》的同学应该会知道,这是getAdaptiveExtension方法返回的结果。你是否猜想,Dubbo的注入是注入@Adaptive修饰的类或根据@Adaptive方法动态编译产生的类吗?下面,我们来看看源码。
injectExtension方法实现了注入的功能,在实现类的实例被创建之后开始注入
private T injectExtension(T instance) {
try {
// 这里的objectFactory是AdaptiveExtensionFactory
// 在getExtensionLoader方法中的代码:
// this.objectFactory = type == ExtensionFactory.class ? null : (ExtensionFactory)getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension();
// 可见只要参数接口类型不是ExtensionFactory,结果就是AdaptiveExtensionFactory(因为此类上标注了@Adaptive)
if (this.objectFactory != null) {
// 获取当前扩展实现类所有的方法,getMethods只会获取到public方法
Method[] var2 = instance.getClass().getMethods();
int var3 = var2.length;
// 遍历所有方法
for(int var4 = 0; var4 < var3; ++var4) {
Method method = var2[var4];
// 方法满足四个条件就会执行注入:
// 1.set开头。
// 2.只有一个参数。
// 3.访问修饰符是public(感觉这个是多余的,因为getMethods已经只能获取public方法了)。
// 4.方法上没有标注@DisableInject
if (method.getName().startsWith("set") && method.getParameterTypes().length == 1 && Modifier.isPublic(method.getModifiers()) && method.getAnnotation(DisableInject.class) == null) {
Class pt = method.getParameterTypes()[0];
try {
// 5.方法名要大于3,因为set就占用了3个长度
// 截取方法名,获取配置key
// 比如setName,获取到的值是:name
String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
// 根据截取后获取到的配置key,去获取对应的扩展实现类
// 实际调用的就是AdaptiveExtensionFactory的getExtension方法了
Object object = this.objectFactory.getExtension(pt, property);
// 找到了就注入
if (object != null) {
method.invoke(instance, object);
}
} catch (Exception var9) {
logger.error("fail to inject via method " + method.getName() + " of interface " + this.type.getName() + ": " + var9.getMessage(), var9);
}
}
}
}
} catch (Exception var10) {
logger.error(var10.getMessage(), var10);
}
return instance;
}
}根据截取方法名得到的配置key,去获取扩展实现类。下面来看看AdaptiveExtensionFactory的getExtension方法。
@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {
// 用于保存所有的普通ExtensionFactory
// 过滤掉了@Adaptive修饰的,和包装类
private final List<ExtensionFactory> factories;
public AdaptiveExtensionFactory() {
ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
List<ExtensionFactory> list = new ArrayList();
// 注意getSupportedExtensions这里获取到adaptive本身
Iterator var3 = loader.getSupportedExtensions().iterator();
// 获取到SpiExtensionFactory和SpringExtensionFactory
while(var3.hasNext()) {
String name = (String)var3.next();
list.add(loader.getExtension(name));
}
// 顺序一定是SpiExtensionFactory在前,SpringExtensionFactory在后
this.factories = Collections.unmodifiableList(list);
}
public <T> T getExtension(Class<T> type, String name) {
// 依次从SpiExtensionFactory、SpringExtensionFactory 中去获取
Iterator var3 = this.factories.iterator();
Object extension;
do {
if (!var3.hasNext()) {
return null;
}
ExtensionFactory factory = (ExtensionFactory)var3.next();
extension = factory.getExtension(type, name);
} while(extension == null);
// 获取到了就返回实现类实例
// 获取不到就返回null
return extension;
}
}factories里的顺序为什么是SpiExtensionFactory在前,SpringExtensionFactory在后?
猜想:是不是配置文件中的顺序决定的?
spring=com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory
adaptive=com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory
spi=com.alibaba.dubbo.common.extension.factory.SpiExtensionFactoryDubbo的配置顺序是上面这样的。和猜想的不对,那是怎么回事呢?关键在于getSupportedExtensions方法。
public Set<String> getSupportedExtensions() {
Map<String, Class<?>> clazzes = this.getExtensionClasses();
// 这里将结果放进了TreeSet中,TreeSet是具有排序功能的Set
// TreeSet的实现用的是TreeMap。如果没有指定排序器,TreeMap用的是key的排序规则
// 这里的key是String类型的,所以会使用String的排序规则。(以后有机会聊聊TreeMap)
// 所以,按照排序spi要比spring在前
return Collections.unmodifiableSet(new TreeSet(clazzes.keySet()));
}下面来看看SpiExtensionFactory。
public <T> T getExtension(Class<T> type, String name) {
// 前置判断类型是否是接口,是否标注了@SPI注解
if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
// 就两种结果:
// 获取接口类型的扩展实现类数量大于0,就创建自适应扩展类实例
// 否则返回null。
if (loader.getSupportedExtensions().size() > 0) {
// 如果没有实现类上标注@Adaptive,或者接口方法也没有@Adaptive这里会报错
return loader.getAdaptiveExtension();
}
}
return null;
}可见。依赖注入,要么不注入,要么注入的就是自适应扩展实现类。
// 静态方法也是可以正常注入的
public static void setDog(AnimalService animalService) {
System.out.println(animalService.getClass().getName());
}
// 与方法的返回值也没有关系。有关系的是方法必须是public,和参数必须是一个
public String setDog(AnimalService animalService) {
System.out.println(animalService.getClass().getName());
return "";
}
// setCat和setDog注入的都是同一个study.rui.dubbo.AnimalService$Adaptive对象。(为什么?你想想上面的分析过程)
// set后面的属性名要是配置key。不是配置key的话也不会注入
public void setCat(AnimalService animalService) {
System.out.println(animalService.getClass().getName());
}
再来看看包装类注入。
在createExtension方法中:
this.injectExtension(instance);
// 在方法set的依赖注入完成之后,紧接着来包装类的注入
Set<Class<?>> wrapperClasses = this.cachedWrapperClasses;
Class wrapperClass;
if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
// 遍历所有的包装类,然后通过包装类的有参构造函数创建对象
// 继续injectExtension,完成包装类里面的set方法注入,将包装类对象赋值给instance
for(Iterator var5 = wrapperClasses.iterator(); var5.hasNext(); instance = this.injectExtension(wrapperClass.getConstructor(this.type).newInstance(instance))) {
wrapperClass = (Class)var5.next();
}
}
// 可以看出,如果存在包装类,这里返回的就是包装类实例了
return instance;通过代码:wrapperClass.getConstructor(this.type).newInstance(instance),是不是可以猜测:包装类的特征了,要有一个有参构造函数,参数的类型就是接口的类型。
包装类是从cachedWrapperClasses集合里面取的,那是什么时候被放进去的呢?在loadClass中:
// 具体看isWrapperClass
else if (this.isWrapperClass(clazz)) {
Set<Class<?>> wrappers = this.cachedWrapperClasses;
if (wrappers == null) {
this.cachedWrapperClasses = new ConcurrentHashSet();
wrappers = this.cachedWrapperClasses;
}
wrappers.add(clazz);
}
// 有一个接口实现类,它有个构造函数,构造函数只有一个接口类型的参数
private boolean isWrapperClass(Class<?> clazz) {
try {
// 如果不存在,这个方法会报异常
clazz.getConstructor(this.type);
return true;
} catch (NoSuchMethodException var3) {
return false;
}
}█ 总结
通过getExtension获取扩展实现类的时候,如果有包装类,就将实际的实现类包装进包装类里,将包装类的实例返回。通过getAdaptiveExtension和getActivateExtension就没有这样的处理逻辑。
set方法注入和包装类注入的区别就是,set方法注入返回的是实际实现类本身,包装类注入的话,返回的是包装类实例。