Apache Dubbo是一款高性能、轻量级的开源Java RPC框架,实际应用中,比较传统的使用方式是通过xml文件配置Dubbo Provider和Consumer,完成与Spring的整合。自Spring Boot面世后,注解驱动开发的方式已成为Spring的主流方式。对于Dubbo,Apache官方也提供了dubbo-spring-boot-starter,来降低Dubbo与Spring整合成本,做到通过注解方式极速接入。
Dubbo项目为了与Spring整合,提供了三个核心注解:使用 @Service【导出服务】,使用@Reference【引入服务】,使用 @EnableDubbo 一键完成 Dubbo 所需运行环境的自动配置。在这一篇,主要探究Dubbo的Consumer是如何做到与Spring无缝整合的。
Consumer的整合比较简单,看过源码就会发现,完全是通过 ReferenceAnnotationBeanPostProcessor
这个 BeanPostProcessor 来完成整合工作的。
从 ReferenceAnnotationBeanPostProcessor 的类层次结构图,可以看到其实现了 MergedBeanDefinitionPostProcessor
和 InstantiationAwareBeanPostProcessor
这两个BPP接口。这两个BPP接口为Consumer侧整合,提供了关键的回调方法。
public interface MergedBeanDefinitionPostProcessor extends BeanPostProcessor {
// merged BeanDefinition 回调
/**
* Post-process the given merged bean definition for the specified bean.
* @param beanDefinition the merged bean definition for the bean
* @param beanType the actual type of the managed bean instance
* @param beanName the name of the bean
*/
void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName);
}
public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {
// 实例化阶段的前置处理回调
Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException;
// 实例化阶段的后置处理回调
boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException;
// 依赖注入
/**
* Post-process the given property values before the factory applies them
* to the given bean. Allows for checking whether all dependencies have been
* satisfied, for example based on a "Required" annotation on bean property setters.
* Also allows for replacing the property values to apply, typically through
* creating a new MutablePropertyValues instance based on the original PropertyValues,
* adding or removing specific values.
* @param pvs the property values that the factory is about to apply (never {@code null})
* @param pds the relevant property descriptors for the target bean (with ignored
* dependency types - which the factory handles specifically - already filtered out)
* @param bean the bean instance created, but whose properties have not yet been set
* @param beanName the name of the bean
* @return the actual property values to apply to the given bean (can be the passed-in
* PropertyValues instance), or {@code null} to skip property population
* @throws org.springframework.beans.BeansException in case of errors
* @see org.springframework.beans.MutablePropertyValues
*/
PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException;
}
整合流程简要示意图如下,可分为三步来看,稍微概括下:
@EnableDubbo
注解或Spring Boot的spring.factories
文件扩展机制,引入了 DubboAutoConfiguration
这一配置类;DubboAutoConfiguration
进一步向Spring容器导入 ReferenceAnnotationBeanPostProcessor
这个BPP组件;ReferenceAnnotationBeanPostProcessor
这个BPP分别执行postProcessMergedBeanDefinition()
、postProcessPropertyValues()
方法,完成ControllerA对ServerB的依赖查找和依赖注入。注: ReferenceAnnotationBeanPostProcessor有三个比较重要的属性
1、Class extends Annotation>[] annotationTypes:其实就是@Reference注解类型,是由构造函数传入。Dubbo为了保持兼容,传入了2个:Reference.class, com.alibaba.dubbo.config.annotation.Reference.class;依赖查找的时候就是根据这里的注解类型来过滤。
2、ConcurrentMap injectionMetadataCache:用来缓存 @Reference 依赖查找的InjectionMetadata结果对象;
3、ConcurrentMap injectedObjectsCache:缓存已完成依赖注入的对象。
对照上图,下面再结合源码来进行分析佐证。
依赖查找的过程,即是筛选出当前实例对象中,@Reference
注解的字段和方法并缓存起来,后面进行依赖注入时会用到。
protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof MergedBeanDefinitionPostProcessor) {
MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
// 生命周期回调
bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);
}
}
}
public abstract class AbstractAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter implements MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware, BeanClassLoaderAware, EnvironmentAware, DisposableBean {
private static final int CACHE_SIZE = Integer.getInteger("", 32).intValue();
private final Log logger = LogFactory.getLog(this.getClass());
private final Class<? extends Annotation>[] annotationTypes;
private final ConcurrentMap<String, AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata> injectionMetadataCache;
private final ConcurrentMap<String, Object> injectedObjectsCache;
private ConfigurableListableBeanFactory beanFactory;
private Environment environment;
private ClassLoader classLoader;
private int order;
// 构造函数传入的annotationTypes 就是 @Reference
public AbstractAnnotationBeanPostProcessor(Class... annotationTypes) {
this.injectionMetadataCache = new ConcurrentHashMap(CACHE_SIZE);
this.injectedObjectsCache = new ConcurrentHashMap(CACHE_SIZE);
this.order = 2147483644;
Assert.notEmpty(annotationTypes, "The argument of annotations' types must not empty");
this.annotationTypes = annotationTypes;
}
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
if (beanType != null) {
// 查找需要依赖注入的元素(属性、方法)
InjectionMetadata metadata = this.findInjectionMetadata(beanName, beanType, (PropertyValues)null);
metadata.checkConfigMembers(beanDefinition);
}
}
// 具体依赖查找过程
private InjectionMetadata findInjectionMetadata(String beanName, Class<?> clazz, PropertyValues pvs) {
String cacheKey = StringUtils.hasLength(beanName) ? beanName : clazz.getName();
// 先从缓存找:第一次在injectionMetadataCache中肯定找不到
AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata metadata = (AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata)this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
ConcurrentMap var6 = this.injectionMetadataCache;
synchronized(this.injectionMetadataCache) {
// 双重检查
metadata = (AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata)this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
if (metadata != null) {
metadata.clear(pvs);
}
try {
// 缓存没有,则构建InjectionMetadata
metadata = this.buildAnnotatedMetadata(clazz);
// 放入缓存
this.injectionMetadataCache.put(cacheKey, metadata);
} catch (NoClassDefFoundError var9) {
throw new IllegalStateException("Failed to introspect object class [" + clazz.getName() + "] for annotation metadata: could not find class that it depends on", var9);
}
}
}
}
return metadata;
}
}
AnnotatedInjectionMetadata
;查找 @Reference 注解的字段或方法是通过反射遍历所有 @Reference 字段 或 setter方法来实现的。private AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata buildAnnotatedMetadata(final Class<?> beanClass) {
// 查找 @Reference 注解的属性
Collection<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement> fieldElements = findFieldAnnotationMetadata(beanClass);
Collection<AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement> methodElements = findAnnotatedMethodMetadata(beanClass);
return new AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata(beanClass, fieldElements, methodElements);
}
private List<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement> findFieldAnnotationMetadata(final Class<?> beanClass) {
final List<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement> elements = new LinkedList<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement>();
// 反射遍历每一个字段,查看是否有 @Reference 注解
ReflectionUtils.doWithFields(beanClass, new ReflectionUtils.FieldCallback() {
@Override
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
for (Class<? extends Annotation> annotationType : getAnnotationTypes()) {
// 解析得到 @Reference 注解的各属性
AnnotationAttributes attributes = getAnnotationAttributes(field, annotationType, getEnvironment(), true, true);
if (attributes != null) {
if (Modifier.isStatic(field.getModifiers())) {
if (logger.isWarnEnabled()) {
logger.warn("@" + annotationType.getName() + " is not supported on static fields: " + field);
}
return;
}
// 统一封装为 AnnotatedFieldElement;AnnotatedFieldElement 是当前类的一个内部类,本身继承了Spring的InjectionMetadata
elements.add(new AnnotatedFieldElement(field, attributes));
}
}
}
});
return elements;
}
依赖查找完成后,已经将 @Reference 注解的属性或方法,统一封装成 InjectionMetadata
对象,缓存在AbstractAnnotationBeanPostProcessor的injectionMetadataCache
中;紧接着就开始依赖注入,依赖注入过程中,则利用到依赖查找过程中构建的缓存。
// com.alibaba.spring.beans.factory.annotation.AbstractAnnotationBeanPostProcessor#postProcessPropertyValues
@Override
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
// 第二次调用findInjectionMetadata方法,由于已经有缓存了,直接从缓存就能拿到
InjectionMetadata metadata = findInjectionMetadata(beanName, bean.getClass(), pvs);
try {
// 依赖注入;这个metadata对象是 AbstractAnnotationBeanPostProcessor的内部类AnnotatedFieldElement
metadata.inject(bean, beanName, pvs);
} catch (BeanCreationException ex) {
throw ex;
} catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of @" + getAnnotationType().getSimpleName()
+ " dependencies is failed", ex);
}
return pvs;
}
对于属性注入,同Spring的@Resource等常用注解注入方式类似(可对照Spring提供的CommonAnnotationBeanPostProcessor看下)。不过这里的属性注入执行逻辑是由Dubbo提供,源码在com.alibaba.spring.beans.factory.annotation.AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement
public class AnnotatedFieldElement extends InjectionMetadata.InjectedElement {
private final Field field;
private final AnnotationAttributes attributes;
private volatile Object bean;
protected AnnotatedFieldElement(Field field, AnnotationAttributes attributes) {
super(field, null);
this.field = field;
this.attributes = attributes;
}
@Override
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
Class<?> injectedType = field.getType();
// 【核心】这里生成的是Dubbo Provider接口的代理对象
Object injectedObject = getInjectedObject(attributes, bean, beanName, injectedType, this);
ReflectionUtils.makeAccessible(field);
// 反射,直接属性注入
field.set(bean, injectedObject);
}
}
至此,就完成了 @Reference 注解的依赖查找和依赖注入过程,总体流程和Spring原生的@Resouce、@Autowired 依赖查找注入过程几乎完全一致。
前一节,已经将Dubbo Consumer侧对Provider的依赖注入流程梳理清楚了。但是,还剩一个问题没交代:既然是Dubbo Consumer,那Dubbo服务引入是哪个环节做到的呢?带着这个疑问,回到上文提到的AnnotatedFieldElement.inject()方法
public class AnnotatedFieldElement extends InjectionMetadata.InjectedElement {
@Override
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
Class<?> injectedType = field.getType();
// 【核心】这里生成的是Dubbo Provider接口的代理对象
Object injectedObject = getInjectedObject(attributes, bean, beanName, injectedType, this);
ReflectionUtils.makeAccessible(field);
// 反射,直接属性注入
field.set(bean, injectedObject);
}
}
由于Consumer侧是没有Provider的实现类的,显然依赖注入的肯定是个代理对象。那这个代理对象是如何创造创造出来的?进com.alibaba.spring.beans.factory.annotation.AbstractAnnotationBeanPostProcessor#getInjectedObject方法看看。
// com.alibaba.spring.beans.factory.annotation.AbstractAnnotationBeanPostProcessor#getInjectedObject
protected Object getInjectedObject(AnnotationAttributes attributes, Object bean, String beanName, Class<?> injectedType,
InjectionMetadata.InjectedElement injectedElement) throws Exception {
String cacheKey = buildInjectedObjectCacheKey(attributes, bean, beanName, injectedType, injectedElement);
// 先读缓存:第一次请求肯定没有
Object injectedObject = injectedObjectsCache.get(cacheKey);
if (injectedObject == null) {
// 生成代理对象
injectedObject = doGetInjectedBean(attributes, bean, beanName, injectedType, injectedElement);
// Customized inject-object if necessary
// 写入缓冲
injectedObjectsCache.putIfAbsent(cacheKey, injectedObject);
}
return injectedObject;
}
接着进 org.apache.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor#doGetInjectedBean 方法。
@Override
protected Object doGetInjectedBean(AnnotationAttributes attributes, Object bean, String beanName, Class<?> injectedType,
InjectionMetadata.InjectedElement injectedElement) throws Exception {
/**
* The name of bean that annotated Dubbo's {@link Service @Service} in local Spring {@link ApplicationContext}
*/
String referencedBeanName = buildReferencedBeanName(attributes, injectedType);
// eg: @Reference(timeout=2000,url=dubbo://127.0.0.1:12345,version=1.0.0) org.apache.dubbo.spring.boot.demo.consumer.DemoService
// 可以看出:@Reference注解即使是对同一个Dubbo服务的引用,只要注解的属性稍有不同,比如超时时间不同,那缓存生成的key就会不同
/**
* The name of bean that is declared by {@link Reference @Reference} annotation injection
*/
String referenceBeanName = getReferenceBeanName(attributes, injectedType);
// 这一行的主要作用就是 new了一个ReferenceBean对象出来,并将 @Reference 的属性赋值进去
ReferenceBean referenceBean = buildReferenceBeanIfAbsent(referenceBeanName, attributes, injectedType);
boolean localServiceBean = isLocalServiceBean(referencedBeanName, referenceBean, attributes);
// 将referenceBean 注册为单例bean,交给Spring容器进行管理
registerReferenceBean(referencedBeanName, referenceBean, attributes, localServiceBean, injectedType);
cacheInjectedReferenceBean(referenceBean, injectedElement);
// 真正开始创建代理对象
return getOrCreateProxy(referencedBeanName, referenceBean, localServiceBean, injectedType);
}
private Object getOrCreateProxy(String referencedBeanName, ReferenceBean referenceBean, boolean localServiceBean,
Class<?> serviceInterfaceType) {
if (localServiceBean) { // If the local @Service Bean exists, build a proxy of Service
return newProxyInstance(getClassLoader(), new Class[]{serviceInterfaceType},
newReferencedBeanInvocationHandler(referencedBeanName));
} else {
exportServiceBeanIfNecessary(referencedBeanName); // If the referenced ServiceBean exits, export it immediately
// 调用 ReferenceBean的get方法,触发ReferenceBean的初始化
return referenceBean.get();
}
}
通过上面的分析可知,代理对象的生成肯定是围绕 ReferenceBean 来展开的。不过ReferenceBean的源码,并没有太多逻辑。核心逻辑还在在其父类ReferenceConfig中
public class ReferenceConfig<T> extends ReferenceConfigBase<T> {
public static final Logger logger = LoggerFactory.getLogger(ReferenceConfig.class);
private static final Protocol REF_PROTOCOL = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
private static final Cluster CLUSTER = ExtensionLoader.getExtensionLoader(Cluster.class).getAdaptiveExtension();
private static final ProxyFactory PROXY_FACTORY = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
/**
* The interface proxy reference
*/
private transient volatile T ref;
/**
* The invoker of the reference service
*/
private transient volatile Invoker<?> invoker;
/**
* The flag whether the ReferenceConfig has been initialized
*/
private transient volatile boolean initialized;
/**
* whether this ReferenceConfig has been destroyed
*/
private transient volatile boolean destroyed;
private final ServiceRepository repository;
private DubboBootstrap bootstrap;
// 获取代理对象的入口方法
public synchronized T get() {
if (destroyed) {
throw new IllegalStateException("The invoker of ReferenceConfig(" + url + ") has already destroyed!");
}
if (ref == null) {
// 初始化
init();
}
return ref;
}
public synchronized void init() {
if (initialized) {
// 避免重复初始化
return;
}
if (bootstrap == null) {
// 【核心1】: 如果 DubboBootstrap 为null,代表DubboBootstrap还没初始化过,则触发一次初始化
bootstrap = DubboBootstrap.getInstance();
bootstrap.init();
}
// 省略。。。。
Map<String, String> map = new HashMap<String, String>();
map.put(SIDE_KEY, CONSUMER_SIDE);
// 省略部分URL参数拼接代码。。。。
// 【核心2】: 创建代理对象
ref = createProxy(map);
serviceMetadata.setTarget(ref);
serviceMetadata.addAttribute(PROXY_CLASS_REF, ref);
ConsumerModel consumerModel = repository.lookupReferredService(serviceMetadata.getServiceKey());
consumerModel.setProxyObject(ref);
consumerModel.init(attributes);
// 标记已完成
initialized = true;
// dispatch a ReferenceConfigInitializedEvent since 2.7.4
dispatch(new ReferenceConfigInitializedEvent(this, invoker));
}
@SuppressWarnings({"unchecked", "rawtypes", "deprecation"})
private T createProxy(Map<String, String> map) {
// 省略。。。
if (urls.size() == 1) {
//【核心3】这一步会 new一个 DubboInvoker;Dubbo内部会通过netty建立与Provider的连接,DubboInvoker持有该连接
invoker = REF_PROTOCOL.refer(interfaceClass, urls.get(0));
} else {
List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
URL registryURL = null;
for (URL url : urls) {
invokers.add(REF_PROTOCOL.refer(interfaceClass, url));
if (UrlUtils.isRegistry(url)) {
registryURL = url; // use last registry url
}
}
if (registryURL != null) { // registry url is available
// for multi-subscription scenario, use 'zone-aware' policy by default
URL u = registryURL.addParameterIfAbsent(CLUSTER_KEY, ZoneAwareCluster.NAME);
// The invoker wrap relation would be like: ZoneAwareClusterInvoker(StaticDirectory) -> FailoverClusterInvoker(RegistryDirectory, routing happens here) -> Invoker
invoker = CLUSTER.join(new StaticDirectory(u, invokers));
} else { // not a registry url, must be direct invoke.
invoker = CLUSTER.join(new StaticDirectory(invokers));
}
}
}
// 省略。。。
// 【核心4】采用Dubbo提供的字节码技术生成代理对象,细节见JavassistProxyFactory、org.apache.dubbo.common.bytecode.Proxy
// create service proxy
return (T) PROXY_FACTORY.getProxy(invoker, ProtocolUtils.isGeneric(generic));
}
}
与Provider侧建立连接过程,见org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol#protocolBindingRefer,由于与本文主流程不太相关故不再展开。
@Override
public <T> Invoker<T> protocolBindingRefer(Class<T> serviceType, URL url) throws RpcException {
optimizeSerialization(url);
// create rpc invoker.
DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
invokers.add(invoker);
return invoker;
}
private ReferenceCountExchangeClient buildReferenceCountExchangeClient(URL url) {
ExchangeClient exchangeClient = initClient(url);
return new ReferenceCountExchangeClient(exchangeClient);
}
到这里,Spring对Dubbo Consumer侧的整合流程基本梳理完成了,抛去Dubbo服务引用的细节,流程还是很简单的,仅依靠了ReferenceAnnotationBeanPostProcessor
这个BPP提供的2个回调方法。如果熟悉Spring BeanPostProcessor
扩展机制的话,相信看起来应该很轻松。下一篇,会接着总结下Dubbo Provider与Spring的整合流程。
全文完~