dubbo源码-整合spring之配置解析

dubbo源码-整合spring之配置解析

前言

@EnableDubbo(scanBasePackages = "org.apache.dubbo.demo.provider")
@PropertySource("classpath:/spring/dubbo-provider.properties")

如果想要使用dubbo,只需要添加上面两个注解即可,第一个路径是扫描@DubboService注解,第二个注解则是dubbo配置。
关于配置dubbo又有很多种写法,preoperties,xml,yml,@Bean,或者直接使用dubbo原生api ServiceConfig(提供者),ReferenceConfig(消费者)提供的api。

preoperties写法如下

dubbo.application.name=dubbo-demo-annotation-provider
dubbo.protocol.name=dubbo
dubbo.protocol.port=20880
dubbo.registry.address=redis://localhost:6379

@Bean写法如下

 @Bean
 public RegistryConfig registryConfig() {
       RegistryConfig registryConfig = new RegistryConfig();
       registryConfig.setAddress("zookeeper://127.0.0.1:2181");
       return registryConfig;
}

通过@Bean的写法可以推测出他的配置是有规则的
prefix=dubbo.application最终会生成ApplicationConfig放到spring容器
prefix=dubbo.protocol最终会生成ProtocolConfig放到spring容器
prefix=registry.protocol最终会生成RegistryConfig放到spring容器

也可以推测出针对xml,yml,以及properties这三种写法 ,dubbo一定会扩展spring某个接口,从而拿到dubbo自己所需要的配置,然后解析配置,生成各种XXXConfig放到spring容器中

本篇博客我们就来聊聊dubbo是扩展了spring哪个接口,又是如何生成config,放到spring容器的。
ps:本篇博客对spring源码要求较高,若无基础,还请翻阅笔者之前的spring源码相关博客

源码解析

@EnableDubboConfig
@DubboComponentScan//本篇博客不讨论这个注解,他是扫描@DubboService和@DubboRefrence的
public @interface EnableDubbo {
	//省略,AlisaFor可以把值放到另一个注解里
}
//import注解可以手动导入一个类放到spring容器
@Import(DubboConfigConfigurationRegistrar.class)
public @interface EnableDubboConfig {
	//dubbo默认支持多配置,即多注册中心,多协议。。。
    boolean multiple() default true;
}

DubboConfigConfigurationRegistrar

public class DubboConfigConfigurationRegistrar implements ImportBeanDefinitionRegistrar{

    private ConfigurableApplicationContext applicationContext;
	//ImportBeanDefinitionRegistrar接口提供的扩展点
	//第一个参数是EnableDubboConfig注解封装的元数据,第二个参数是bd注册器
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        AnnotationAttributes attributes = AnnotationAttributes.fromMap(
                importingClassMetadata.getAnnotationAttributes(EnableDubboConfig.class.getName()));
		boolean multiple = attributes.getBoolean("multiple");
		
        //默认单多配置都支持
        registerBeans(registry, DubboConfigConfiguration.Single.class);
        if (multiple) {
            registerBeans(registry, DubboConfigConfiguration.Multiple.class);
        }
		//上面两个registerBeans就是配置的核心代码了,不过我们先来解决下面这个分支线
        registerCommonBeans(registry);
    }
	
}

registerCommonBeans

static void registerCommonBeans(BeanDefinitionRegistry registry) {
        //处理ReferenceBean的,这个很难,涉及到RefenceBean的生成,dubbo的服务引入,这在后续的博客会提到
        registerInfrastructureBean(registry, ReferenceAnnotationBeanPostProcessor.BEAN_NAME,
                ReferenceAnnotationBeanPostProcessor.class);
		
        //注册别名的,
        registerInfrastructureBean(registry, DubboConfigAliasPostProcessor.BEAN_NAME,
                DubboConfigAliasPostProcessor.class);


        //dubbo的LifeCycle接口,实现spring的ApplicationListener,负责dubbo的启动和关闭
        registerInfrastructureBean(registry, DubboApplicationListenerRegistrar.BEAN_NAME,
                DubboApplicationListenerRegistrar.class);

        //给RegistryConfig,ApplicationConfig等配置的属性id和name设置默认值
        registerInfrastructureBean(registry, DubboConfigDefaultPropertyValueBeanPostProcessor.BEAN_NAME,
                DubboConfigDefaultPropertyValueBeanPostProcessor.class);
    }

