Dubbo笔记 ㉕ : Spring 执行流程概述

一、前言

本系列为个人Dubbo学习笔记,内容基于《深度剖析Apache Dubbo 核心技术内幕》, 过程参考官方源码分析文章,仅用于个人笔记记录。本文分析基于Dubbo2.7.5版本,由于个人理解的局限性,若文中不免出现错误,感谢指正。

系列文章地址:Dubbo源码分析:全集整理


本文基于 Dubbo 2.7.5 版本。关于该部分逻辑,如有需要可参考:

  1. Dubbo笔记 ㉕ : Spring 执行流程概述
  2. Dubbo笔记 ㉖ : DubboBootstrap 的服务暴露
  3. Dubbo笔记 ㉗ : 服务自省-提供者
  4. Dubbo笔记 ㉘ : 服务自省-消费者

二、Dubbo 的自动装配

在Springboot 集成 Dubbo 时需要引入如下依赖:

        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>2.7.5</version>
        </dependency>

而 dubbo-spring-boot-starter 会引入如下两个依赖

    <dependency>
      <groupId>org.apache.dubbo</groupId>
      <artifactId>dubbo-spring-boot-autoconfigure</artifactId>
      <version>2.7.5</version>
    </dependency>
    
    <dependency>
      <groupId>org.apache.dubbo</groupId>
      <artifactId>dubbo-spring-boot-autoconfigure-compatible</artifactId>
      <version>2.7.5</version>
    </dependency>

上面的两个依赖会通过Springboot的自动装配引入Dubbo的自动装配类:DubboAutoConfiguration、DubboRelaxedBindingAutoConfiguration、DubboRelaxedBinding2AutoConfiguration。
如下图:

在这里插入图片描述
关于Springboot的 自动装配,如有需要详参: Spring源码分析十一:Springboot 自动装配


上面我们看到, Dubbo通过 Springboot的 自动装配引入了下面几个类:

  • DubboAutoConfiguration : 用于引入 Dubbo的 配置类。包括 ServiceAnnotationBeanPostProcessor、ReferenceAnnotationBeanPostProcessor、SingleDubboConfigConfiguration、MultipleDubboConfigConfiguration、primaryPropertyResolver 等
  • DubboRelaxedBindingAutoConfiguration :注入了宽松配置解析器,用于Springboot 1.x 版本, 解析 Dubbo 的配置
  • DubboRelaxedBinding2AutoConfiguration : 功能同 DubboRelaxedBindingAutoConfiguration,用于 Springboot 2.x 版本,解析 Dubbo的配置

三、提供者的启动流程

下面我们以服务提供者的视角来看在 Spring 中Dubbo服务暴露的流程:

  1. Springboot 启动时会激活 ServiceAnnotationBeanPostProcessor #postProcessBeanDefinitionRegistry 方法。此方法会向容器中注册DubboBootstrapApplicationListener(如果没有注册) 以及被 @Service (Dubbo 提供的 service 注解) 修饰的 Bean的 ServiceBean 的 BeanDefinition。
  2. 在容器刷新结束后,会通过 AbstractApplicationContext#finishRefresh 发送容器刷新结束事件(ContextRefreshedEvent),而 DubboBootstrapApplicationListener 会监听此事件,并调用 DubboBootstrap#start 方法来发布 Dubbo 服务。同时 DubboBootstrapApplicationListener 也监听 ContextClosedEvent 事件,用于停止服务暴露。
  3. DubboBootstrap#start 进行服务暴露。

上面的概述涉及到了 ServiceAnnotationBeanPostProcessor、DubboBootstrapApplicationListener 、DubboBootstrap 几个类,下面我们来看看 ServiceAnnotationBeanPostProcessor 和 DubboBootstrapApplicationListener 的实现,DubboBootstrap 由于篇幅限制,详细逻辑在 Dubbo笔记 ㉖ : DubboBootstrap 的服务暴露 一文中进行分析。

1. ServiceAnnotationBeanPostProcessor

ServiceAnnotationBeanPostProcessor 类结构如下:
在这里插入图片描述

ServiceAnnotationBeanPostProcessor 的作用有两个:

  1. 如果容器中没有 DubboBootstrapApplicationListener 的BeanDefinition,则向 Spring 容器中 注册 DubboBootstrapApplicationListener 的 BeanDefinition。
  2. 向 Spring 容器中 注册被 @Service 注解修饰的类对应的 ServiceBean 的 BeanDefinition。这里的@Service 指的是 Dubbo提供的 @Service注解。在 Spring 中 Dubbo会为每个被 @Service 注解修饰的类创建一个对应的 ServiceBean 来保存当前类暴露的一些信息。而 ServiceAnnotationBeanPostProcessor 会为这些 ServiceBean 生成对应的 BeanDefinition 保存到容器中。

