Spring Boot整合Dubbo Consumer

文章目录

  • 一、前言
  • 二、Consumer 整合
    • 2.1 ReferenceAnnotationBeanPostProcessor 类层次结构
    • 2.2 Consumer整合流程
    • 2.3 源码分析
      • 2.3.1 依赖查找
      • 2.3.2 依赖注入
  • 三、Consumer 侧服务引入
    • 3.1 代理对象生成

一、前言

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 整合

Consumer的整合比较简单,看过源码就会发现,完全是通过 ReferenceAnnotationBeanPostProcessor 这个 BeanPostProcessor 来完成整合工作的。

2.1 ReferenceAnnotationBeanPostProcessor 类层次结构

Spring Boot整合Dubbo Consumer_第1张图片

从 ReferenceAnnotationBeanPostProcessor 的类层次结构图,可以看到其实现了 MergedBeanDefinitionPostProcessorInstantiationAwareBeanPostProcessor 这两个BPP接口。这两个BPP接口为Consumer侧整合,提供了关键的回调方法。

  • MergedBeanDefinitionPostProcessor
    作用:在bean实例化完成后调用,可用来修改merged BeanDefinition的一些properties 或缓存一些meta信息以便后续的回调使用
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);

}
  • InstantiationAwareBeanPostProcessor
    作用:对象实例化前、后的生命周期回调;对象实例化之后,设置PropertyValue的生命周期回调
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; }

2.2 Consumer整合流程

整合流程简要示意图如下,可分为三步来看,稍微概括下:

  • 1、应用通过 @EnableDubbo注解或Spring Boot的spring.factories文件扩展机制,引入了 DubboAutoConfiguration这一配置类;
  • 2、DubboAutoConfiguration 进一步向Spring容器导入 ReferenceAnnotationBeanPostProcessor这个BPP组件;
  • 3、当Ioc容器启动时(AbstractApplicationContext.refresh()),假设ControllerA 依赖了 Dubbo服务 ServerB,则在 ControllerA 进行实例化之后、初始化之前,ReferenceAnnotationBeanPostProcessor 这个BPP分别执行postProcessMergedBeanDefinition()postProcessPropertyValues() 方法,完成ControllerA对ServerB的依赖查找和依赖注入。
    Spring Boot整合Dubbo Consumer_第2张图片

注: ReferenceAnnotationBeanPostProcessor有三个比较重要的属性

1、Class[] annotationTypes:其实就是@Reference注解类型,是由构造函数传入。Dubbo为了保持兼容,传入了2个:Reference.class, com.alibaba.dubbo.config.annotation.Reference.class;依赖查找的时候就是根据这里的注解类型来过滤。

2、ConcurrentMap injectionMetadataCache:用来缓存 @Reference 依赖查找的InjectionMetadata结果对象;

3、ConcurrentMap injectedObjectsCache:缓存已完成依赖注入的对象。

2.3 源码分析

对照上图,下面再结合源码来进行分析佐证。

2.3.1 依赖查找

依赖查找的过程,即是筛选出当前实例对象中,@Reference注解的字段和方法并缓存起来,后面进行依赖注入时会用到。

  • 1、AbstractAutowireCapableBeanFactory#applyMergedBeanDefinitionPostProcessors
    在对象实例化出来之后,Spring执行对merged BeanDefinition的生命周期回调;从源码可以看到只有 MergedBeanDefinitionPostProcessor 类型的BPP才有处理资格
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);
      }
   }
}
  • 2、ReferenceAnnotationBeanPostProcessor的postProcessMergedBeanDefinition()方法
    该方法的实现在抽象父类 AbstractAnnotationBeanPostProcessor中
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;
    }
}
  • 2、构建InjectionMetadata
    实际构建的是Dubbo提供的InjectionMetadata子类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;

}

2.3.2 依赖注入

依赖查找完成后,已经将 @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 依赖查找注入过程几乎完全一致。


三、Consumer 侧服务引入

3.1 代理对象生成

前一节,已经将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的整合流程。

全文完~

你可能感兴趣的:(spring)