注册别名

public class DubboConfigAliasPostProcessor implements BeanDefinitionRegistryPostProcessor, BeanPostProcessor {
    public final static String BEAN_NAME = "dubboConfigAliasPostProcessor";

    private BeanDefinitionRegistry registry;

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        this.registry = registry;
    }
	//
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        //前言中提到的xxxConfig 都继承AbstractConfig,也就是说只有dubbo的配置类才走if
        //如果xxxConfig拿得到id,就用id给beanName对应的Bean注册一个别名,这样通过id和beanName都可以拿到Bean
        if (bean instanceof AbstractConfig) {
            String id = ((AbstractConfig) bean).getId();
            if (hasText(id)                                     // id MUST be present in AbstractConfig
                    && !nullSafeEquals(id, beanName)            // id MUST NOT be equal to bean name
                    && !hasAlias(registry, beanName, id)) {     // id MUST NOT be present in AliasRegistry
                //注册别名,这样通过getBean(id||beanName)都可以拿到obj
                registry.registerAlias(beanName, id);
            }
        }
        return bean;
    }
}

dubbo的LifeCycle接口,DubboBootstrap的启动与停止

public class DubboApplicationListenerRegistrar implements ApplicationContextAware {

    public static final String BEAN_NAME = "dubboApplicationListenerRegister";

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        addApplicationListeners((ConfigurableApplicationContext) applicationContext);
    }
	
    private void addApplicationListeners(ConfigurableApplicationContext context) {
        context.addApplicationListener(createDubboBootstrapApplicationListener(context));
        context.addApplicationListener(createDubboLifecycleComponentApplicationListener(context));
    }

    private ApplicationListener<?> createDubboBootstrapApplicationListener(ConfigurableApplicationContext context) {
        //实现spring的ApplicationListener,负责dubbo的启动和关闭
        //如果接受到ContextRefreshedEvent事件,就调用DubboBootStrap.start()
        //如果接受到ContextClosedEvent事件,就调用DubboBootStrap.stop()
        return new DubboBootstrapApplicationListener(context);
    }

    private ApplicationListener<?> createDubboLifecycleComponentApplicationListener(ConfigurableApplicationContext context) {
    	//实现spring的ApplicationListener,负责dubbo的LifeCycle生命周期回调
        //如果接受到ContextRefreshedEvent事件,就调用所有实现LifeCycle的类的start方法
        //如果接受到ContextClosedEvent事件,就调用所有实现LifeCycle的类的destory方法
        return new DubboLifecycleComponentApplicationListener(context);
    }
}

第三个不看了,就是给xxxConfig的id和name设置默认值,前提是id和name不存在,下面开始进入主分支
registerBeans(registry, DubboConfigConfiguration.Single.class);

registerBeans
本方法内部没啥好看的,因为是spring的源码了
主要需要关注 DubboConfigConfiguration.Single这个类

//不同前缀根据这里配置的规则放到不同的配置文件
@EnableConfigurationBeanBindings({
            @EnableConfigurationBeanBinding(prefix = "dubbo.application", type = ApplicationConfig.class),
            @EnableConfigurationBeanBinding(prefix = "dubbo.module", type = ModuleConfig.class),
            @EnableConfigurationBeanBinding(prefix = "dubbo.registry", type = RegistryConfig.class),
            @EnableConfigurationBeanBinding(prefix = "dubbo.protocol", type = ProtocolConfig.class),
            @EnableConfigurationBeanBinding(prefix = "dubbo.monitor", type = MonitorConfig.class),
            @EnableConfigurationBeanBinding(prefix = "dubbo.provider", type = ProviderConfig.class),
            @EnableConfigurationBeanBinding(prefix = "dubbo.consumer", type = ConsumerConfig.class),
            @EnableConfigurationBeanBinding(prefix = "dubbo.config-center", type = ConfigCenterBean.class),
            @EnableConfigurationBeanBinding(prefix = "dubbo.metadata-report", type = MetadataReportConfig.class),
            @EnableConfigurationBeanBinding(prefix = "dubbo.metrics", type = MetricsConfig.class),
            @EnableConfigurationBeanBinding(prefix = "dubbo.ssl", type = SslConfig.class)
    })
    public static class Single {

    }