: Spring在容器中创建Bean时依赖于 BeanDefinition。简单来说,Spring在创建容器中的 Bean时首先会先创建BeanDefinition,随后根据BeanDefinition 再创建Bean,随后将Bean注入到容器中。所以这里创建的都是 BeanDefinition。


下面我们来详细分析:由于 ServiceAnnotationBeanPostProcessor 实现了 BeanDefinitionRegistryPostProcessor 接口,所以我们这里关注 postProcessBeanDefinitionRegistry 方法。

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {

        // @since 2.7.5
        // 1. 注册 DubboBootstrapApplicationListener BeanDefinition
        registerBeans(registry, DubboBootstrapApplicationListener.class);
		// 2. 获取指定的 Dubbo扫描路径
        Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);

        if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
        	// 3. 注册 ServiceBean 
            registerServiceBeans(resolvedPackagesToScan, registry);
        } else {
			// 打印日志
        }

    }

我们按照上面的注释:

  1. registerBeans 将 DubboBootstrapApplicationListener 的 BeanDefinition 注册到容器中
  2. resolvePackagesToScan 获取到 Dubbo Bean的 扫描路径
  3. registerServiceBeans 扫描指定路径下的 Dubbo Bean ,生成对应的 BeanDefinition 注册到容器中。

下面我们详细来看每一步的具体实现。

1.1 registerBeans

registerBeans 方法的具体实现是 AnnotatedBeanDefinitionRegistryUtils#registerBeans,主要作用如果 DubboBootstrapApplicationListener BeanDefinition没有注册,则将 DubboBootstrapApplicationListener 的 BeanDefinition 注册到容器中。其实现如下:

	// 入参 的 annotatedClasses 是 DubboBootstrapApplicationListener.class
    public static void registerBeans(BeanDefinitionRegistry registry, Class<?>... annotatedClasses) {

        if (ObjectUtils.isEmpty(annotatedClasses)) {
            return;
        }

        // Remove all annotated-classes that have been registered
        Iterator<Class<?>> iterator = new ArrayList<Class<?>>(asList(annotatedClasses)).iterator();
		// 如果容器中已经存在 annotatedClass 的 BeanDefinition,则将其移除,不再进行下面的创建
        while (iterator.hasNext()) {
            Class<?> annotatedClass = iterator.next();
            if (isPresentBean(registry, annotatedClass)) {
                iterator.remove();
            }
        }
		
        AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(registry);
        // 创建 annotatedClasses 的 BeanDefinition,并通过 registry 注册到容器中
        reader.register(annotatedClasses);
    }

1.2 resolvePackagesToScan

ServiceAnnotationBeanPostProcessor#packagesToScan 实现如下,主要作用是解析 Dubbo配置中的 dubbo.scan 配置的扫描路径并返回。dubbo.scan配置指定了我们需要扫描的路径,在这个路径下的 类如果被Dubbo注解修饰则会被进一步处理。

	// 返回解析后的  dubbo.scan 指定的路径
    private Set<String> resolvePackagesToScan(Set<String> packagesToScan) {
    	// 获取Dubbo bean 的扫描路径
        Set<String> resolvedPackagesToScan = new LinkedHashSet<String>(packagesToScan.size());
        for (String packageToScan : packagesToScan) {
            if (StringUtils.hasText(packageToScan)) {
            	// 替换 dubbo.scan 配置中的占位符
                String resolvedPackageToScan = environment.resolvePlaceholders(packageToScan.trim());
                resolvedPackagesToScan.add(resolvedPackageToScan);
            }
        }
        // 返回解析后的扫描路径
        return resolvedPackagesToScan;
    }

1.3 registerServiceBeans

在该方法中, Dubbo会扫描 被 org.apache.dubbo.config.annotation.Service 和 com.alibaba.dubbo.config.annotation.Service 注解修饰的类(版本更高则可能是 DubboService),被这两个注解修饰的类会作为服务提供者暴露处理,而这里会生成对应服务的 ServiceBean 的BeanDefinition。


如:对于 ProviderServiceImpl,实现如下:

@Service(version = "1.0.0", group = "spring")
public class ProviderServiceImpl implements ProviderService {
    @Override
    public void sayHello() {
        System.out.println("hello");
    }

    @Override
    public void sayHello(String msg) {
        System.out.println("hello msg = " + msg);
    }

    @Override
    public String sayHelloWorld() {
        System.out.println("hello world");
        return "hello world";
    }
}

