第四节: Spring与Dubbo整合原理与源码分析
正文
整体架构和流程
应⽤启动类与配置
1 public class Application { 2 public static void main(String[] args) throws Exception { 3 AnnotationConfigApplicationContext context = new Annotati onConfigApplicationContext(ProviderConfiguration.class); 4 context.start(); 5 System.in.read(); 6 } 7 8 @Configuration 9 @EnableDubbo(scanBasePackages = "org.apache.dubbo.demo.provid er") 10 @PropertySource("classpath:/spring/dubbo-provider.properties" ) 11 static class ProviderConfiguration { 12 13 } 14 }
应⽤配置类为ProviderConfiguration, 在配置上有两个⽐较重要的注解
- @PropertySource表示将dubbo-provider.properties中的配置项添加到Spring容器中,可以通过
@Value的⽅式获取到配置项中的值 - @EnableDubbo(scanBasePackages = “org.apache.dubbo.demo.provider”)表示对指定包下的类
进⾏扫描,扫描@Service与@Reference注解,并且进⾏处理
@EnableDubbo
在EnableDubbo注解上,有另外两个注解,也是研究Dubbo最重要的两个注解
- @EnableDubboConfig
- @DubboComponentScan
1 @Target({ElementType.TYPE}) 2 @Retention(RetentionPolicy.RUNTIME) 3 @Inherited 4 @Documented 5 @Import(DubboConfigConfigurationRegistrar.class) 6 public @interface EnableDubboConfig { 7 boolean multiple() default true; 8 }
1 @Target(ElementType.TYPE) 2 @Retention(RetentionPolicy.RUNTIME) 3 @Documented 4 @Import(DubboComponentScanRegistrar.class) 5 public @interface DubboComponentScan { 6 String[] value() default {}; 7 8 String[] basePackages() default {}; 9 10 Class<?>[] basePackageClasses() default {}; 11 12 }
注意两个注解中对应的@Import注解所导⼊的类:
- DubboConfigConfigurationRegistrar
- DubboComponentScanRegistrar
Spring在启动时会解析这两个注解,并且执⾏对应的Registrar类中的registerBeanDefinitions⽅法(这
是Spring中提供的扩展功能。)
DubboConfigConfigurationRegistrar
流程
Spring启动时,会调⽤DubboConfigConfigurationRegistrar的registerBeanDefinitions⽅法,该⽅法
是利⽤Spring中的AnnotatedBeanDefinitionReader来读取:
- DubboConfigConfiguration.Single.class
- DubboConfigConfiguration.Multiple.class
这两个类上的注解。
1 @EnableDubboConfigBindings({ 2 @EnableDubboConfigBinding(prefix = "dubbo.application", type = ApplicationConfig.class), 3 @EnableDubboConfigBinding(prefix = "dubbo.module", type = Mod uleConfig.class), 4 @EnableDubboConfigBinding(prefix = "dubbo.registry", type = R egistryConfig.class), 5 @EnableDubboConfigBinding(prefix = "dubbo.protocol", type = P rotocolConfig.class), 6 @EnableDubboConfigBinding(prefix = "dubbo.monitor", type = Mo nitorConfig.class), 7 @EnableDubboConfigBinding(prefix = "dubbo.provider", type = P roviderConfig.class), 8 @EnableDubboConfigBinding(prefix = "dubbo.consumer", type = C onsumerConfig.class), 9 @EnableDubboConfigBinding(prefix = "dubbo.config-center", typ e = ConfigCenterBean.class), 10 @EnableDubboConfigBinding(prefix = "dubbo.metadata-report", t ype = MetadataReportConfig.class), 11 @EnableDubboConfigBinding(prefix = "dubbo.metrics", type = Me tricsConfig.class) 12 }) 13 public static class Single { 14 15 }
1 @EnableDubboConfigBindings({ 2 @EnableDubboConfigBinding(prefix = "dubbo.applications", type = ApplicationConfig.class, multiple = true), 3 @EnableDubboConfigBinding(prefix = "dubbo.modules", type = Mo duleConfig.class, multiple = true), 4 @EnableDubboConfigBinding(prefix = "dubbo.registries", type = RegistryConfig.class, multiple = true), 5 @EnableDubboConfigBinding(prefix = "dubbo.protocols", type = ProtocolConfig.class, multiple = true), 6 @EnableDubboConfigBinding(prefix = "dubbo.monitors", type = M onitorConfig.class, multiple = true), 7 @EnableDubboConfigBinding(prefix = "dubbo.providers", type = ProviderConfig.class, multiple = true), 8 @EnableDubboConfigBinding(prefix = "dubbo.consumers", type = ConsumerConfig.class, multiple = true), 9 @EnableDubboConfigBinding(prefix = "dubbo.config-centers", ty pe = ConfigCenterBean.class, multiple = true), 10 @EnableDubboConfigBinding(prefix = "dubbo.metadata-reports", type = MetadataReportConfig.class, multiple = true), 11 @EnableDubboConfigBinding(prefix = "dubbo.metricses", type = MetricsConfig.class, multiple = true) 12 }) 13 public static class Multiple { 14 15}
这两个类主要⽤到的就是@EnableDubboConfigBindings注解:
1 @Target({ElementType.TYPE}) 2 @Retention(RetentionPolicy.RUNTIME) 3 @Documented 4 @Import(DubboConfigBindingsRegistrar.class) 5 public @interface EnableDubboConfigBindings { 6 7 /** 8 * The value of {@link EnableDubboConfigBindings} 9 * 10 * @return non-null 11 */ 12 EnableDubboConfigBinding[] value(); 13 14 }
@EnableDubboConfigBindings注解上也有⼀个@Import注解,导⼊的是
DubboConfigBindingsRegistrar.class。该类会获取@EnableDubboConfigBindings注解中的value,
也就是多个@EnableDubboConfigBinding注解,然后利⽤DubboConfigBindingRegistrar去处理这些
@EnableDubboConfigBinding注解。
DubboConfigBindingRegistrar
此类中的主要⽅法是registerDubboConfigBeans()⽅法,主要功能就是获取⽤户所设置的properties⽂件
中的内容,对Properties⽂件进⾏解析,根据Properties⽂件的每个配置项的前缀、参数名、参数值⽣成
对应的BeanDefinition。
⽐如:
1 dubbo.application.name=dubbo-demo-provider1-application 2 dubbo.application.logger=log4j
前缀为"dubbo.application"的配置项,会⽣成⼀个ApplicationConfig类型的BeanDefinition,并且
name和logger属性为对应的值。
再⽐如:
1 dubbo.protocols.p1.name=dubbo 2 dubbo.protocols.p1.port=20880 3 dubbo.protocols.p1.host=0.0.0.0 4 5 dubbo.protocols.p2.name=dubbo 6 dubbo.protocols.p2.port=20881 7 dubbo.protocols.p2.host=0.0.0.0
⽐如前缀为"dubbo.protocols"的配置项,会⽣成两个ProtocolConfig类型的BeanDefinition,两个
BeanDefinition的beanName分别为p1和p2。
并且还会针对⽣成的每个BeanDefinition⽣成⼀个和它⼀对⼀绑定的BeanPostProcessor,类型为
DubboConfigBindingBeanPostProcessor.class。
DubboConfigBindingBeanPostProcessor
DubboConfigBindingBeanPostProcessor是⼀个BeanPostProcessor,在Spring启动过程中,会针对
所有的Bean对象进⾏后置加⼯,但是在DubboConfigBindingBeanPostProcessor中有如下判断:
1 if (this.beanName.equals(beanName) && bean instanceof AbstractConf ig)
所以DubboConfigBindingBeanPostProcessor并不会处理Spring容器中的所有Bean,它只会处理上⽂
由Dubbo所⽣成的Bean对象。
并且,在afterPropertiesSet()⽅法中,会先创建⼀个DefaultDubboConfigBinder。
DefaultDubboConfigBinder
当某个AbstractConfig类型的Bean,在经过DubboConfigBindingBeanPostProcessor处理时,此时
Bean对象中的属性是没有值的,会利⽤DefaultDubboConfigBinder进⾏赋值。底层就是利⽤Spring中的
DataBinder技术,结合properties⽂件对对应的属性进⾏赋值。
对应⼀个AbstractConfig类型(针对的其实是⼦类,⽐如ApplicationConfig、RegistryConfig)的
Bean,每个类都有⼀些属性,⽽properties⽂件是⼀个key-value对,所以实际上DataBinder就是将属性
名和properties⽂件中的key进⾏匹配,如果匹配成功,则把value赋值给属性。具体DataBinder技术是如
何⼯作的,请⾃⾏学习(不难)。
举个例⼦:
1 dubbo.application.name=dubbo-demo-provider1-application 2 dubbo.application.logger=log4j
对于此配置,它对应ApplicationConfig对象(beanName是⾃动⽣成的),所以最终ApplicationConfig
对象的name属性的值为“dubbo-demo-provider1-application”,logger属性的值为“log4j”。
对于
1 dubbo.protocols.p1.name=dubbo 2 dubbo.protocols.p1.port=20880 3 dubbo.protocols.p1.host=0.0.0.0
它对应ProtocolConfig对象(beanName为p1),所以最终ProtocolConfig对象的name属性的值
为“dubbo”,port属性的值为20880,host属性的值为“0.0.0.0”。
这样就完成了对properties⽂件的解析。
总结
DubboConfigConfigurationRegistrar的主要作⽤就是对propties⽂件进⾏解析并根据不同的配置项项⽣
成对应类型的Bean对象。
DubboComponentScanRegistrar
DubboConfigConfigurationRegistrar的作⽤是向Spring容器中注册两个Bean:
- ServiceAnnotationBeanPostProcessor
- ReferenceAnnotationBeanPostProcessor
ServiceAnnotationBeanPostProcessor
ServiceAnnotationBeanPostProcessor是⼀个BeanDefinitionRegistryPostProcessor,是⽤来注册
BeanDefinition的。
它的主要作⽤是扫描Dubbo的@Service注解,⼀旦扫描到某个@Service注解就把它以及被它注解的类当
做⼀个Dubbo服务,进⾏服务导出。
DubboClassPathBeanDefinitionScanner
DubboClassPathBeanDefinitionScanner是所Dubbo⾃定义的扫描器,继承了Spring中的
ClassPathBeanDefinitionScanner了。
DubboClassPathBeanDefinitionScanner相对于ClassPathBeanDefinitionScanner并没有做太多的改
变,只是把useDefaultFilters设置为了false,主要是因为Dubbo中的@Service注解是Dubbo⾃定义的,
在这个注解上并没有⽤@Component注解(因为Dubbo不是⼀定要结合Spring才能⽤),所以为了能利⽤
Spring的扫描逻辑,需要把useDefaultFilters设置为false。
没扫描到⼀个@Service注解,就会得到⼀个BeanDefinition,这个BeanDefinition的beanClass属性就是
具体的服务实现类。
但,如果仅仅只是这样,这只是得到了⼀个Spring中的Bean,对于Dubbo来说此时得到的Bean是⼀个服
务,并且,还需要解析@Service注解的配置信息,因为这些都是服务的参数信息,所以在扫描完了之后,
9
会针对所得到的每个BeanDefinition,都会额外的再⽣成⼀个ServiceBean类型的Bean对象。
ServiceBean
ServiceBean表示⼀个Dubbo服务,它有⼀些参数,⽐如:
- ref,表示服务的具体实现类
- interface,表示服务的接⼝
- parameters,表示服务的参数(@Service注解中所配置的信息)
- application,表示服务所属的应⽤
- protocols,表示服务所使⽤的协议
- registries,表示服务所要注册的注册中⼼
所以在扫描到⼀个@Service注解后,其实会得到两个Bean: - ⼀个就是服务实现类本身⼀个Bean对象
- ⼀个就是对应的ServiceBean类型的⼀个Bean对象
并且需要注意的是,ServiceBean实现了ApplicationListener接⼝,所以当Spring启动完成后会触发
onApplicationEvent()⽅法的调⽤,⽽在这个⽅法内会调⽤export(),这个⽅法就是服务导出的⼊⼝⽅
法。
关于RuntimeBeanReference参考https://www.yuque.com/renyong-jmovm/ufz328/gbwvk7。
ReferenceAnnotationBeanPostProcessor
ReferenceAnnotationBeanPostProcessor是处理@Reference注解的。
ReferenceAnnotationBeanPostProcessor的⽗类是AnnotationInjectedBeanPostProcessor,是⼀个
InstantiationAwareBeanPostProcessorAdapter,是⼀个BeanPostProcessor。
Spring在对Bean进⾏依赖注⼊时会调⽤AnnotationInjectedBeanPostProcessor的
postProcessPropertyValues()⽅法来给某个Bean按照ReferenceAnnotationBeanPostProcessor的逻
辑进⾏依赖注⼊。
在注⼊之前会查找注⼊点,被@Reference注解的属性或⽅法都是注⼊点。
针对某个Bean找到所有注⼊点之后,就会进⾏注⼊了,注⼊就是给属性或给set⽅法赋值,但是在赋值之
前得先得到⼀个值,此时就会调⽤ReferenceAnnotationBeanPostProcessor的doGetInjectedBean()
⽅法来得到⼀个对象,⽽这个对象的构造就⽐较复杂了,因为对于Dubbo来说,注⼊给某个属性的应该是
当前这个属性所对应的服务接⼝的代理对象。
11
但是在⽣成这个代理对象之前,还要考虑问题:
- 当前所需要引⼊的这个服务,是不是在本地就存在?不存在则要把按Dubbo的逻辑⽣成⼀个代理对象
- 当前所需要引⼊的这个服务,是不是已经被引⼊过了(是不是已经⽣成过代理对象了),如果是应该是
不⽤再重复去⽣成了。
⾸先如何判断当前所引⼊的服务是本地的⼀个服务(就是当前应⽤⾃⼰所提供的服务)。
我们前⾯提到,Dubbo通过@Service来提供⼀个服务,并且会⽣成两个Bean: - ⼀个服务实现类本身Bean
- ⼀个ServiceBean类型的Bean,这个Bean的名字是这么⽣成的:
1 private String generateServiceBeanName(AnnotationAttributes servic eAnnotationAttributes, Class<?> interfaceClass) { 2 ServiceBeanNameBuilder builder = create(interfaceClass, enviro nment) 3 .group(serviceAnnotationAttributes.getString("grou p")) 4 .version(serviceAnnotationAttributes.getString("ve rsion")); 5 return builder.build(); 6 }
是通过接⼝类型+group+version来作为ServiceBean类型Bean的名字的。
所以现在对于服务引⼊,也应该提前根据@Reference注解中的信息和属性接⼝类型去判断⼀下当前
Spring容器中是否存在对应的ServiceBean对象,如果存在则直接取出ServiceBean对象的ref属性所对应
的对象,作为要注⼊的结果。
然后如何判断当前所引⼊的这个服务是否已经被引⼊过了(是不是已经⽣成过代理对象了)。
这就需要在第⼀次引⼊某个服务后(⽣成代理对象后)进⾏缓存(记录⼀下)。Dubbo中是这么做的:
- ⾸先根据@Reference注解的所有信息+属性接⼝类型⽣成⼀个字符串
- 然后@Reference注解的所有信息+属性接⼝类型⽣成⼀个ReferenceBean对象(ReferenceBean对
象中的get⽅法可以得到⼀个Dubbo⽣成的代理对象,可以理解为服务引⼊的⼊⼝⽅法) - 把字符串作为beanName,ReferenceBean对象作为bean注册到Spring容器中,同时也会放⼊
referenceBeanCache中。
有了这些逻辑,@Reference注解服务引⼊的过程是这样的: - 得到当前所引⼊服务对应的ServiceBean的beanName(源码中叫referencedBeanName)
12 - 根据@Reference注解的所有信息+属性接⼝类型得到⼀个referenceBeanName
- 根据referenceBeanName从referenceBeanCache获取对应的ReferenceBean,如果没有则创建⼀
个ReferenceBean - 根据referencedBeanName(ServiceBean的beanName)判断Spring容器中是否存在该bean,如果
存在则给ref属性所对应的bean取⼀个别名,别名为referenceBeanName。
a. 如果Spring容器中不存在referencedBeanName对应的bean,则判断容器中是否存在
referenceBeanName所对应的Bean,如果不存在则将创建出来的ReferenceBean注册到Spring
容器中(此处这么做就⽀持了可以通过@Autowired注解也可以使⽤服务了,ReferenceBean是
⼀个FactoryBean) - 如果referencedBeanName存在对应的Bean,则额外⽣成⼀个代理对象,代理对象的
InvocationHandler会缓存在localReferenceBeanInvocationHandlerCache中,这样如果引⼊的是
同⼀个服务,并且这个服务在本地, - 如果referencedBeanName不存在对应的Bean,则直接调⽤ReferenceBean的get()⽅法得到⼀个代
理对象
第四节: Spring与Dubbo整合原理与源码分析
正文
整体架构和流程
应⽤启动类与配置
1 public class Application { 2 public static void main(String[] args) throws Exception { 3 AnnotationConfigApplicationContext context = new Annotati onConfigApplicationContext(ProviderConfiguration.class); 4 context.start(); 5 System.in.read(); 6 } 7 8 @Configuration 9 @EnableDubbo(scanBasePackages = "org.apache.dubbo.demo.provid er") 10 @PropertySource("classpath:/spring/dubbo-provider.properties" ) 11 static class ProviderConfiguration { 12 13 } 14 }
应⽤配置类为ProviderConfiguration, 在配置上有两个⽐较重要的注解
- @PropertySource表示将dubbo-provider.properties中的配置项添加到Spring容器中,可以通过
@Value的⽅式获取到配置项中的值 - @EnableDubbo(scanBasePackages = “org.apache.dubbo.demo.provider”)表示对指定包下的类
进⾏扫描,扫描@Service与@Reference注解,并且进⾏处理
@EnableDubbo
在EnableDubbo注解上,有另外两个注解,也是研究Dubbo最重要的两个注解
- @EnableDubboConfig
- @DubboComponentScan
1 @Target({ElementType.TYPE}) 2 @Retention(RetentionPolicy.RUNTIME) 3 @Inherited 4 @Documented 5 @Import(DubboConfigConfigurationRegistrar.class) 6 public @interface EnableDubboConfig { 7 boolean multiple() default true; 8 }
1 @Target(ElementType.TYPE) 2 @Retention(RetentionPolicy.RUNTIME) 3 @Documented 4 @Import(DubboComponentScanRegistrar.class) 5 public @interface DubboComponentScan { 6 String[] value() default {}; 7 8 String[] basePackages() default {}; 9 10 Class<?>[] basePackageClasses() default {}; 11 12 }
注意两个注解中对应的@Import注解所导⼊的类:
- DubboConfigConfigurationRegistrar
- DubboComponentScanRegistrar
Spring在启动时会解析这两个注解,并且执⾏对应的Registrar类中的registerBeanDefinitions⽅法(这
是Spring中提供的扩展功能。)
DubboConfigConfigurationRegistrar
流程
Spring启动时,会调⽤DubboConfigConfigurationRegistrar的registerBeanDefinitions⽅法,该⽅法
是利⽤Spring中的AnnotatedBeanDefinitionReader来读取:
- DubboConfigConfiguration.Single.class
- DubboConfigConfiguration.Multiple.class
这两个类上的注解。
1 @EnableDubboConfigBindings({ 2 @EnableDubboConfigBinding(prefix = "dubbo.application", type = ApplicationConfig.class), 3 @EnableDubboConfigBinding(prefix = "dubbo.module", type = Mod uleConfig.class), 4 @EnableDubboConfigBinding(prefix = "dubbo.registry", type = R egistryConfig.class), 5 @EnableDubboConfigBinding(prefix = "dubbo.protocol", type = P rotocolConfig.class), 6 @EnableDubboConfigBinding(prefix = "dubbo.monitor", type = Mo nitorConfig.class), 7 @EnableDubboConfigBinding(prefix = "dubbo.provider", type = P roviderConfig.class), 8 @EnableDubboConfigBinding(prefix = "dubbo.consumer", type = C onsumerConfig.class), 9 @EnableDubboConfigBinding(prefix = "dubbo.config-center", typ e = ConfigCenterBean.class), 10 @EnableDubboConfigBinding(prefix = "dubbo.metadata-report", t ype = MetadataReportConfig.class), 11 @EnableDubboConfigBinding(prefix = "dubbo.metrics", type = Me tricsConfig.class) 12 }) 13 public static class Single { 14 15 }
1 @EnableDubboConfigBindings({ 2 @EnableDubboConfigBinding(prefix = "dubbo.applications", type = ApplicationConfig.class, multiple = true), 3 @EnableDubboConfigBinding(prefix = "dubbo.modules", type = Mo duleConfig.class, multiple = true), 4 @EnableDubboConfigBinding(prefix = "dubbo.registries", type = RegistryConfig.class, multiple = true), 5 @EnableDubboConfigBinding(prefix = "dubbo.protocols", type = ProtocolConfig.class, multiple = true), 6 @EnableDubboConfigBinding(prefix = "dubbo.monitors", type = M onitorConfig.class, multiple = true), 7 @EnableDubboConfigBinding(prefix = "dubbo.providers", type = ProviderConfig.class, multiple = true), 8 @EnableDubboConfigBinding(prefix = "dubbo.consumers", type = ConsumerConfig.class, multiple = true), 9 @EnableDubboConfigBinding(prefix = "dubbo.config-centers", ty pe = ConfigCenterBean.class, multiple = true), 10 @EnableDubboConfigBinding(prefix = "dubbo.metadata-reports", type = MetadataReportConfig.class, multiple = true), 11 @EnableDubboConfigBinding(prefix = "dubbo.metricses", type = MetricsConfig.class, multiple = true) 12 }) 13 public static class Multiple { 14 15}
这两个类主要⽤到的就是@EnableDubboConfigBindings注解:
1 @Target({ElementType.TYPE}) 2 @Retention(RetentionPolicy.RUNTIME) 3 @Documented 4 @Import(DubboConfigBindingsRegistrar.class) 5 public @interface EnableDubboConfigBindings { 6 7 /** 8 * The value of {@link EnableDubboConfigBindings} 9 * 10 * @return non-null 11 */ 12 EnableDubboConfigBinding[] value(); 13 14 }
@EnableDubboConfigBindings注解上也有⼀个@Import注解,导⼊的是
DubboConfigBindingsRegistrar.class。该类会获取@EnableDubboConfigBindings注解中的value,
也就是多个@EnableDubboConfigBinding注解,然后利⽤DubboConfigBindingRegistrar去处理这些
@EnableDubboConfigBinding注解。
DubboConfigBindingRegistrar
此类中的主要⽅法是registerDubboConfigBeans()⽅法,主要功能就是获取⽤户所设置的properties⽂件
中的内容,对Properties⽂件进⾏解析,根据Properties⽂件的每个配置项的前缀、参数名、参数值⽣成
对应的BeanDefinition。
⽐如:
1 dubbo.application.name=dubbo-demo-provider1-application 2 dubbo.application.logger=log4j
前缀为"dubbo.application"的配置项,会⽣成⼀个ApplicationConfig类型的BeanDefinition,并且
name和logger属性为对应的值。
再⽐如:
1 dubbo.protocols.p1.name=dubbo 2 dubbo.protocols.p1.port=20880 3 dubbo.protocols.p1.host=0.0.0.0 4 5 dubbo.protocols.p2.name=dubbo 6 dubbo.protocols.p2.port=20881 7 dubbo.protocols.p2.host=0.0.0.0
⽐如前缀为"dubbo.protocols"的配置项,会⽣成两个ProtocolConfig类型的BeanDefinition,两个
BeanDefinition的beanName分别为p1和p2。
并且还会针对⽣成的每个BeanDefinition⽣成⼀个和它⼀对⼀绑定的BeanPostProcessor,类型为
DubboConfigBindingBeanPostProcessor.class。
DubboConfigBindingBeanPostProcessor
DubboConfigBindingBeanPostProcessor是⼀个BeanPostProcessor,在Spring启动过程中,会针对
所有的Bean对象进⾏后置加⼯,但是在DubboConfigBindingBeanPostProcessor中有如下判断:
1 if (this.beanName.equals(beanName) && bean instanceof AbstractConf ig)
所以DubboConfigBindingBeanPostProcessor并不会处理Spring容器中的所有Bean,它只会处理上⽂
由Dubbo所⽣成的Bean对象。
并且,在afterPropertiesSet()⽅法中,会先创建⼀个DefaultDubboConfigBinder。
DefaultDubboConfigBinder
当某个AbstractConfig类型的Bean,在经过DubboConfigBindingBeanPostProcessor处理时,此时
Bean对象中的属性是没有值的,会利⽤DefaultDubboConfigBinder进⾏赋值。底层就是利⽤Spring中的
DataBinder技术,结合properties⽂件对对应的属性进⾏赋值。
对应⼀个AbstractConfig类型(针对的其实是⼦类,⽐如ApplicationConfig、RegistryConfig)的
Bean,每个类都有⼀些属性,⽽properties⽂件是⼀个key-value对,所以实际上DataBinder就是将属性
名和properties⽂件中的key进⾏匹配,如果匹配成功,则把value赋值给属性。具体DataBinder技术是如
何⼯作的,请⾃⾏学习(不难)。
举个例⼦:
1 dubbo.application.name=dubbo-demo-provider1-application 2 dubbo.application.logger=log4j
对于此配置,它对应ApplicationConfig对象(beanName是⾃动⽣成的),所以最终ApplicationConfig
对象的name属性的值为“dubbo-demo-provider1-application”,logger属性的值为“log4j”。
对于
1 dubbo.protocols.p1.name=dubbo 2 dubbo.protocols.p1.port=20880 3 dubbo.protocols.p1.host=0.0.0.0
它对应ProtocolConfig对象(beanName为p1),所以最终ProtocolConfig对象的name属性的值
为“dubbo”,port属性的值为20880,host属性的值为“0.0.0.0”。
这样就完成了对properties⽂件的解析。
总结
DubboConfigConfigurationRegistrar的主要作⽤就是对propties⽂件进⾏解析并根据不同的配置项项⽣
成对应类型的Bean对象。
DubboComponentScanRegistrar
DubboConfigConfigurationRegistrar的作⽤是向Spring容器中注册两个Bean:
- ServiceAnnotationBeanPostProcessor
- ReferenceAnnotationBeanPostProcessor
ServiceAnnotationBeanPostProcessor
ServiceAnnotationBeanPostProcessor是⼀个BeanDefinitionRegistryPostProcessor,是⽤来注册
BeanDefinition的。
它的主要作⽤是扫描Dubbo的@Service注解,⼀旦扫描到某个@Service注解就把它以及被它注解的类当
做⼀个Dubbo服务,进⾏服务导出。
DubboClassPathBeanDefinitionScanner
DubboClassPathBeanDefinitionScanner是所Dubbo⾃定义的扫描器,继承了Spring中的
ClassPathBeanDefinitionScanner了。
DubboClassPathBeanDefinitionScanner相对于ClassPathBeanDefinitionScanner并没有做太多的改
变,只是把useDefaultFilters设置为了false,主要是因为Dubbo中的@Service注解是Dubbo⾃定义的,
在这个注解上并没有⽤@Component注解(因为Dubbo不是⼀定要结合Spring才能⽤),所以为了能利⽤
Spring的扫描逻辑,需要把useDefaultFilters设置为false。
没扫描到⼀个@Service注解,就会得到⼀个BeanDefinition,这个BeanDefinition的beanClass属性就是
具体的服务实现类。
但,如果仅仅只是这样,这只是得到了⼀个Spring中的Bean,对于Dubbo来说此时得到的Bean是⼀个服
务,并且,还需要解析@Service注解的配置信息,因为这些都是服务的参数信息,所以在扫描完了之后,
9
会针对所得到的每个BeanDefinition,都会额外的再⽣成⼀个ServiceBean类型的Bean对象。
ServiceBean
ServiceBean表示⼀个Dubbo服务,它有⼀些参数,⽐如:
- ref,表示服务的具体实现类
- interface,表示服务的接⼝
- parameters,表示服务的参数(@Service注解中所配置的信息)
- application,表示服务所属的应⽤
- protocols,表示服务所使⽤的协议
- registries,表示服务所要注册的注册中⼼
所以在扫描到⼀个@Service注解后,其实会得到两个Bean: - ⼀个就是服务实现类本身⼀个Bean对象
- ⼀个就是对应的ServiceBean类型的⼀个Bean对象
并且需要注意的是,ServiceBean实现了ApplicationListener接⼝,所以当Spring启动完成后会触发
onApplicationEvent()⽅法的调⽤,⽽在这个⽅法内会调⽤export(),这个⽅法就是服务导出的⼊⼝⽅
法。
关于RuntimeBeanReference参考https://www.yuque.com/renyong-jmovm/ufz328/gbwvk7。
ReferenceAnnotationBeanPostProcessor
ReferenceAnnotationBeanPostProcessor是处理@Reference注解的。
ReferenceAnnotationBeanPostProcessor的⽗类是AnnotationInjectedBeanPostProcessor,是⼀个
InstantiationAwareBeanPostProcessorAdapter,是⼀个BeanPostProcessor。
Spring在对Bean进⾏依赖注⼊时会调⽤AnnotationInjectedBeanPostProcessor的
postProcessPropertyValues()⽅法来给某个Bean按照ReferenceAnnotationBeanPostProcessor的逻
辑进⾏依赖注⼊。
在注⼊之前会查找注⼊点,被@Reference注解的属性或⽅法都是注⼊点。
针对某个Bean找到所有注⼊点之后,就会进⾏注⼊了,注⼊就是给属性或给set⽅法赋值,但是在赋值之
前得先得到⼀个值,此时就会调⽤ReferenceAnnotationBeanPostProcessor的doGetInjectedBean()
⽅法来得到⼀个对象,⽽这个对象的构造就⽐较复杂了,因为对于Dubbo来说,注⼊给某个属性的应该是
当前这个属性所对应的服务接⼝的代理对象。
11
但是在⽣成这个代理对象之前,还要考虑问题:
- 当前所需要引⼊的这个服务,是不是在本地就存在?不存在则要把按Dubbo的逻辑⽣成⼀个代理对象
- 当前所需要引⼊的这个服务,是不是已经被引⼊过了(是不是已经⽣成过代理对象了),如果是应该是
不⽤再重复去⽣成了。
⾸先如何判断当前所引⼊的服务是本地的⼀个服务(就是当前应⽤⾃⼰所提供的服务)。
我们前⾯提到,Dubbo通过@Service来提供⼀个服务,并且会⽣成两个Bean: - ⼀个服务实现类本身Bean
- ⼀个ServiceBean类型的Bean,这个Bean的名字是这么⽣成的:
1 private String generateServiceBeanName(AnnotationAttributes servic eAnnotationAttributes, Class<?> interfaceClass) { 2 ServiceBeanNameBuilder builder = create(interfaceClass, enviro nment) 3 .group(serviceAnnotationAttributes.getString("grou p")) 4 .version(serviceAnnotationAttributes.getString("ve rsion")); 5 return builder.build(); 6 }
是通过接⼝类型+group+version来作为ServiceBean类型Bean的名字的。
所以现在对于服务引⼊,也应该提前根据@Reference注解中的信息和属性接⼝类型去判断⼀下当前
Spring容器中是否存在对应的ServiceBean对象,如果存在则直接取出ServiceBean对象的ref属性所对应
的对象,作为要注⼊的结果。
然后如何判断当前所引⼊的这个服务是否已经被引⼊过了(是不是已经⽣成过代理对象了)。
这就需要在第⼀次引⼊某个服务后(⽣成代理对象后)进⾏缓存(记录⼀下)。Dubbo中是这么做的:
- ⾸先根据@Reference注解的所有信息+属性接⼝类型⽣成⼀个字符串
- 然后@Reference注解的所有信息+属性接⼝类型⽣成⼀个ReferenceBean对象(ReferenceBean对
象中的get⽅法可以得到⼀个Dubbo⽣成的代理对象,可以理解为服务引⼊的⼊⼝⽅法) - 把字符串作为beanName,ReferenceBean对象作为bean注册到Spring容器中,同时也会放⼊
referenceBeanCache中。
有了这些逻辑,@Reference注解服务引⼊的过程是这样的: - 得到当前所引⼊服务对应的ServiceBean的beanName(源码中叫referencedBeanName)
12 - 根据@Reference注解的所有信息+属性接⼝类型得到⼀个referenceBeanName
- 根据referenceBeanName从referenceBeanCache获取对应的ReferenceBean,如果没有则创建⼀
个ReferenceBean - 根据referencedBeanName(ServiceBean的beanName)判断Spring容器中是否存在该bean,如果
存在则给ref属性所对应的bean取⼀个别名,别名为referenceBeanName。
a. 如果Spring容器中不存在referencedBeanName对应的bean,则判断容器中是否存在
referenceBeanName所对应的Bean,如果不存在则将创建出来的ReferenceBean注册到Spring
容器中(此处这么做就⽀持了可以通过@Autowired注解也可以使⽤服务了,ReferenceBean是
⼀个FactoryBean) - 如果referencedBeanName存在对应的Bean,则额外⽣成⼀个代理对象,代理对象的
InvocationHandler会缓存在localReferenceBeanInvocationHandlerCache中,这样如果引⼊的是
同⼀个服务,并且这个服务在本地, - 如果referencedBeanName不存在对应的Bean,则直接调⽤ReferenceBean的get()⽅法得到⼀个代
理对象