看到这就有点懵逼了,也没看到啥时候拿到我们写的配置啊,到目前为止还只看到了规则而已。
我们需要看@EnableConfigurationBeanBindings和@EnableConfigurationBeanBinding这两个注解

@Import({ConfigurationBeanBindingsRegister.class})
public @interface EnableConfigurationBeanBindings {
    EnableConfigurationBeanBinding[] value();
}

@Import({ConfigurationBeanBindingRegistrar.class})
public @interface EnableConfigurationBeanBinding {
}

我们先看ConfigurationBeanBindingsRegister这个类
ConfigurationBeanBindingsRegister

public class ConfigurationBeanBindingsRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware {
    private ConfigurableEnvironment environment;

    public ConfigurationBeanBindingsRegister() {
    }

    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        AnnotationAttributes attributes = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(EnableConfigurationBeanBindings.class.getName()));
        //这里根据value拿到的就是@EnableConfigurationBeanBindings注解下的那个value
        AnnotationAttributes[] annotationAttributes = attributes.getAnnotationArray("value");
        //ConfigurationBeanBindingRegistrar 就是@EnableConfigurationBeanBinding 注解import的类
        ConfigurationBeanBindingRegistrar registrar = new ConfigurationBeanBindingRegistrar();
        registrar.setEnvironment(this.environment);//environment里含有spring所有配置,包括我们的dubbo配置
        //遍历每一个配置规则
        for(AnnotationAttributes element : annotationAttributes ){
        	registrar.registerConfigurationBeanDefinitions(element, registry);
        }
    }

    public void setEnvironment(Environment environment) {
        Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
        this.environment = (ConfigurableEnvironment)environment;
    }
}

registerConfigurationBeanDefinitions

public void registerConfigurationBeanDefinitions(Map<String, Object> attributes, BeanDefinitionRegistry registry) {
        String prefix = (String)AnnotationUtils.getRequiredAttribute(attributes, "prefix");
        prefix = this.environment.resolvePlaceholders(prefix);
        Class<?> configClass = (Class)AnnotationUtils.getRequiredAttribute(attributes, "type");
        boolean multiple = (Boolean)AnnotationUtils.getAttribute(attributes, "multiple", false);
        boolean ignoreUnknownFields = (Boolean)AnnotationUtils.getAttribute(attributes, "ignoreUnknownFields", true);
        boolean ignoreInvalidFields = (Boolean)AnnotationUtils.getAttribute(attributes, "ignoreInvalidFields", true);
        //上面就是在取@EnableConfigurationBeanBinding上的信息
        
        this.registerConfigurationBeans(prefix, configClass, multiple, ignoreUnknownFields, ignoreInvalidFields, registry);
    }

registerConfigurationBeans

