前言
Dubbo的API,除了填补Java的SPI缺点之外,其实还有别的作用,比如它还实现自己的一个IOC(当然也有基于Spring的),还有基于扩展点的AOP,这次就来简单看看它的源码是什么样子的。
Dubbo Version: 2.7.7
Dubbb IOC
injectExtension
首先回到老地方org.apache.dubbo.common.extension.ExtensionLoader
,之前就说过,SPI的操作基本都是围绕这它转的,这次也不例外。
来到这个方法org.apache.dubbo.common.extension.ExtensionLoader#createExtension
,其实IOC和AOP的逻辑都在这里面:
@SuppressWarnings("unchecked")
private T createExtension(String name, boolean wrap) {
// 读取、预处理
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null) {
throw findException(name);
}
try {
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
// 实现IOC核心,完成一些依赖注入的工作,里面顺便能看到ExtensionFactory,它也是一个比较关心的地方
injectExtension(instance);
if (wrap) {
List<Class<?>> wrapperClassesList = new ArrayList<>();
if (cachedWrapperClasses != null) {
wrapperClassesList.addAll(cachedWrapperClasses);
wrapperClassesList.sort(WrapperComparator.COMPARATOR);
Collections.reverse(wrapperClassesList);
}
// AOP的核心就是下面这段
if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
for (Class<?> wrapperClass : wrapperClassesList) {
Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
if (wrapper == null
|| (ArrayUtils.contains(wrapper.matches(), name) && !ArrayUtils.contains(wrapper.mismatches(), name))) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
}
}
// 将包装后的类在注入
initExtension(instance);
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
type + ") couldn't be instantiated: " + t.getMessage(), t);
}
}
这里跟进injectExtension方法:
org.apache.dubbo.common.extension.ExtensionLoader#createExtension
private T injectExtension(T instance) {
// 这里的objectFactory
if (objectFactory == null) {
return instance;
}
try {
// 遍历所有set方法
for (Method method : instance.getClass().getMethods()) {
if (!isSetter(method)) {
continue;
}
/**
* Check {@link DisableInject} to see if we need auto injection for this property
*/
if (method.getAnnotation(DisableInject.class) != null) {
continue;
}
// 获取要set的参数类型
Class<?> pt = method.getParameterTypes()[0];
if (ReflectUtils.isPrimitives(pt)) {
continue;
}
try {
// 获取要set的属性名
String property = getSetterProperty(method);
// 获取要set的对象
Object object = objectFactory.getExtension(pt, property);
if (object != null) {
// 反射调用set注入对象,完成属性注入
method.invoke(instance, object);
}
} catch (Exception e) {
logger.error("Failed to inject via method " + method.getName()
+ " of interface " + type.getName() + ": " + e.getMessage(), e);
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return instance;
}
- 可以看到注入其实很简单,就是单纯的依靠反射去完成而已,这里关键还是如何获取到要set进去的object,即objectFactory.getExtension(pt, property);逻辑。
objectFactory
我们可以追溯一下objectFactory是怎么诞生的,其实就在就在ExtensionLoader的构造函数中:
org.apache.dubbo.common.extension.ExtensionLoader#ExtensionLoader
private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
之前讲Dubbo加载配置的时候就说过了,加载扩展类的时候,是根据SPI描述文件加载的,于是我在不同文件找到如下类型:
在ExtensionLoader第一次构造的时候,就会加载AdaptiveExtensionFactory
,然后在AdaptiveExtensionFactory
中加载SpiExtensionFactory
和SpringExtensionFactory
,下面来看看AdaptiveExtensionFactory
代码:
package org.apache.dubbo.common.extension.factory;
import org.apache.dubbo.common.extension.Adaptive;
import org.apache.dubbo.common.extension.ExtensionFactory;
import org.apache.dubbo.common.extension.ExtensionLoader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* AdaptiveExtensionFactory
*/
@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {
private final List<ExtensionFactory> factories;
public AdaptiveExtensionFactory() {
// 这段逻辑其实就是在加载SpringExtensionFactory和SpiExtensionFactory
ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
for (String name : loader.getSupportedExtensions()) {
list.add(loader.getExtension(name));
}
factories = Collections.unmodifiableList(list);
}
@Override
public <T> T getExtension(Class<T> type, String name) {
for (ExtensionFactory factory : factories) {
// 尝试从Spring上下文获取 或者 从cachedAdaptiveInstance中获取
T extension = factory.getExtension(type, name);
if (extension != null) {
return extension;
}
}
return null;
}
}
再打个断点瞧瞧:
由上面代码我们可以得知,除了SpiExtensionFactory
和SpringExtensionFactory
外,其它情况经过injectExtension方法时,objectFactory都是AdaptiveExtensionFactory
的实例了,不信你可以再打断点看看。
好了,现在我们已经知道这里的objectFactory就是AdaptiveExtensionFactory
了,视角重新回到injectExtension方法的objectFactory.getExtension(pt, property);中,第一次getExtension则会进入到AdaptiveExtensionFactory
的实现中:
org.apache.dubbo.common.extension.factory.AdaptiveExtensionFactory#getExtension
@Override
public <T> T getExtension(Class<T> type, String name) {
for (ExtensionFactory factory : factories) {
T extension = factory.getExtension(type, name);
if (extension != null) {
return extension;
}
}
return null;
}
- 这里的factories就是上面说的SpiExtensionFactory和SpringExtensionFactory。
然后来看看SpringExtensionFactory和SpiExtensionFactory的getExtension方法实现:
org.apache.dubbo.config.spring.extension.SpringExtensionFactory#getExtension
@Override
@SuppressWarnings("unchecked")
public <T> T getExtension(Class<T> type, String name) {
//SPI should be get from SpiExtensionFactory
if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
return null;
}
// 从上下文获取对象
for (ApplicationContext context : CONTEXTS) {
T bean = BeanFactoryUtils.getOptionalBean(context, name, type);
if (bean != null) {
return bean;
}
}
//logger.warn("No spring extension (bean) named:" + name + ", try to find an extension (bean) of type " + type.getName());
return null;
}
org.apache.dubbo.common.extension.factory.SpiExtensionFactory#getExtension
@Override
public <T> T getExtension(Class<T> type, String name) {
// 从cachedAdaptiveInstance中获取
if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
if (!loader.getSupportedExtensions().isEmpty()) {
return loader.getAdaptiveExtension();
}
}
return null;
}
上面Spring的IOC加载相信就不用我多说了,这里稍微跟一下getAdaptiveExtension()方法,里面有一段动态生成字节码的逻辑,稍微跟一下。
动态生成字节码
这里只会看看它的简单逻辑,不会对其深入,对javassist感兴趣的就自行Google补充吧
从上面的loader.getAdaptiveExtension();逐步跟进:
org.apache.dubbo.common.extension.ExtensionLoader#getAdaptiveExtensionClass
private Class<?> getAdaptiveExtensionClass() {
// 讲配置加载的时候已经看过了
getExtensionClasses();
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
// 跟进这里,里面有javassist生成字节码的相关逻辑
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
这里继续跟进createAdaptiveExtensionClass()方法:
org.apache.dubbo.common.extension.ExtensionLoader#createAdaptiveExtensionClass
private Class<?> createAdaptiveExtensionClass() {
// 构建Java代码,非字节码,给接口生成实现(有Adaptive注解的方法有具体实现,没有注解的方法就直接抛UnsupportedOperationException异常)
String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
ClassLoader classLoader = findClassLoader();
org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
// javassist动态构建
return compiler.compile(code, classLoader);
}
关于字节码的动态构建,这里就不详细展开了,有兴趣的自行Google,我也还没细看过,下面简单贴下两个生成前后的接口和类:
原接口
package org.apache.dubbo.rpc;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.Adaptive;
import org.apache.dubbo.common.extension.SPI;
import java.util.Collections;
import java.util.List;
/**
* Protocol. (API/SPI, Singleton, ThreadSafe)
*/
@SPI("dubbo")
public interface Protocol {
int getDefaultPort();
@Adaptive
<T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
@Adaptive
<T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
void destroy();
default List<ProtocolServer> getServers() {
return Collections.emptyList();
}
}
生成后:
package org.apache.dubbo.rpc;
import org.apache.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol {
public void destroy() {
throw new UnsupportedOperationException("The method public abstract void org.apache.dubbo.rpc.Protocol.destroy() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
}
public int getDefaultPort() {
throw new UnsupportedOperationException("The method public abstract int org.apache.dubbo.rpc.Protocol.getDefaultPort() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
}
public org.apache.dubbo.rpc.Exporter export(org.apache.dubbo.rpc.Invoker arg0) throws org.apache.dubbo.rpc.RpcException {
if (arg0 == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
if (arg0.getUrl() == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
org.apache.dubbo.common.URL url = arg0.getUrl();
String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");
org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.export(arg0);
}
public java.util.List getServers() {
throw new UnsupportedOperationException("The method public default java.util.List org.apache.dubbo.rpc.Protocol.getServers() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
}
public org.apache.dubbo.rpc.Invoker refer(java.lang.Class arg0, org.apache.dubbo.common.URL arg1) throws org.apache.dubbo.rpc.RpcException {
if (arg1 == null) throw new IllegalArgumentException("url == null");
org.apache.dubbo.common.URL url = arg1;
String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");
org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.refer(arg0, arg1);
}
}
- 可见接口上没带Adaptive注解的方法,方法体内全都是抛出了UnsupportedOperationExceptioni异常。
Dubbo的IOC代码阅读就到这了,虽然有些细节没一一展开讲,但是相信并不影响整体理解,这里稍微再小结一下:
- Dubbo的依赖注入是依靠反射完成的。
- 在正式注入到目标对象之前,会获取要注入的对象,获取方式有两种,一种是Dubbo的SPI描述文件中已经有实现类了,就直接从缓存里面获取;另一种就是通过@SPI接口本身动态构建字节码,且只对带@Adaptive的方法进行实际实现,不带@Adaptive的则方法体内抛出UnsupportedOperationExceptioni异常。
Dubbo AOP
Dubbo的AOP其实很容易理解,实现其实就是疯狂包装,在包装类上进行增强,用到了装饰者模式,下面先来看看源码。
回到org.apache.dubbo.common.extension.ExtensionLoader#createExtension
方法中,找到如下代码:
List<Class<?>> wrapperClassesList = new ArrayList<>();
// 之前讲加载配置的时候就讲到过这个了,这里不再补充,如果不明白,可看下面提供的demo
if (cachedWrapperClasses != null) {
wrapperClassesList.addAll(cachedWrapperClasses);
wrapperClassesList.sort(WrapperComparator.COMPARATOR);
Collections.reverse(wrapperClassesList);
}
// AOP的核心就是下面这段
if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
for (Class<?> wrapperClass : wrapperClassesList) {
Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
if (wrapper == null
|| (ArrayUtils.contains(wrapper.matches(), name) && !ArrayUtils.contains(wrapper.mismatches(), name))) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
}
核心是这一段(T) wrapperClass.getConstructor(type).newInstance(instance)
可见是调用构造器注入原始对象进行包装的,我们可以看看官方的代码是不是这样:
为了便于理解,这里再自定义一个例子,注意这是在一个Dubbo项目当中,请注意相关依赖的配置:
- 红框以外的代码都无需关注。
Person.java
import org.apache.dubbo.common.extension.SPI;
/**
* @author wenjie
*/
@SPI("person")
public interface Person {
void hello();
}
Student.java
import store.wenjie.www.dubboproviderdemo.test.Person;
/**
* @author wenjie
*/
public class Student implements Person {
public void hello() {
System.out.println("I am student");
}
}
StudentWrapper.java
import store.wenjie.www.dubboproviderdemo.test.Person;
/**
* @author wenjie
*/
public class StudentWrapper implements Person {
private Person person;
public StudentWrapper(Person person) {
this.person = person;
}
public void hello() {
System.out.println("before");
person.hello();
System.out.println("after");
}
}
测试类Test.java
import org.apache.dubbo.common.extension.ExtensionLoader;
public class Test1 {
public static void main(String[] args) {
ExtensionLoader<Person> loader = ExtensionLoader.getExtensionLoader(Person.class);
Person studesnt = loader.getExtension("student");
studesnt.hello();
}
}
配置store.wenjie.www.dubboproviderdemo.test.Person
,等下还要加上包装类:
student=store.wenjie.www.dubboproviderdemo.test.impl.Student
执行上面的Test.java主函数,执行结果如下:
更改配置store.wenjie.www.dubboproviderdemo.test.Person
如下:
student=store.wenjie.www.dubboproviderdemo.test.impl.Student
filter=store.wenjie.www.dubboproviderdemo.test.wrapper.StudentWrapper
再执行一次测试:
就是这么简单= =,相信不用我再过多阐述了。
AOP的源码阅读就先这样了,如果有什么遗漏的欢迎指出补充~