SpringBoot环境下的Apache Dubbo 源码学习第二篇

BeanDefinitionRegistryPostProcessor

MapperScannerConfigurer

这个BeanDefinitionRegistryPostProcessor的意义在于将Mybatis定义的一些对象集成到SpringIOC容器中。在执行postProcessBeanDefinitionRegistry,这个方法做的事情就是将接口转换成BeanDefinition【实际是MapperFactoryBean】。
Spring在跟BeanDefinition创建对象时,是根据BeanDefinition的beanClass。 因此,会发现,虽然spring-mybatis提供的ClassPathMapperScanner调用父类的doScan将接口转换成对应BeanDefinition之外,还会对这些BeanDefinition做一些processBeanDefinitions处理,即将为这些BeanDefinition指定beanClass=MapperFactoryBean.class。
当Spring在创建含有Mapper接口注入的对象时,即populateBean时,会创建Mapper接口对象,这是创建的时MapperFactoryBean,然后再通过mapperFactoryBean.getObject()通过对应的MapperProxyFactory会真正创建mapper接口的代理对象。
从这里也可以看出BeanFactoryFactoryBean的不同区别,通俗点来说BeanFactory可以被认为是创建bean对象的工厂,而FactoryBean是一种可以被Bean Factory创建的特殊Bean,特殊在于这个bean也可以通过getObject创建对象,这个创建对象可以不通过Spring的机制,比如MapperFactoryBean的getObject。因此,这个FactoryBean的意义在于集成第三方组件内部的一些定义的对象。类似的还有ShiroFilterFactoryBean。
说一下mybatis在Spring环境下的工作机制的原因在于,Dubbo也是有自己的一些对象需要通过SpringIOC容器来创建,比如ServiceBean。而mybatis-spring提供了使用范例。我也是在读到Dubbo的源码时,有种直觉,就是Dubbo团队参考了mybatis-spring。因为Dubbo跟mybatis-spring之间的处理有很多相像的地方。我觉得,即便你深入了解过Spring源码,当你提供第三方的服务组件,需要集成到Spring环境中,如果有一种使用范例,那么在开发起来,会十分的便利。因为我们在学习一件事时,经常问的就是是什么,为什么,跟怎样做。而深入了解Spring源码也只是停留在是什么跟为什么的阶段,怎样做?mybatis-spring。

DubboConfigAliasPostProcessor

ServiceAnnotationBeanPostProcessor

由于在上一篇中,该BeanDefinitionRegistryPostProcessor已经在ConfigurationClassPostProcessor这一阶段被加载到Spring IOC容器中了。因此,会进入invokeBeanFactoryPostProcessors的第二阶段,从IOC容器中再次寻找BeanDefinitionRegistryPostProcessor,且是实现了Ordered的继续创建并执行。
因为,IOC容器中可能存在通过配置类而新加入的该BeanDefinitionRegistry类型的BeanDefinition。
跳过进入第三阶段,没有实现Ordered的BeanDefinitionRegistryPostProcessor的创建跟执行,即ServiceAnnotationBeanPostProcessor。

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

        // @since 2.7.5
        // 这个是统一监听器,而不再是每一个ServiceBean都是ApplicationListener
        // 相当于一个窗口。我司的RSF就是这样,有一个统一的窗口
        registerBeans(registry, DubboBootstrapApplicationListener.class);
		// 解析Dubbo服务的包名
        Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);

        if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
            // 将该包名下的Dubbo服务都解析成BeanDefinition并注册到IOC容器中,
            // 以便被IOC容器创建对象
            // 用DubboClassPathBeanDefinitionScanner将Dubbo服务解析成BeanDefinition
            // 思路跟mybatis-spring一样。就不在赘述了。
            registerServiceBeans(resolvedPackagesToScan, registry);
        } else {
            if (logger.isWarnEnabled()) {
                logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
            }
        }

    }

总结

提供自定义的BeanDefinitionRegistry服务,需要实现:

  1. 实现BeanDefinitionRegistryPostProcessor接口的PostProcessor
  2. 继承ClassPathBeanDefinitionScanner并重写doScan,这个重写需要注意的一点就是,不用取代原有的doScan逻辑,继续利用原来的,我们自己有特殊需求的,可以对生成的BeanDefinition做处理,比如mybatis-spring,dubbo。

BeanPostProcessor

当IOC容器初始化,执行完invokeBeanFactoryPostProcessors这一有关BeanDefinition生成的阶段后,紧接着,需要创建能对bean对象的初始化(生命周期)有影响的BeanPostProcessor。Dubbo的@Reference注解所对应的的ReferenceAnnotationBeanPostProcessor就是在这一阶段创建,而它的BeanDefinition则是在上一阶段就已经注册到IOC容器中了,这部分十分简单,就不详述。