在 Spring容器中会存在一个 ProviderServiceImpl 实例,同时会存在一个 ServiceBean 实例保存了ProviderServiceImpl 要暴露的服务信息,其中 ServiceBean 在 容器中的 beanName 生成规则是 ServiceBean:{接口路径}:{版本号}:{分组} ,如下图:
在这里插入图片描述

所以 ServiceAnnotationBeanPostProcessor#registerServiceBeans 这里是为了每个 Bean对应的 ServiceBean 生成对应的 BeanDefinition 并注册到容器中。


下面我们来看 ServiceAnnotationBeanPostProcessor#registerServiceBeans的实现,如下:

    private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
		// 1. 创建  Dubbo 扫描类
        DubboClassPathBeanDefinitionScanner scanner =
                new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);

        BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);

        scanner.setBeanNameGenerator(beanNameGenerator);
		// 扫描被 @Service 注解修饰的类,这里的Service是  org.apache.dubbo.config.annotation.Service
        scanner.addIncludeFilter(new AnnotationTypeFilter(Service.class));

         // 扫描被 @com.alibaba.dubbo.config.annotation.Service 注解修饰的类
        scanner.addIncludeFilter(new AnnotationTypeFilter(com.alibaba.dubbo.config.annotation.Service.class));

        for (String packageToScan : packagesToScan) {

            // Registers @Service Bean first
            // 2. 在这里会首先将 被 @Service 修饰的 BeanDefinition 注册到 registry 中
            scanner.scan(packageToScan);

            // Finds all BeanDefinitionHolders of @Service whether @ComponentScan scans or not.
            // 3. 获取所有 @Service 修饰的 Bean的  BeanDefinitionHolder
            Set<BeanDefinitionHolder> beanDefinitionHolders =
                    findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);

            if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {

                for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
                	// 4. 准备注册 ServiceBean的 BeanDefinition
                    registerServiceBean(beanDefinitionHolder, registry, scanner);
                }
				... 日志打印
            } else {
				... 日志打印
            }
        }

    }

这里我们可以看到 :

  1. 首先构建扫描器类 DubboClassPathBeanDefinitionScanner,扫描 被 org.apache.dubbo.config.annotation.Service 注解和 com.alibaba.dubbo.config.annotation.Service 注解修饰的类。
  2. 将被 org.apache.dubbo.config.annotation.Service 注解和 com.alibaba.dubbo.config.annotation.Service 注解修饰的类 的 BeanDefinition 注册到容器中 :对 dubbo.scan 的配置路径进行扫描,需要注意的是scanner.scan(packageToScan); 方法会在扫描到满足条件的类时会直接构建其 BeanDefinition 并注册到 registry 中。也就是说此时被 @Service 修饰的 类的 BeanDefinition 在这一步被注册到容器中。
  3. 获取所有被 org.apache.dubbo.config.annotation.Service 注解和 com.alibaba.dubbo.config.annotation.Service 注解修饰的类 的 BeanDefinitionHolder
  4. . 随后遍历 BeanDefinitionHolder,通过 registerServiceBean(beanDefinitionHolder, registry, scanner); 方法生成 BeanDefinition 并注册到容器中,这里的 BeanDefinition是 ServiceBean的 BeanDefinition。

上面我们看到,ServiceBean的 BeanDefinition 构建交由 registerServiceBean(beanDefinitionHolder, registry, scanner); 来完成,下面我们来看看 registerServiceBean(beanDefinitionHolder, registry, scanner);的具体实现 :

1.3.1 registerServiceBean

在 ServiceAnnotationBeanPostProcessor#registerServiceBean 中生成了 ServiceBean 的 BeanDefinition 并注册到容器中。我们这里直接来看 registerServiceBean 的实现,如下:

    private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry,
                                     DubboClassPathBeanDefinitionScanner scanner) {
		// 获取 提供者类的 Class
        Class<?> beanClass = resolveClass(beanDefinitionHolder);
		// 获取 @Service 注解
        Annotation service = findServiceAnnotation(beanClass);
		// 获取 @Service 注解的属性
        AnnotationAttributes serviceAnnotationAttributes = getAnnotationAttributes(service, false, false);
		// 获取暴露的服务接口
        Class<?> interfaceClass = resolveServiceInterfaceClass(serviceAnnotationAttributes, beanClass);	
		// 获取 beanName
        String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();
		// 封装成 ServiceBean BeanDefinition
        AbstractBeanDefinition serviceBeanDefinition =
                buildServiceBeanDefinition(service, serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName);

        // ServiceBean Bean name
        // 获取 ServiceBean 的 beanName ,规则是 ServiceBean:{接口路径}:{版本号}:{分组}
        String beanName = generateServiceBeanName(serviceAnnotationAttributes, interfaceClass);
		// 如果 ServiceBean BeanDefinition 没有注册,则进行注册
        if (scanner.checkCandidate(beanName, serviceBeanDefinition)) { // check duplicated candidate bean
            registry.registerBeanDefinition(beanName, serviceBeanDefinition);
        } else {
        	// ... 打印日志
        }

    }
	// 生成 serviceBeanName, 规则是 ServiceBean:{接口路径}:{版本号}:{分组}
    private String generateServiceBeanName(AnnotationAttributes serviceAnnotationAttributes, Class<?> interfaceClass) {
        ServiceBeanNameBuilder builder = create(interfaceClass, environment)
                .group(serviceAnnotationAttributes.getString("group"))
                .version(serviceAnnotationAttributes.getString("version"));
        return builder.build();
    }


