1.抛出问题
dubbo
的设计思路是微内核+插件,Filter
等插件被dubbo
创建,而不是被spring
创建。
Filter
和spring
两不相认,如果想在Filter
中使用被spring
管理的对象,注入spring
的bean,怎么办?
2.先给出结论
在Filter
中新建一个setter
方法。此方法名称形如setAbc
,有且仅有一个参数。
在spring
上下文中定义一个名为“abc”的bean,类型要对。
如此即可实现,在Fliter
被实例化后,此setter
被调用,传入名为“abc”的bean(一定要类型正确,名字对上更好,详细算法见3.2节结尾处)。
(4.章介绍另一种机制,spring
利用instrumentation
,load-time-weaver
令非spring
管理的对象也能使用spring
基础设施,开阔思路,不建议用)
3.源码分析
这部分尽可能从现象到原因探索。
- 插件实例化后可被调
setter
,参数从objectFactory
中来 -
objectFactory
是ExtensionFactory
接口类型的,SpringExtensionFactory
是此接口的一个实现类 -
SpringExtensionFactory
在使用dubbo-spring
时被设置好
3.1.插件实例化后可被调setter,参数从objectFactory中来
Filter
中的setter
可被dubbo
调起,在spring
的ApplicationContext
中按名字查找并塞进一个bean。
Filter
等插件被按SPI
机制管理,com.alibaba.dubbo.common.extension.ExtensionLoader<T>
的public T getExtension(String name)
函数被用于加载特定的插件,其中有一层缓存,真正的创建过程在private T createExtension(String name)
函数中。createExtension(...)
函数源码如下
private T createExtension(String name) {
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);
}
injectExtension(instance);
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
for (Class<?> wrapperClass : wrapperClasses) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
type + ") could not be instantiated: " + t.getMessage(), t);
}
}
可见其中的主要步骤:
- 调用
Class
的newInstance()
方法,实例化插件 - 调用
ExtensionLoader<T>
的injectExtension(T instance)
,从objectFactory
中拿一个bean,调用插件的setter
方法 - 处理一下
wrapper
再看injectExtension(...)
函数源码:
private T injectExtension(T instance) {
try {
if (objectFactory != null) {
for (Method method : instance.getClass().getMethods()) {
if (method.getName().startsWith("set")
&& method.getParameterTypes().length == 1
&& Modifier.isPublic(method.getModifiers())) {
Class<?> pt = method.getParameterTypes()[0];
try {
String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
Object object = objectFactory.getExtension(pt, property);
if (object != null) {
method.invoke(instance, object);
}
} catch (Exception e) {
logger.error("fail to inject via method " + method.getName()
+ " of interface " + type.getName() + ": " + e.getMessage(), e);
}
}
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return instance;
}
可见其中的主要步骤:
- 遍历方法
- 如果是
setter
(即,方法名以“set”开头,有1个参数,是public
) - 得到属性名,从
objectFactory
中得到bean - 调用
setter
3.2.objectFactory是ExtensionFactory接口类型的,SpringExtensionFactory是此接口的一个实现类
属性private final ExtensionFactory objectFactory
在私有构造函数中被初始化:
private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
接口ExtensionFactory
中只有一个方法,见源码:
@SPI
public interface ExtensionFactory {
/**
* Get extension.
*
* @param type object type.
* @param name object name.
* @return object instance.
*/
<T> T getExtension(Class<T> type, String name);
}
按照dubbo
的SPI
机制的惯例,见META-INF/dubbo/internal/com.alibaba.dubbo.common.extension.ExtensionFactory
内容:
adaptive=com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory
spi=com.alibaba.dubbo.common.extension.factory.SpiExtensionFactory
spring=com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory
关注“spring”名称对应的SpringExtensionFactory
,其源码片段:
public <T> T getExtension(Class<T> type, String name) {
for (ApplicationContext context : contexts) {
if (context.containsBean(name)) {
Object bean = context.getBean(name);
if (type.isInstance(bean)) {
return (T) bean;
}
}
}
logger.warn("No spring extension(bean) named:" + name + ", try to find an extension(bean) of type " + type.getName());
for (ApplicationContext context : contexts) {
try {
return context.getBean(type);
} catch (NoUniqueBeanDefinitionException multiBeanExe) {
throw multiBeanExe;
} catch (NoSuchBeanDefinitionException noBeanExe) {
if (logger.isDebugEnabled()) {
logger.debug("Error when get spring extension(bean) for type:" + type.getName(), noBeanExe);
}
}
}
logger.warn("No spring extension(bean) named:" + name + ", type:" + type.getName() + " found, stop get bean.");
return null;
}
可见其中查找逻辑:
- 遍历
ApplicationContext
- 先按名字找bean,要求类型必须正确
- 如果上一步找不到,就不考虑此名字了。改为仅按类型找,要求比类型bean必须是唯一的
3.3.SpringExtensionFactory在使用dubbo-spring时被设置好
查找SpringExtensionFactory
在哪里被调用,仅ReferenceBean
和ServiceBean
,这两个类是使用dubbo-spring
一定绕不开的。
涉及SpringExtensionFactory
的逻辑相似,ReferenceBean
和ServiceBean
均实现了ApplicationContextAware
,回调方法中有
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
SpringExtensionFactory.addApplicationContext(applicationContext);
}
(ServiceBean
的此方法中多了增加监听器的功能)
可见其中调用了SpringExtensionFactory
的静态方法public static void addApplicationContext(ApplicationContext context)
4.另一种做法
核心思路是利用instrumentation
和load-time-weaver
。
启动时加入-javaagent
参数,在自定义Filter
上加@Configurable
注解。
不建议的理由主要是需要修改启动脚本。