示例代碼
public class Provider { /** * In order to make sure multicast registry works, need to specify '-Djava.net.preferIPv4Stack=true' before * launch the application */ public static void main(String[] args) throws Exception { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ProviderConfiguration.class); context.start(); System.in.read(); } @Configuration /** * <1>內部包含import注解 作為初始化 @EnableDubbo只是一個合並注解 具體看內部。。可以使用內部注解分開使用 * 快速入門:https://www.cnblogs.com/LQBlog/p/15410425.html */ @EnableDubbo(scanBasePackages = "provider") // @PropertySource("classpath:dubbo-provider.properties") // <2> spring 注解的方式加載properties static class ProviderConfiguration { } }
dubbo-provider配置
dubbo.application.name=soa-promotion-provider dubbo.registry.address=127.0.0.1:9080 dubbo.registry.check=false dubbo.protocol.name=dubbo dubbo.protocol.port=23888 dubbo.protocol.threads=500 dubbo.protocol.dispatcher=message dubbo.provider.delay=-1 dubbo.provider.retries=false
配置之后就會在容器里面創建對應的config對象
<1>EnableDubbo
@Target({ElementType.TYPE})//只能打在類上 @Retention(RetentionPolicy.RUNTIME)//保留到運行時 @Inherited//被此注解修飾的注解,打在某實現類上,有其他類繼承實現類,則也會繼承此注解 @Documented//打在某個類上的時候 類生成java doc時此注解會生成出來 @EnableDubboConfig//<2>@Import注解位置 記錄一下 還支持實現ImportSelector實現spring 切入 @DubboComponentScan public @interface EnableDubbo { /** * spring的注解 后續可以通過spring api獲取DubboComponentScan 拿到DubboComponentScan設置的值 * @return */ @AliasFor(annotation = DubboComponentScan.class, attribute = "basePackages") String[] scanBasePackages() default {}; /** * DubboComponentScan的合並注解 * @return */ @AliasFor(annotation = DubboComponentScan.class, attribute = "basePackageClasses") Class<?>[] scanBasePackageClasses() default {}; /** * DubboComponentScan的合並注解 * @return */ @AliasFor(annotation = EnableDubboConfig.class, attribute = "multiple") boolean multipleConfig() default true; }
<2>EnableDubboConfig
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented @Import(DubboConfigConfigurationRegistrar.class)//<3>初始化切入點 public @interface EnableDubboConfig { /** * 是否支持多配置 * dubbo.registrys.beaname1.address=192.168.0.1:1111 * dubbo.registrys.beaname2.address=192.168.0.1:1111 * @return */ boolean multiple() default true; }
@Import快速入門:鏈接
可以理解為當 bean被spring容器初始化則@Import則會被調用
DubboConfigConfigurationRegistrar
<3>DubboConfigConfigurationRegistrar
org.springframework.context.annotation.ImportBeanDefinitionRegistrar
public class DubboConfigConfigurationRegistrar implements ImportBeanDefinitionRegistrar { /** * * @param importingClassMetadata 包含@Import注解類的其他注解信息 * @param registry 通過他可以像容器注入bean */ @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { /** * 獲取EnableDubboConfig 注解信息 */ AnnotationAttributes attributes = AnnotationAttributes.fromMap( importingClassMetadata.getAnnotationAttributes(EnableDubboConfig.class.getName())); /** * 獲取multiple 默認為true 表示是否支持多個config配置 * 參考 */ boolean multiple = attributes.getBoolean("multiple"); /** * 初始化單個配置config * <4>注意此類打了@Import注解 繼續往下看 */ registerBeans(registry, DubboConfigConfiguration.Single.class); /** * <4>如果開啟多個配置 則初始化多個配置的config * dubbo.applications.beanname.id=ddd * dubbo.registrys.beaname2.address=192.168.0.1:1111 * dubbo.registrys.beaname2.address=192.168.0.1:2222 */ if (multiple) { // Since 2.6.6 https://github.com/apache/incubator-dubbo/issues/3193 registerBeans(registry, DubboConfigConfiguration.Multiple.class); } } }
<4>DubboConfigConfiguration
public class DubboConfigConfiguration { /** * <5>定義了單個配置 每個前綴對應的 類 */ @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 { } /** * <5>定義了多個配置每個前綴對應的類 */ @EnableDubboConfigBindings({ @EnableDubboConfigBinding(prefix = "dubbo.applications", type = ApplicationConfig.class, multiple = true), @EnableDubboConfigBinding(prefix = "dubbo.modules", type = ModuleConfig.class, multiple = true), @EnableDubboConfigBinding(prefix = "dubbo.registries", type = RegistryConfig.class, multiple = true), @EnableDubboConfigBinding(prefix = "dubbo.protocols", type = ProtocolConfig.class, multiple = true), @EnableDubboConfigBinding(prefix = "dubbo.monitors", type = MonitorConfig.class, multiple = true), @EnableDubboConfigBinding(prefix = "dubbo.providers", type = ProviderConfig.class, multiple = true), @EnableDubboConfigBinding(prefix = "dubbo.consumers", type = ConsumerConfig.class, multiple = true) }) public static class Multiple { } }
<5>EnableDubboConfigBindings
/** * Multiple {@link EnableDubboConfigBinding} {@link Annotation} * * @since 2.5.8 * @see EnableDubboConfigBinding */ @Target({ElementType.TYPE}) //注解限制打在的地方 @Retention(RetentionPolicy.RUNTIME) //注解保留時期 這里是保留到運行時 @Documented //標識被javadoc記錄 @Import(DubboConfigBindingsRegistrar.class)//注意看<3>.4處會注冊打上此注解的實例到容器 所以會觸發Import <6>@Import public @interface EnableDubboConfigBindings { /** * The value of {@link EnableDubboConfigBindings} * * @return non-null */ EnableDubboConfigBinding[] value(); }
DubboConfigBindingsRegistrar
<6>registerBeanDefinitions
com.alibaba.dubbo.config.spring.context.annotation.DubboConfigBindingsRegistrar#registerBeanDefinitions
@Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { //獲取EnableDubboConfigBindings注解實例 AnnotationAttributes attributes = AnnotationAttributes.fromMap( importingClassMetadata.getAnnotationAttributes(EnableDubboConfigBindings.class.getName())); //獲得values注解實例 EnableDubboConfigBinding AnnotationAttributes[] annotationAttributes = attributes.getAnnotationArray("value"); DubboConfigBindingRegistrar registrar = new DubboConfigBindingRegistrar(); registrar.setEnvironment(environment); //循環遍歷EnableDubboConfigBinding配置<4>處 能夠獲取到各個前綴和class的映射關系 for (AnnotationAttributes element : annotationAttributes) { //<7> 負責解析前綴和class映射關系 registrar.registerBeanDefinitions(element, registry); } }
DubboConfigBindingRegistrar
<7>registerBeanDefinitions
com.alibaba.dubbo.config.spring.context.annotation.DubboConfigBindingRegistrar#registerBeanDefinitions
protected void registerBeanDefinitions(AnnotationAttributes attributes, BeanDefinitionRegistry registry) { /*** * 獲得注解配置的前綴 比如 * dubbo.application * environment.resolvePlaceholders是為了避免我們配置的占位符所以轉換一下 * 比如${applictionPrefix}.name= */ String prefix = environment.resolvePlaceholders(attributes.getString("prefix")); /** * 獲取對應的class 如前綴是dubbo.application class就是com.alibaba.dubbo.config.ApplicationConfig * 泛型<extends AbstractConfig>限制class必須繼承AbstractConfig */ Class<? extends AbstractConfig> configClass = attributes.getClass("type"); /** * 是否是多個配置 */ boolean multiple = attributes.getBoolean("multiple"); /** * <8>創建對應的config bean */ registerDubboConfigBeans(prefix, configClass, multiple, registry); }
<8>registerDubboConfigBeans
com.alibaba.dubbo.config.spring.context.annotation.DubboConfigBindingRegistrar#registerBeanDefinitions
->
com.alibaba.dubbo.config.spring.context.annotation.DubboConfigBindingRegistrar#registerDubboConfigBeans
private void registerDubboConfigBeans(String prefix, Class<? extends AbstractConfig> configClass, boolean multiple, BeanDefinitionRegistry registry) { //獲得指定前綴的屬性 如:dubbo.application.name=333 key=name value=333 Map<String, Object> properties = getSubProperties(environment.getPropertySources(), prefix); if (CollectionUtils.isEmpty(properties)) { if (log.isDebugEnabled()) { log.debug("There is no property for binding to dubbo config class [" + configClass.getName() + "] within prefix [" + prefix + "]"); } return; } /** * 配置的mltiple為false取得配置文件配置的id作為beanName(id) * 如果沒獲取到則生成類全名稱#index 疊加 * multip是true則解析 * dubbo.applications.beanname1.name=ffff * dubbo.applications.beanname2.namee=ffff2 * 這個時候beanname1和beanname2會作為二beanid */ Set<String> beanNames = multiple ? resolveMultipleBeanNames(properties) : Collections.singleton(resolveSingleBeanName(properties, configClass, registry)); for (String beanName : beanNames) { /** * 這里並沒有真正的初始化config對象,只是將bean的定義告訴容器 供容器初始化 * 初始化后config各個屬性都是空 */ registerDubboConfigBean(beanName, configClass, registry); /** * <9>初始化對應的DubboConfigBindingBeanPostProcessor 用於后面為將properties配置注入到對應的config */ registerDubboConfigBindingBeanPostProcessor(prefix, beanName, multiple, registry); } //向容器創建NamePropertyDefaultValueDubboConfigBeanCustomizer,供DubboConfigBindingBeanPostProcessor使用 registerDubboConfigBeanCustomizers(registry); }
<9>registerDubboConfigBindingBeanPostProcessor
com.alibaba.dubbo.config.spring.context.annotation.DubboConfigBindingRegistrar#registerBeanDefinitions
->
com.alibaba.dubbo.config.spring.context.annotation.DubboConfigBindingRegistrar#registerDubboConfigBeans
->
com.alibaba.dubbo.config.spring.context.annotation.DubboConfigBindingRegistrar#registerDubboConfigBindingBeanPostProcessor
private void registerDubboConfigBindingBeanPostProcessor(String prefix, String beanName, boolean multiple, BeanDefinitionRegistry registry) { //獲得DubboConfigBindingBeanPostProcessor的class Class<?> processorClass = DubboConfigBindingBeanPostProcessor.class; //獲得DubboConfigBindingBeanPostProcessor的class 的BeanDefinitionBuilder BeanDefinitionBuilder builder = rootBeanDefinition(processorClass); //獲得前綴 String actualPrefix = multiple ? normalizePrefix(prefix) + beanName : prefix; //告訴容器初始化時 調用2個參數的構造函數 傳入前綴和beanName 供后續使用 builder.addConstructorArgValue(actualPrefix).addConstructorArgValue(beanName); AbstractBeanDefinition beanDefinition = builder.getBeanDefinition(); beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); /** * 告訴容器創建DubboConfigBindingBeanPostProcessor 對象 * 實現了InitializingBean 在初始化會先調用<10> * 實現了BeanPostProcessor 用於所有bean創建前創建后的前置處理<13> */ registerWithGeneratedName(beanDefinition, registry); if (log.isInfoEnabled()) { log.info("The BeanPostProcessor bean definition [" + processorClass.getName() + "] for dubbo config bean [name : " + beanName + "] has been registered."); } }
DubboConfigBindingBeanPostProcessor
<10>afterPropertiesSet
com.alibaba.dubbo.config.spring.beans.factory.annotation.DubboConfigBindingBeanPostProcessor#afterPropertiesSet
@Override public void afterPropertiesSet() throws Exception { /*** * <11><從容器獲取DubboConfigBinder組件 獲取不到則使用 DefaultDubboConfigBinder */ initDubboConfigBinder(); /** *<12>從容器獲取dubboConfigBeanCustomizer組件 * 默認NamePropertyDefaultValueDubboConfigBeanCustomizer在<8>處初始化 * 用於給容器里面的config做初始化 */ initConfigBeanCustomizers(); }
<11>initDubboConfigBinder
com.alibaba.dubbo.config.spring.beans.factory.annotation.DubboConfigBindingBeanPostProcessor#afterPropertiesSet
->
com.alibaba.dubbo.config.spring.beans.factory.annotation.DubboConfigBindingBeanPostProcessor#initDubboConfigBinder
private void initDubboConfigBinder() { if (dubboConfigBinder == null) { try { //從容器獲得這個類型的bean 這里我們可以做自己的擴展 dubboConfigBinder = applicationContext.getBean(DubboConfigBinder.class); } catch (BeansException ignored) { if (log.isDebugEnabled()) { log.debug("DubboConfigBinder Bean can't be found in ApplicationContext."); } //獲取不到則使用默認的 並且把環境信息傳入供后續使用 dubboConfigBinder = createDubboConfigBinder(applicationContext.getEnvironment()); } } }
<12>initConfigBeanCustomizers
com.alibaba.dubbo.config.spring.beans.factory.annotation.DubboConfigBindingBeanPostProcessor#afterPropertiesSet
->
com.alibaba.dubbo.config.spring.beans.factory.annotation.DubboConfigBindingBeanPostProcessor#initConfigBeanCustomizers
private void initConfigBeanCustomizers() { //從容器獲取DubboConfigBeanCustomizer的實現 可以有多個 注意在<8>處默認初始化了NamePropertyDefaultValueDubboConfigBeanCustomizer在 Collection<DubboConfigBeanCustomizer> configBeanCustomizers = beansOfTypeIncludingAncestors(applicationContext, DubboConfigBeanCustomizer.class).values(); this.configBeanCustomizers = new ArrayList<DubboConfigBeanCustomizer>(configBeanCustomizers); //根據@Sort注解排序 AnnotationAwareOrderComparator.sort(this.configBeanCustomizers); }
<13>postProcessBeforeInitialization
com.alibaba.dubbo.config.spring.beans.factory.annotation.DubboConfigBindingBeanPostProcessor#postProcessBeforeInitialization
由spring調度 實現了BeanPostProcessor 接口 在spring 創建bean之前都會使用Processor做前置和后置處理
/** *bean創建之后的后置處理 * @param bean * @param beanName * @return * @throws BeansException */ @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { /** *當前config是否能被beanProcessor處理 * 比如處理 dubbo.application.id=ddd 那么初始化這個config 則會被當時初始化的BeanPostProcessor處理 *beanName 在<9>處初始化一個前綴對應一個config */ if (beanName.equals(this.beanName) && bean instanceof AbstractConfig) { AbstractConfig dubboConfig = (AbstractConfig) bean; /** *<14>進行綁定 */ bind(prefix, dubboConfig); /** * <15>進行綁定 */ customize(beanName, dubboConfig); } return bean; } /** * * 並創建之前的前置處理 並沒有做具體實現 * @param bean * @param beanName * @return * @throws BeansException */ @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; }
<14>bind
com.alibaba.dubbo.config.spring.beans.factory.annotation.DubboConfigBindingBeanPostProcessor#postProcessBeforeInitialization
->
com.alibaba.dubbo.config.spring.beans.factory.annotation.DubboConfigBindingBeanPostProcessor#bind
private void bind(String prefix, AbstractConfig dubboConfig) { //<16>傳入前綴和config對象執行bind邏輯如果沒有指定 這里就是調用默認的DefaultDubboConfigBinder dubboConfigBinder.bind(prefix, dubboConfig); if (log.isInfoEnabled()) { log.info("The properties of bean [name : " + beanName + "] have been binding by prefix of " + "configuration properties : " + prefix); } }
<15>customize
com.alibaba.dubbo.config.spring.beans.factory.annotation.DubboConfigBindingBeanPostProcessor#postProcessBeforeInitialization
->
com.alibaba.dubbo.config.spring.beans.factory.annotation.DubboConfigBindingBeanPostProcessor#customize
private void customize(String beanName, AbstractConfig dubboConfig) { //<8>處默認初始化了一個 NamePropertyDefaultValueDubboConfigBeanCustomizer for (DubboConfigBeanCustomizer customizer : configBeanCustomizers) { //<17> customizer.customize(beanName, dubboConfig); } }
DefaultDubboConfigBinder
<16>bind
com.alibaba.dubbo.config.spring.beans.factory.annotation.DubboConfigBindingBeanPostProcessor#postProcessBeforeInitialization
->
com.alibaba.dubbo.config.spring.beans.factory.annotation.DubboConfigBindingBeanPostProcessor#bind
->
com.alibaba.dubbo.config.spring.context.properties.DefaultDubboConfigBinder#bind
public class DefaultDubboConfigBinder extends AbstractDubboConfigBinder { @Override public <C extends AbstractConfig> void bind(String prefix, C dubboConfig) { DataBinder dataBinder = new DataBinder(dubboConfig); // Set ignored* dataBinder.setIgnoreInvalidFields(isIgnoreInvalidFields()); dataBinder.setIgnoreUnknownFields(isIgnoreUnknownFields()); // 獲得指定前綴的配置信息 如 dubbo.application.name=aaa 則返回key=name value=aaa Map<String, Object> properties = getSubProperties(getPropertySources(), prefix); //用於將配置綁定 MutablePropertyValues propertyValues = new MutablePropertyValues(properties); // 這個時候propertie配置的信息 將綁定到config對象里面 dataBinder.bind(propertyValues); } }
NamePropertyDefaultValueDubboConfigBeanCustomizer
<17>customize
com.alibaba.dubbo.config.spring.beans.factory.annotation.DubboConfigBindingBeanPostProcessor#postProcessBeforeInitialization
->
com.alibaba.dubbo.config.spring.beans.factory.annotation.DubboConfigBindingBeanPostProcessor#customize
->
com.alibaba.dubbo.config.spring.context.config.NamePropertyDefaultValueDubboConfigBeanCustomizer#customize
public void customize(String beanName, AbstractConfig dubboConfigBean) { //獲得config的Name屬性 PropertyDescriptor propertyDescriptor = getPropertyDescriptor(dubboConfigBean.getClass(), PROPERTY_NAME); //是否含有name屬性 if (propertyDescriptor != null) { // "name" property is present //獲得對應的getMethod Method getNameMethod = propertyDescriptor.getReadMethod(); if (getNameMethod == null) { // if "getName" method is absent return; } //獲得name值 正常情況 我們前面bind已經綁定了 Object propertyValue = ReflectionUtils.invokeMethod(getNameMethod, dubboConfigBean); //如果我們配置了 前面會正常bind這里就會為空 if (propertyValue != null) { // If The return value of "getName" method is not null return; } //如果沒有配置name config又有name屬性 則將傳入的設置 正常情況不會到這里來 可以看<13>處 Method setNameMethod = propertyDescriptor.getWriteMethod(); if (setNameMethod != null && getNameMethod != null) { // "setName" and "getName" methods are present if (Arrays.equals(of(String.class), setNameMethod.getParameterTypes())) { // the param type is String // set bean name to the value of the "name" property ReflectionUtils.invokeMethod(setNameMethod, dubboConfigBean, beanName); } } } }
對於parameters配置
dubbo.application.parameters['validation']=jvalidation
總結
properties配置是如何初始化的
1.EnableDubbo 是一個組合注解 這個注解上打上了@EnableDubboConfig注解 具體初始化時通過EnableDubboConfig 注解處理的
2.此注解有個@Import(DubboConfigConfigurationRegistrar.class)作為初始化切入點
3.DubboConfigConfigurationRegistrar會根據注解配置的multiple是true還是false判斷是否讀取支持多配置來判斷是否像容器注冊DubboConfigConfiguration.Single,DubboConfigConfiguration.Single或者Multiple
4.這2個類上面也打了@EnableDubboConfigBindings 配置了配置前綴和config的class映射關系 具體看<4><5>
5.EnableDubboConfigBindings此注解也打了@Import(DubboConfigBindingsRegistrar.class)作為切入點
6.DubboConfigBindingsRegistrar內部會通過config和映射關系 像spring注入一個config實例
7.同時像容器注冊一個DubboConfigBindingBeanPostProcessor 傳入前綴和config beanName
8.DubboConfigBindingBeanPostProcessor BeanPostProcessor, InitializingBean
9.通過afterPropertiesSet在初始化2個綁定器
10.通過BeanPostProcessor 在spring創建對象后置 后判斷這個處理器是不是處理這個類型 如果是 則通過綁定器將配置信息通過反射設置到config對象