我们这里需要关注如下代码,该方法构建了 ServiceBean 的 BeanDefinition:

 AbstractBeanDefinition serviceBeanDefinition =
                buildServiceBeanDefinition(service, serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName);

其实现如下:

	// 该方法返回的是 ServiceBean 的 BeanDefinition
    private AbstractBeanDefinition buildServiceBeanDefinition(Annotation serviceAnnotation,
                                                              AnnotationAttributes serviceAnnotationAttributes,
                                                              Class<?> interfaceClass,
                                                              String annotatedServiceBeanName) {
		// 1. 创建 BeanDefinition 创建器类
        BeanDefinitionBuilder builder = rootBeanDefinition(ServiceBean.class);
		// 2. 开始为 BeanDefinition 添加 PropertyValue 属性
        AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();

        MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();

        String[] ignoreAttributeNames = of("provider", "monitor", "application", "module", "registry", "protocol",
                "interface", "interfaceName", "parameters");
		// 2.1 添加Service 注解属性,忽略 ignoreAttributeNames  中的属性
        propertyValues.addPropertyValues(new AnnotationPropertyValuesAdapter(serviceAnnotation, environment, ignoreAttributeNames));

        // References "ref" property to annotated-@Service Bean
        // 2.2 添加 ref 属性。annotatedServiceBeanName是暴露的接口实现类,这里会将其封装为RuntimeBeanReference。
        // 如 ProviderServiceImpl 为 Provider Service的实现类,这里会将其封装为 ref : new RuntimeBeanReference("providerServiceImpl")。
        addPropertyReference(builder, "ref", annotatedServiceBeanName);
        // 2.3 下面也是配置 propertyValue 属性,这里不再继续分析。
		....
		// 2.4 返回构建的 BeanDefinition
        return builder.getBeanDefinition();

    }

其中 ServiceBean#ref 保存调用的服务实例, 如下图:
在这里插入图片描述

注:

  1. 关于 RuntimeBeanReference :当我们需要动态注入Bean,并给该Bean的属性注入其他Bean时,比如在Mybatis和Spring的整合中,我们需要动态注入Mapper到spring容器中,而该Mapper如果需要执行SQL语句,还需要持有SqlSessionFactory的引用。但是我们注入时,可能对应的Bean还没有准备好,这时,我们就可以使用RuntimeBeanReference,以保持对实际Bean的引用。在Spring处理依赖关系时,最终会将该引用替换成实际生成的Bean对象。例如:

    definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
    
  2. 关于 ServiceBean,其 结构如下图:

    在这里插入图片描述
    这里我们需要注意 ServiceBean 是 AbstractConfig 的子类。我们这里需要看一下AbstractConfig#addIntoConfigManager 方法:

        @PostConstruct
        public void addIntoConfigManager() {
        	// 将当前类添加到 ConfigManager 的配置中
            ApplicationModel.getConfigManager().addConfig(this);
        }
    

    AbstractConfig#addIntoConfigManager 方法被 @PostConstruct 注解修饰,也即是说,每个 ServiceBean 在创建时就会通过 AbstractConfig#addIntoConfigManager 将自身添加到 ConfigManager 的配置中(确切的说是保存到了 ConfigManager#configsCache 中缓存了起来)。在之后 DubboBootstrap 进行服务暴露时会使用到,具体我们后文再详解。


这里我们可以看到 ServiceBean 中包含了 Ref (提供服务的对象实例)、version (服务版本号)、group(服务分组) 等信息,这一步完成 ServiceBean的 BeanDefinition 后,Spring在后续的流程会创建对应的实例对象,Dubbo的服务暴露则是通过 这些ServiceBean 实例来完成。

1.4 总结

