版本说明
springboot starter : 0.1.1
dubbo版本: 2.6.2
自动配置类
@Configuration@ConditionalOnProperty(prefix = DUBBO_PREFIX, name = "enabled", matchIfMissing = true, havingValue = "true")@ConditionalOnClass(AbstractConfig.class)public class DubboAutoConfiguration { // 单个dubbo配置绑定bean , 默认就是单个 @EnableDubboConfig protected static class SingleDubboConfigConfiguration { } /** * 多个dubbo配置绑定bean , 默认不使用。 * */ @ConditionalOnProperty(name = MULTIPLE_CONFIG_PROPERTY_NAME, havingValue = "true") @EnableDubboConfig(multiple = true) protected static class MultipleDubboConfigConfiguration { } /** * service类,服务提供者的BeanDefinitionRegistryPostProcessor类,用来解析 * @Service注解,生成Service的BeanDefinition类,放入spring容器,供spring容器生成Bean * */ @ConditionalOnProperty(name = BASE_PACKAGES_PROPERTY_NAME) @ConditionalOnClass(RelaxedPropertyResolver.class) @Bean public ServiceAnnotationBeanPostProcessor serviceAnnotationBeanPostProcessor(Environment environment) { RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(environment); Set packagesToScan = resolver.getProperty(BASE_PACKAGES_PROPERTY_NAME, Set.class, emptySet()); return new ServiceAnnotationBeanPostProcessor(packagesToScan); } // springboot dataBinder 机制的扩展,用来将具体的属性设置到相应的实体类里面去。 @ConditionalOnClass(RelaxedDataBinder.class) @Bean @Scope(scopeName = SCOPE_PROTOTYPE) public RelaxedDubboConfigBinder relaxedDubboConfigBinder() { return new RelaxedDubboConfigBinder(); } /** * 用来解析@Reference 注解,消费者引用哪些服务,通过这个注解来进行引用 * 给标注这个@Reference注解的属性赋值, 和@autowired的做法类似。 * */ @ConditionalOnMissingBean @Bean(name = ReferenceAnnotationBeanPostProcessor.BEAN_NAME) public ReferenceAnnotationBeanPostProcessor referenceAnnotationBeanPostProcessor() { return new ReferenceAnnotationBeanPostProcessor(); }}复制代码
配置说明:
SingleDubboConfigConfiguration : 引入了单个dubbo配置绑定bean的配置 , 默认使用
// 配置如下dubbo.applicationdubbo.moduledubbo.registrydubbo.protocoldubbo.monitordubbo.providerdubbo.consumer复制代码
**MultipleDubboConfigConfiguration ** :多个dubbo配置绑定bean , 默认不使用。Dubbo @Service 和 @Reference 允许 Dubbo 应用关联ApplicationConfig Bean 或者指定多个RegistryConfig Bean 等能力。换句话说,Dubbo 应用上下文中可能存在多个ApplicationConfig 等 Bean定义。
// 配置如下dubbo.applicationsdubbo.modulesdubbo.registriesdubbo.protocolsdubbo.monitorsdubbo.providersdubbo.consumers复制代码
**serviceAnnotationBeanPostProcessor ** :解析service类注解的类,如果在spring boot启动类上配置了@DubboComponentScan 则默认不使用。
**referenceAnnotationBeanPostProcessor ** : 为@Reference注入对象,如果在spring boot启动类上配置了@DubboComponentScan 则默认不使用。
因为在@DubboComponentScan这个注解中引入了DubboComponentScanRegistrar这个注册类,该类中做了解析@service注解和@Reference的事情
@EnableDubboConfig
@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Inherited@Documented@Import(DubboConfigConfigurationSelector.class) // 主要作用是这个public @interface EnableDubboConfig { /** * It indicates whether binding to multiple Spring Beans. * * @return the default value is false
* @revised 2.5.9 */ boolean multiple() default false;}复制代码
主要的作用就是导入了这个类DubboConfigConfigurationSelector
DubboConfigConfigurationSelector
public class DubboConfigConfigurationSelector implements ImportSelector, Ordered { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { // 获取注解上的属性,这个是通过@EnableDubboConfig导入的,所以AnnotationMetadata里面就包含了这个注解的值 AnnotationAttributes attributes = AnnotationAttributes.fromMap( importingClassMetadata.getAnnotationAttributes(EnableDubboConfig.class.getName())); // 是否是多配置,默认为false boolean multiple = attributes.getBoolean("multiple"); if (multiple) { return of(DubboConfigConfiguration.Multiple.class.getName()); } else { // 这里就直接讲解单配置的。 return of(DubboConfigConfiguration.Single.class.getName()); } } private static T[] of(T... values) { return values; } @Override public int getOrder() { return HIGHEST_PRECEDENCE; }}复制代码
DubboConfigConfigurationSelector这个类实现了ImportSelector 接口,该接口的selectImports方法就是返回bean的名称,供spring初始化,所以这里返回了
DubboConfigConfiguration.Single.class.getName() , spring就会初始化这个类了。
Single
DubboConfigConfiguration.Single的代码如下 , 通过@EnableDubboConfigBindings注解,导入了多个@EnableDubboConfigBinding
@EnableDubboConfigBindings({ @EnableDubboConfigBinding(prefix = "dubbo.application", type = ApplicationConfig.class), @EnableDubboConfigBinding(prefix = "dubbo.module", type = ModuleConfig.class), @EnableDubboConfigBinding(prefix = "dubbo.registry", type = RegistryConfig.class), @EnableDubboConfigBinding(prefix = "dubbo.protocol", type = ProtocolConfig.class), @EnableDubboConfigBinding(prefix = "dubbo.monitor", type = MonitorConfig.class), @EnableDubboConfigBinding(prefix = "dubbo.provider", type = ProviderConfig.class), @EnableDubboConfigBinding(prefix = "dubbo.consumer", type = ConsumerConfig.class) }) public static class Single { }复制代码
EnableDubboConfigBindings
由上面可以看到,spring在初始化Single这个类的时候,必然会加载他上面的注解,该类的主要作用就是为了导入它上面的注解,@EnableDubboConfigBindings
@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Import(DubboConfigBindingsRegistrar.class) // 导入了这个类。public @interface EnableDubboConfigBindings { /** * The value of {@link EnableDubboConfigBindings} * * @return non-null */ EnableDubboConfigBinding[] value();}复制代码
@EnableDubboConfigBindings 注解导入了DubboConfigBindingsRegistrar这个类,该类的作用是将配置属性和dubbo的配置进行绑定。注解的value是7个子注解
@EnableDubboConfigBinding ,后面DubboConfigBindingsRegistrar解析的时候,会获取到这个7个子注解,将对应的属性和dubbo的配置类进行绑定。
DubboConfigBindingsRegistrar
public class DubboConfigBindingsRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware { private ConfigurableEnvironment environment; @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {// 获取导入此类的注解信息,@EnableDubboConfigBindings AnnotationAttributes attributes = AnnotationAttributes.fromMap( importingClassMetadata.getAnnotationAttributes(EnableDubboConfigBindings.class.getName())); // 获取@EnableDubboConfigBinding 子注解 AnnotationAttributes[] annotationAttributes = attributes.getAnnotationArray("value"); // 初始化DubboConfigBindingRegistrar类,该类的主要作用就是为了解析单个的@EnableDubboConfigBinding注解 DubboConfigBindingRegistrar registrar = new DubboConfigBindingRegistrar(); registrar.setEnvironment(environment); for (AnnotationAttributes element : annotationAttributes) {// 循环注册,通过注解里面的信息,生成Dubbo配置的BeanDefinition,最后放入spring容器中,供spring容器实例化。 registrar.registerBeanDefinitions(element, registry); } } @Override public void setEnvironment(Environment environment) { Assert.isInstanceOf(ConfigurableEnvironment.class, environment); this.environment = (ConfigurableEnvironment) environment; }}复制代码
注册dubbo的配置bean
protected void registerBeanDefinitions(AnnotationAttributes attributes, BeanDefinitionRegistry registry) {//1. 从环境 中取出 响应的属性名 String prefix = environment.resolvePlaceholders(attributes.getString("prefix")); // 2. 获取dubbo的配置类的class Class extends AbstractConfig> configClass = attributes.getClass("type"); // 3. 获取是否是多个dubbo的配置 boolean multiple = attributes.getBoolean("multiple"); // 注册dubbo的配置bean registerDubboConfigBeans(prefix, configClass, multiple, registry); }复制代码
步骤说明:
1.参数attributes就是@EnableDubboConfigBinding里面的属性,获取prefix属性值,就是获取到了:dubbo.application
例:
@EnableDubboConfigBinding(prefix = "dubbo.application", type = ApplicationConfig.class)
2.获取dubbo的配置类的class,也就是获取到了ApplicationConfig.class
3.获取multiple的值,默认没有配置就是false
4.调用registerDubboConfigBeans方法生成dubbo的配置bean
private void registerDubboConfigBeans(String prefix, Class extends AbstractConfig> configClass, boolean multiple, BeanDefinitionRegistry registry) {// 根据属性名,如:dubbo.application 获取具体的属性值 Map properties = getSubProperties(environment.getPropertySources(), prefix); if (CollectionUtils.isEmpty(properties)) { // 如果没有配置,则没有必要生成对应的dubbo配置bean了 if (log.isDebugEnabled()) { log.debug("There is no property for binding to dubbo config class [" + configClass.getName() + "] within prefix [" + prefix + "]"); } return; }// BeanName Set beanNames = multiple ? resolveMultipleBeanNames(properties) : Collections.singleton(resolveSingleBeanName(properties, configClass, registry)); for (String beanName : beanNames) { // 生成bena registerDubboConfigBean(beanName, configClass, registry); // 注册dubbo的DubboConfigBindingBeanPostProcessor registerDubboConfigBindingBeanPostProcessor(prefix, beanName, multiple, registry); } } private void registerDubboConfigBean(String beanName, Class extends AbstractConfig> configClass, BeanDefinitionRegistry registry) {// 生成BeanDefinitionBuilder BeanDefinitionBuilder builder = rootBeanDefinition(configClass); AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();// 通过BeanDefinitionRegistry注册dubbo的配置bean registry.registerBeanDefinition(beanName, beanDefinition); if (log.isInfoEnabled()) { log.info("The dubbo config bean definition [name : " + beanName + ", class : " + configClass.getName() + "] has been registered."); } }复制代码
DubboConfigBindingBeanPostProcessor
这个类是在dubbo的配置类初始化完成后会执行响应的方法。用来将属性值设置到对应的属性里面去,在springboot中我们存在这种情况
first-name,firstName, FIRST_NAME , 比如我们在yaml文件中配置这样的属性,我们的java bean中的属性是firstName , 在使用@ConfigurationProperties
注解的时候我们无需担心,如果不是用springboot自身的config类来注入,那么我们自己处理这种情况就会变的非常麻烦,所以dubbo选择的是通过RelaxedDataBinder类来处理这个问题。这是spring boot的机制。
DubboConfigBindingBeanPostProcessor类实现了BeanPostProcessor, ApplicationContextAware, InitializingBean 这三个接口,下面是挑了一些重要的方法展示出来 , **每个dubbo配置类都有相应的DubboConfigBindingBeanPostProcessor **
public DubboConfigBindingBeanPostProcessor(String prefix, String beanName) { Assert.notNull(prefix, "The prefix of Configuration Properties must not be null"); Assert.notNull(beanName, "The name of bean must not be null"); this.prefix = prefix; // 属性前缀 this.beanName = beanName; // dubbo的配置类名 }@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { // 会在每一个bean实例化之后、初始化(如afterPropertiesSet方法)之前被调用。 if (beanName.equals(this.beanName) && bean instanceof AbstractConfig) { AbstractConfig dubboConfig = (AbstractConfig) bean; // 将属性和配置进行绑定 dubboConfigBinder.bind(prefix, dubboConfig); if (log.isInfoEnabled()) { log.info("The properties of bean [name : " + beanName + "] have been binding by prefix of " + "configuration properties : " + prefix); } } return bean;}@Overridepublic void afterPropertiesSet() throws Exception { // DubboConfigBindingBeanPostProcessor 初始化之后就会执行 if (dubboConfigBinder == null) { try { // 从容器中获取DubboConfigBinder , DubboConfigBinder的作用范围是prototype , 每次调用getbean都会新创建一个 dubboConfigBinder = applicationContext.getBean(DubboConfigBinder.class); } catch (BeansException ignored) { if (log.isDebugEnabled()) { log.debug("DubboConfigBinder Bean can't be found in ApplicationContext."); } // Use Default implementation dubboConfigBinder = createDubboConfigBinder(applicationContext.getEnvironment()); } } dubboConfigBinder.setIgnoreUnknownFields(ignoreUnknownFields); dubboConfigBinder.setIgnoreInvalidFields(ignoreInvalidFields);}复制代码
dubboConfigBinder的代码如下,下面主要就是调用springboot 的dataBinder机制进行属性设置了
public class RelaxedDubboConfigBinder extends AbstractDubboConfigBinder { @Override public void bind(String prefix, C dubboConfig) { RelaxedDataBinder relaxedDataBinder = new RelaxedDataBinder(dubboConfig); // Set ignored* relaxedDataBinder.setIgnoreInvalidFields(isIgnoreInvalidFields()); relaxedDataBinder.setIgnoreUnknownFields(isIgnoreUnknownFields()); //从Environment中获取属性 Map properties = getSubProperties(getPropertySources(), prefix); // 将属性MAP转换为MutablePropertyValues MutablePropertyValues propertyValues = new MutablePropertyValues(properties); // 绑定 relaxedDataBinder.bind(propertyValues); }}复制代码
通过上面的源码,可以看出来,dubbo的配置是一环接着一环,很多时候一个不起眼的地方就是往下走的关键代码,他主要是通过注解的导入配置类,然后通过
BeanDefinitionRegistry生成对应的beanDefintion放入spring容器中。
本文所解析的这些源码均不涉及dubbo的核心功能,仅仅是讲了dubbo启动之后,如何获取到配置,如果进行配置装配,方便大家后续有个好的理解。
有兴趣可以看下一spring的扩展机制,dubbo中都有大量的使用到。
作者:sharedCode
链接:https://juejin.cn/post/6898141178563133453
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。