private void registerConfigurationBeans(String prefix, Class<?> configClass, boolean multiple, boolean ignoreUnknownFields, boolean ignoreInvalidFields, BeanDefinitionRegistry registry) {
       	//根据前缀dubbo.application去spring environment里拿到我们配置的东西
       	//<name,dubbo-provider>
       	//<id,1>
        Map<String, Object> configurationProperties = PropertySourcesUtils.getSubProperties(this.environment.getPropertySources(), this.environment, prefix);
       	
        if (!CollectionUtils.isEmpty(configurationProperties)) {
        	//单配置走resolveSingleBeanName,如果配置了dubbo.application.id, beanNames就是id
        	//如果没有配置id,beanNames 是org.apache.dubbo.config.ApplicationConfig#0
            Set<String> beanNames = multiple ? this.resolveMultipleBeanNames(configurationProperties) : Collections.singleton(this.resolveSingleBeanName(configurationProperties, configClass, registry));
           
           for(String beanName:beanNames ){
           		this.registerConfigurationBean(beanName, configClass, multiple, ignoreUnknownFields, ignoreInvalidFields, configurationProperties, registry);
           }
           
           //todo,别忘了这行代码
           this.registerConfigurationBindingBeanPostProcessor(registry);
        
    }

registerConfigurationBean

private void registerConfigurationBean(String beanName, Class<?> configClass, boolean multiple, boolean ignoreUnknownFields, boolean ignoreInvalidFields, Map<String, Object> configurationProperties, BeanDefinitionRegistry registry) {

        BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(configClass);
        AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
        
        beanDefinition.setSource(EnableConfigurationBeanBinding.class);//这里很关键,后面数据绑定会用得到
        
  		//把这三个放到beanDefinition的attributes里
  		//configurationProperties是<name,dubbo-provider>,<id,1>
		beanDefinition.setAttribute("configurationProperties", configurationProperties);
        beanDefinition.setAttribute("ignoreUnknownFields", ignoreUnknownFields);
        beanDefinition.setAttribute("ignoreInvalidFields", ignoreInvalidFields);
        
        registry.registerBeanDefinition(beanName, beanDefinition);
    }

看到目前为止还只是生成了bd,并且把我们的dubbo配置放到了attributes里,下面我们要看最终如何把bd的attributes放到bean里面的

this.registerConfigurationBindingBeanPostProcessor(registry);

private void registerConfigurationBindingBeanPostProcessor(BeanDefinitionRegistry registry) {
        BeanRegistrar.registerInfrastructureBean(registry, "configurationBeanBindingPostProcessor", ConfigurationBeanBindingPostProcessor.class);
    }

ConfigurationBeanBindingPostProcessor

public class ConfigurationBeanBindingPostProcessor implements BeanPostProcessor, BeanFactoryAware{
   
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        //没有就返回null
        BeanDefinition beanDefinition = this.getNullableBeanDefinition(beanName);
        //如果bean是dubbo的配置类xxxXConfig
        if (this.isConfigurationBean(bean, beanDefinition)) {
        	//数据绑定
            this.bindConfigurationBean(bean, beanDefinition);
            //没啥看的,就是ConfigurationBeanCustomizer接口,回调customize函数
            this.customize(beanName, bean);
        }
        return bean;
    }
}

bean是否是dubbo的配置类

private boolean isConfigurationBean(Object bean, BeanDefinition beanDefinition) {
        return beanDefinition != null && 
        //beanDefinition.getSource()就是我上面提示的注意的地方
EnableConfigurationBeanBinding.class.equals(beanDefinition.getSource()) && ObjectUtils.nullSafeEquals(this.getBeanClassName(bean), beanDefinition.getBeanClassName());
    }

bindConfigurationBean

private void bindConfigurationBean(Object configurationBean, BeanDefinition beanDefinition) {
        //这三个就是之前塞到bd的attributes里的
        Map<String, Object> configurationProperties = getConfigurationProperties(beanDefinition);
        boolean ignoreUnknownFields = getIgnoreUnknownFields(beanDefinition);
        boolean ignoreInvalidFields = getIgnoreInvalidFields(beanDefinition);
        //数据绑定器把configurationProperties里的键值对,绑定到configurationBean对象里
        //即<name,dubbo-provider>,找到configurationBean属性名为name的,赋值为dubbo-provider
        this.getConfigurationBeanBinder().bind(configurationProperties, ignoreUnknownFields, ignoreInvalidFields, configurationBean);
       
    }

spring DataBinder测试用例

public static void main(String[] args) {

		User user = new User();
		DataBinder dataBinder = new DataBinder(user);
		dataBinder.setIgnoreInvalidFields(true);
		dataBinder.setIgnoreUnknownFields(true);

		List<PropertyValue> pvs = new ArrayList();
		pvs.add(new PropertyValue("name","lry"));
		MutablePropertyValues propertyValues = new MutablePropertyValues(pvs);

		dataBinder.bind(propertyValues);

		System.out.println(user);
	}

分析总结

大体流程如下:利用EnvironmentAware 扩展点可以拿到相应前缀的dubbo配置,根据不同前缀生成不同的Config bd,并把配置信息放到bd 的attributes,再利用spring扩展点BeanPostProcessor和自带的DataBinder数据器 把bd的 attributes数据绑定到 实际的xxxConfig.

个人思考:拿到某个前缀对应的配置信息后,为什么不直接new XXXConfig,然后利用Spring的扩展点BeanFactoryAware拿到beanFactory直接beanFactory.registrySingleton(“xxxConfig”,new XXXConfig)呢,我觉得这样也行的通啊

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值