我们这里总结一下 ServiceAnnotationBeanPostProcessor 的作用:

  1. 在 Spring容器启动时,会触发 BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry 方法,所以在 Spring 容器启动时会触发 ServiceAnnotationBeanPostProcessor#postProcessBeanDefinitionRegistry 方法。关于 BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry 方法,如有需要详参 Spring源码分析二:BeanFactoryPostProcessor 的处理

  2. ServiceAnnotationBeanPostProcessor#postProcessBeanDefinitionRegistry 中首先会判断 DubboBootstrapApplicationListenerBeanDefinition 是否注册,如果没有,则注册一个 DubboBootstrapApplicationListenerBeanDefinition。在 Spring中,一个 Bean如果被创建,首先会创建其 BeanDefinition ,之后再根据BeanDefinition 创建真正的 Bean实例。这里的过程是创建BeanDefinition。

  3. 随后 ServiceAnnotationBeanPostProcessor#postProcessBeanDefinitionRegistry 会解析出配置中的 dubbo.scan 配置路径,扫描指定路径下的所有bean,如果 bean被 org.apache.dubbo.config.annotation.Service 和 com.alibaba.dubbo.config.annotation.Service 注解修饰,则会为其创建对应的 ServiceBean 的 BeanDefinition,并注册到容器中。这里需要注意,这里创建的BeanDefinition 代表的类型是 ServiceBean。假设此时 DemoService 被 @Service 注解修饰,这里会创建一个 ServiceBean 的 BeanDefinition,而这个 ServiceBean 中保存了 DemoService 服务暴露的相关信息,如 超时时间、版本号、具体服务实现类等。

2. DubboBootstrapApplicationListener

上面我们提到了 ServiceAnnotationBeanPostProcessor 中会注册DubboBootstrapApplicationListener 的 BeanDefinition 到容器中。

DubboBootstrapApplicationListener 的作用是监听刷新事件,用来监听Spring容器刷新结束或关闭事件,当Spring容器刷新结束后开始准备暴露dubbo服务,当容器关闭时则准备销毁 dubbo 服务。其实现如下:

public class DubboBootstrapApplicationListener extends OneTimeExecutionApplicationContextEventListener
        implements Ordered {

    private final DubboBootstrap dubboBootstrap;

    public DubboBootstrapApplicationListener() {
        this.dubboBootstrap = DubboBootstrap.getInstance();
    }

    @Override
    public void onApplicationContextEvent(ApplicationContextEvent event) {
    	// Springboot 容器刷新结束会发送此事件
        if (event instanceof ContextRefreshedEvent) {
        	// 执行容器刷新
            onContextRefreshedEvent((ContextRefreshedEvent) event);
        } else if (event instanceof ContextClosedEvent) {
        	// 容器关闭触发
            onContextClosedEvent((ContextClosedEvent) event);
        }
    }
	// 启动 Dubbo服务
    private void onContextRefreshedEvent(ContextRefreshedEvent event) {
        dubboBootstrap.start();
    }
	// 销毁 Dubbo服务
    private void onContextClosedEvent(ContextClosedEvent event) {
        dubboBootstrap.stop();
    }

    @Override
    public int getOrder() {
        return LOWEST_PRECEDENCE;
    }
}

这里我们可以看到 DubboBootstrapApplicationListener 会监听 Spring 事件。

当 Spring容器刷新结束后会发送 ContextRefreshedEvent事件,而DubboBootstrapApplicationListener 接收到该事件后会通过 DubboBootstrap#start 来暴露服务。同理Spring容器销毁时发送 ContextClosedEvent事件,DubboBootstrapApplicationListener 则会通过 DubboBootstrap#stop 来停止 Dubbo 服务的暴露。

3. 总结

我们简单总结 :ServiceAnnotationBeanPostProcessor 在 容器进行 Bean注册时 会为被@Service 修饰的类创建 BeanDefinition 和对应的 ServiceBean 的 BeanDefinition 注册到容器中。Spring会根据每个 BeanDefinition 创建对应的类实例,而 ServiceBean 在创建时会通过 AbstractConfig#addIntoConfigManager 将自身添加到 ConfigManager 中。Spring 容器刷新结束后会发送 ContextRefreshedEvent 事件从而触发 DubboBootstrapApplicationListener#onApplicationContextEvent 事件,在此会调用 DubboBootstrap#start 方法,DubboBootstrap#start 会完成整个服务暴露的过程。

四、消费者的启动流程

上面我们以服务提供者的视角看了一下 Springboot启动后,Dubbo服务是如何启动的。下面我们来看一看服务消费者是如何引用的Dubbo服务。(我们这里以 @Reference 注解的方式引用 Dubbo服务为例)

对于提供者, Dubbo提供了ServiceAnnotationBeanPostProcessor 类处理 @Service,对于消费者则是提供了 ReferenceAnnotationBeanPostProcessor 来处理 @Reference 注解。