MessageSource(没有涉及)

ApplicationEventMulticaster

创建默认的广播器

registerListeners

将ApplicationContext中所有的监听器都注册到上一阶段创建的广播器中,包括Dubbo实现的ApplicationListener。

finishBeanFactoryInitialization

根据IOC容器中已经存在的BeanDefinitionName完成Dubbo相关的对象创建。比如DubboBootstrapApplicationListener,以及Dubbo的@Service服务的ServiceBean等。

finishRefresh

当IOC容器初始化完成后,产生一事件ContextRefreshedEvent,该事件经ApplicationEventMulticaster广播器广播到所有的ApplicationListener,包括DubboBootstrapApplicationListener。
将Dubbo提供的服务发布到注册中心去。这里主要详述DubboBootstrapApplicationListener的工作机制。
在这里插入图片描述

/**
     * Start the bootstrap
     */
    public DubboBootstrap start() {
        // 根据状态started只启动一次
        if (started.compareAndSet(false, true)) {
            // 暴露Dubbo服务之前的初始化,主要是设计复杂的类加载机制
            // 启动Dubbo的配置管理ConfigManager,
            // 主要是涉及Application Protocol Monitor Registry 
            // Module Provider Consumer收集管理
            initialize();
            if (logger.isInfoEnabled()) {
                logger.info(NAME + " is starting...");
            }
            // 1. export Dubbo Services
            // 从配置管理中获取所有的Dubbo服务进行暴露服务
            exportServices();

            // Not only provider register
            if (!isOnlyRegisterProvider() || hasExportedServices()) {
                // 2. export MetadataService
                exportMetadataService();
                //3. Register the local ServiceInstance if required
                registerServiceInstance();
            }

            referServices();

            if (logger.isInfoEnabled()) {
                logger.info(NAME + " has started.");
            }
        }
        return this;
    }

在追踪exportServices方法时,会发现,Dubbo服务其实就是Dubbo的URL服务。通俗点说,就是将一个类服务转换成URL服务。这个方法前期做了URL地址的拼装,并将URL转换成Invoker对象,供服务消费方使用。

    // 创建Invoker对象
	Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass,
			registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));
    DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
    // 根据协议暴露invoker
    Exporter<?> exporter = protocol.export(wrapperInvoker);
    exporters.add(exporter);

debug追踪到DubboProtocol的export的openServer方法,默认使用Netty进行数据传输。

当涉及Dubbo的@Reference引用的对象时,会在消费对象创建的populateBean阶段经过ReferenceAnnotationBeanPostProcessor处理,生成该@Reference注入的接口的代理对象。

    @Override
    public PropertyValues postProcessPropertyValues(
            PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {

        InjectionMetadata metadata = findInjectionMetadata(beanName, bean.getClass(), pvs);
        try {
            // 利用Spring的InjectionMetadata将属性pvs注入到bean中去
            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;
    }

继续追踪inject方法,是其ReferenceAnnotationBeanPostProcessor父类的内部类中的。

	@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);

    }

继续追踪,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);

        /**
         * The name of bean that is declared by {@link Reference @Reference} annotation injection
         */
        String referenceBeanName = getReferenceBeanName(attributes, injectedType);
		// 创建Dubbo服务提供的接口在Spring IOC容器中的对应对象
		// 可以将ReferenceBean理解为Dubbo的服务接口的基础数据结构
        ReferenceBean referenceBean = buildReferenceBeanIfAbsent(referenceBeanName, attributes, injectedType);
		// 向容器中注册ReferenceBean
        registerReferenceBean(referencedBeanName, referenceBean, attributes, injectedType);

        cacheInjectedReferenceBean(referenceBean, injectedElement);
		// 获取注入的代理对象,这个代理对象的创建过程涉及Netty客户端的创建,
		// 以及与Netty服务端的连接建立。两种方式,连接客户端,一种是直连【不要单步debug,因为很容易debug迷失。去除所有的断点,直接在客户端初始化那打断点,然后看线程名】;另一种是
		// 通过定时器DubboRegistryRetryTimer链接【单步debug时候,就会出现这种场景】。
        return getOrCreateProxy(referencedBeanName, referenceBeanName, referenceBean, injectedType);
    }

单步debug,以致触发定时任务重试链接服务端,此时的main线程执行消费方的时候,获取的服务提供方列表为空。因此,main线程在执行消费方对目录的providers直接return。而定时任务,可以走下一步toInvokers()。进而是客户端的创建。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值