ReferenceAnnotationBeanPostProcessor 的结构如下:
请添加图片描述

这里需要注意的是 ServiceAnnotationBeanPostProcessor 实现了 BeanDefinitionRegistryPostProcessor 接口,用于注册 BeanDefinition,而 ReferenceAnnotationBeanPostProcessor 实现了 AbstractAnnotationBeanPostProcessor,参与了 Bean的创建过程。


1. ReferenceAnnotationBeanPostProcessor

ReferenceAnnotationBeanPostProcessor 是 Dubbo 提供用来处理 @Reference 注解的后处理器。
ReferenceAnnotationBeanPostProcessor 利用 Bean 后处理器的特性,在每个Bean创建时会判断其是否存在被 @Reference 注解 修饰的属性和方法,如果存在则进行注入。(注入的实例对象是其创建的Dubbo提供者的代理对象。)

关于 Bean 后处理器,如有需要详参:Spring源码分析衍生篇四:后处理器 BeanPostProcessor


下面我们具体来看 ReferenceAnnotationBeanPostProcessor 的逻辑。

1.1 构造函数

ReferenceAnnotationBeanPostProcessor 的构造函数如下:

    public ReferenceAnnotationBeanPostProcessor() {
    	// org.apache.dubbo.config.annotation.Reference 和 com.alibaba.dubbo.config.annotation.Reference 注解
        super(Reference.class, com.alibaba.dubbo.config.annotation.Reference.class);
    }

super调用其父类 AbstractAnnotationBeanPostProcessor 的构造函数,如下:

    public AbstractAnnotationBeanPostProcessor(Class<? extends Annotation>... annotationTypes) {
        Assert.notEmpty(annotationTypes, "The argument of annotations' types must not empty");
        this.annotationTypes = annotationTypes;
    }

可以看到,这里会将 ReferenceAnnotationBeanPostProcessor#annotationTypes 被赋值为 org.apache.dubbo.config.annotation.Referencecom.alibaba.dubbo.config.annotation.Reference
当 Bean创建时,会遍历其所有的属性和方法,判断是否被 ReferenceAnnotationBeanPostProcessor#annotationTypes 修饰,即是否被 org.apache.dubbo.config.annotation.Referencecom.alibaba.dubbo.config.annotation.Reference 修饰。


1.2 ReferenceAnnotationBeanPostProcessor#postProcessPropertyValues

在 Spring 容器中,Bean在创建成功后,会通过 AbstractAutowireCapableBeanFactory#populateBean 方法调用 InstantiationAwareBeanPostProcessor#postProcessProperties 和 InstantiationAwareBeanPostProcessor#postProcessPropertyValues 来填充 Bean的属性。

关于Spring Bean 创建的逻辑,如有需要详参 Spring源码分析七:bean的属性注入⑤ - populateBean


而对于 ReferenceAnnotationBeanPostProcessor, InstantiationAwareBeanPostProcessor#postProcessProperties 方法并没有实现,而InstantiationAwareBeanPostProcessor#postProcessPropertyValues 的实现在其父类 AbstractAnnotationBeanPostProcessor中,如下:

	// AbstractAnnotationBeanPostProcessor#postProcessPropertyValues
    @Override
    public PropertyValues postProcessPropertyValues(
            PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
		// 1. 获取需要依赖注入的元数据信息
        InjectionMetadata metadata = findInjectionMetadata(beanName, bean.getClass(), pvs);
        try {
        	// 2. 进行属性注入
            metadata.inject(bean, beanName, pvs);
        } catch (...) {
        	... 抛出异常
        }
        return pvs;
    }

这里的逻辑比较简单:

  1. 挑选出被 ReferenceAnnotationBeanPostProcessor#annotationTypes 修饰的属性或方法,并封装成 InjectionMetadata。
  2. 调用 InjectionMetadata#inject 来进行属性注入。

下面我们来具体看

1.2.1 AbstractAnnotationBeanPostProcessor#findInjectionMetadata

AbstractAnnotationBeanPostProcessor#findInjectionMetadata 方法挑选出被 ReferenceAnnotationBeanPostProcessor#annotationTypes 修饰的属性或方法,并封装成 InjectionMetadata。其实现如下:

    private InjectionMetadata findInjectionMetadata(String beanName, Class<?> clazz, PropertyValues pvs) {
        // Fall back to class name as cache key, for backwards compatibility with custom callers.
        String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
        // Quick check on the concurrent map first, with minimal locking.
        // 尝试从缓存中获取
        AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
        // 如果需要刷新  : metadata == null || metadata.needsRefresh(clazz)
        if (InjectionMetadata.needsRefresh(metadata, clazz)) {
            synchronized (this.injectionMetadataCache) {
                metadata = this.injectionMetadataCache.get(cacheKey);
                // 进行刷新操作,清除后重新创建
                if (InjectionMetadata.needsRefresh(metadata, clazz)) {
                    if (metadata != null) {
                        metadata.clear(pvs);
                    }
                    try {
                    	// 创建 元数据
                        metadata = buildAnnotatedMetadata(clazz);
                        this.injectionMetadataCache.put(cacheKey, metadata);
                    } catch (NoClassDefFoundError err) {
						... 抛出异常
                    }
                }
            }
        }
        return metadata;
    }

通过上面的代码我们可以发现其关键逻辑在于 metadata = buildAnnotatedMetadata(clazz); ,实现如下:

	// 创建元数据
    private AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata buildAnnotatedMetadata(final Class<?> beanClass) {
    	// 寻找被指定注解修饰的属性
        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>();
		// 遍历当前 Bean的所有方法
        ReflectionUtils.doWithFields(beanClass, new ReflectionUtils.FieldCallback() {
            @Override
            public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
				// 获取注解类型
                for (Class<? extends Annotation> annotationType : getAnnotationTypes()) {
					// 获取注解属性
                    AnnotationAttributes attributes = getAnnotationAttributes(field, annotationType, getEnvironment(), true, true);
					// 如果属性不为空则说明当前属性被指定注解修饰
                    if (attributes != null) {
                    	// 属性为静态则直接返回
                        if (Modifier.isStatic(field.getModifiers())) {
                            return;
                        }
						// 添加属性集合中
                        elements.add(new AnnotatedFieldElement(field, attributes));
                    }
                }
            }
        });

        return elements;

    }

	// 对 方法 的筛选和对属性的基本类型,
    private List<AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement> findAnnotatedMethodMetadata(final Class<?> beanClass) {

        final List<AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement> elements = new LinkedList<AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement>();
		// 遍历当前 Bean的所有方法
        ReflectionUtils.doWithMethods(beanClass, new ReflectionUtils.MethodCallback() {
            @Override
            public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {

                Method bridgedMethod = findBridgedMethod(method);

                if (!isVisibilityBridgeMethodPair(method, bridgedMethod)) {
                    return;
                }

				
                for (Class<? extends Annotation> annotationType : getAnnotationTypes()) {
					// 筛选被 指定注解修饰的方法
                    AnnotationAttributes attributes = getAnnotationAttributes(bridgedMethod, annotationType, getEnvironment(), true, true);

                    if (attributes != null && method.equals(ClassUtils.getMostSpecificMethod(method, beanClass))) {
                    	// 筛选非静态
                        if (Modifier.isStatic(method.getModifiers())) {
                            return;
                        }
                        // 方法至少要有一个入参,这里打日志警告
                        if (method.getParameterTypes().length == 0) {
                        	... 打印日志
                        }
                        // 获取方法表述信息
                        PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, beanClass);
                        // 保存到集合中
                        elements.add(new AnnotatedMethodElement(method, pd, attributes));
                    }
                }
            }
        });

        return elements;
    }

    protected final Class<? extends Annotation>[] getAnnotationTypes() {
        return annotationTypes;
    }

上面我们可以看到,如果当前 Bean 有属性或方法被 ReferenceAnnotationBeanPostProcessor#annotationTypes 修饰,则认为当前属性或方法需要被注入。
而 ReferenceAnnotationBeanPostProcessor#annotationTypes 的值则是在构造函数中赋值的 org.apache.dubbo.config.annotation.Referencecom.alibaba.dubbo.config.annotation.Reference


因此,我们可以简单总结 AbstractAnnotationBeanPostProcessor#findInjectionMetadata的作用是将 Bean 中被 org.apache.dubbo.config.annotation.Referencecom.alibaba.dubbo.config.annotation.Reference 修饰的属性或方法筛选出来并封装成 InjectionMetadata。

1.2.2 InjectionMetadata#inject

InjectionMetadata#inject 的作用是执行注入操作。其实现如下:

	public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
		Collection<InjectedElement> checkedElements = this.checkedElements;
		Collection<InjectedElement> elementsToIterate =
				(checkedElements != null ? checkedElements : this.injectedElements);
		if (!elementsToIterate.isEmpty()) {
			for (InjectedElement element : elementsToIterate) {
				// 执行元素注入操作,这里会调用 AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement 或 AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement
				element.inject(target, beanName, pvs);
			}
		}
	}

这里需要注意:

  • 被 @Reference 注解修饰的 属性被封装成AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement 类型。其实现如下:

         @Override
            protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
    			// 获取注入类型
                Class<?> injectedType = field.getType();
    			// 获取注入的对象实例
                Object injectedObject = getInjectedObject(attributes, bean, beanName, injectedType, this);
    			// 设置权限
                ReflectionUtils.makeAccessible(field);
    			// 反射赋值
                field.set(bean, injectedObject);
    
            }
    
  • 被 @Reference 注解修饰的 方法被封装成AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement 类型。其实现如下

            @Override
            protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
    			// 获取注入类型
                Class<?> injectedType = pd.getPropertyType();
    			// 获取注入的对象实例
                Object injectedObject = getInjectedObject(attributes, bean, beanName, injectedType, this);
    			// 设置权限
                ReflectionUtils.makeAccessible(method);
    			// 反射调用
                method.invoke(bean, injectedObject);
            }
    

上面我们看到,注入的实例是通过 AbstractAnnotationBeanPostProcessor#getInjectedObject来获取,下面我们来看看 其具体实现。

1.2.3 AbstractAnnotationBeanPostProcessor#getInjectedObject

AbstractAnnotationBeanPostProcessor#getInjectedObject 返回了一个服务提供者的代理对象,其实现如下:

    protected Object getInjectedObject(AnnotationAttributes attributes, Object bean, String beanName, Class<?> injectedType,
                                       InjectionMetadata.InjectedElement injectedElement) throws Exception {
		// 获取缓存 key
        String cacheKey = buildInjectedObjectCacheKey(attributes, bean, beanName, injectedType, injectedElement);
		// 尝试从缓存中获取
        Object injectedObject = injectedObjectsCache.get(cacheKey);
		// 缓存没有命中
        if (injectedObject == null) {
        	// 获取要注入的 Bean
            injectedObject = doGetInjectedBean(attributes, bean, beanName, injectedType, injectedElement);
            // Customized inject-object if necessary
            // 放入缓存中
            injectedObjectsCache.putIfAbsent(cacheKey, injectedObject);
        }

        return injectedObject;

    }

AbstractAnnotationBeanPostProcessor#doGetInjectedBean是一个抽象方法,在 ReferenceAnnotationBeanPostProcessor 中实现了该方法,如下:

    @Override
    protected Object doGetInjectedBean(AnnotationAttributes attributes, Object bean, String beanName, Class<?> injectedType,
                                       InjectionMetadata.InjectedElement injectedElement) throws Exception {
          
        // 以 @Service 注解规则生成的 ServiceBean BeanName
        // ServiceBean:com.kingfish.service.ProviderService:1.0.0:spring
        String referencedBeanName = buildReferencedBeanName(attributes, injectedType);
        // 以 @Reference 注解规则生成的 BeanName :如果指定id,则返回id,否则按照规则:@Reference(属性key=value,...) 接口名  生成
        // 如:@Reference(group=spring,version=1.0.0) com.kingfish.service.ProviderService
        String referenceBeanName = getReferenceBeanName(attributes, injectedType);
		// 为当前引用创建一个 ReferenceBean  对象(如果需要)
        ReferenceBean referenceBean = buildReferenceBeanIfAbsent(referenceBeanName, attributes, injectedType);
		// ReferenceBean 注册到容器中
        registerReferenceBean(referencedBeanName, referenceBean, attributes, injectedType);
		// 缓存 ReferenceBean  
        cacheInjectedReferenceBean(referenceBean, injectedElement);
		// 创建Dubbo 服务引用服务的代理对象。
        return getOrCreateProxy(referencedBeanName, referenceBeanName, referenceBean, injectedType);
    }

这里可以看到通过 getOrCreateProxy(referencedBeanName, referenceBeanName, referenceBean, injectedType); 方法创建了一个 Dubbo提供者的代理对象并返回。

关于 Dubbo 消费者的创建过程,之前文章有过对 2.7.0版本的介绍,如有需要详参:Dubbo源码分析:全集整理 ,关于2.7.5版本的差异,后续会有文章来对比。

2. 总结

我们简单总结:消费者服务在启动后, 每个 Bean 创建时都会调用 InstantiationAwareBeanPostProcessor#postProcessProperties 和 InstantiationAwareBeanPostProcessor#postProcessPropertyValues 来填充属性。而 ReferenceAnnotationBeanPostProcessor 这里则是通过反射寻找被 @Reference 注解修饰的属性或方法,随后创建对应的Dubbo 代理类赋值。


以上:内容部分参考
《深度剖析Apache Dubbo 核心技术内幕》
https://blog.csdn.net/lichuangcsdn/article/details/89931460
https://blog.csdn.net/zhaodongchao1992/article/details/103475912
如有侵扰,联系删除。 内容仅用于自我记录学习使用。如有错误,欢迎指正

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猫吻鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值