Dubbo 第四节: Spring与Dubbo整合原理与源码分析

简介: DubboConfigConfigurationRegistrar的主要作⽤就是对propties⽂件进⾏解析并根据不同的配置项项⽣成对应类型的Bean对象。

第四节: 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, 在配置上有两个⽐较重要的注解

  1. @PropertySource表示将dubbo-provider.properties中的配置项添加到Spring容器中,可以通过
    @Value的⽅式获取到配置项中的值
  2. @EnableDubbo(scanBasePackages = “org.apache.dubbo.demo.provider”)表示对指定包下的类
    进⾏扫描,扫描@Service与@Reference注解,并且进⾏处理

@EnableDubbo

在EnableDubbo注解上,有另外两个注解,也是研究Dubbo最重要的两个注解

  1. @EnableDubboConfig
  2. @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注解所导⼊的类:

  1. DubboConfigConfigurationRegistrar
  2. DubboComponentScanRegistrar
    Spring在启动时会解析这两个注解,并且执⾏对应的Registrar类中的registerBeanDefinitions⽅法(这
    是Spring中提供的扩展功能。)

DubboConfigConfigurationRegistrar

流程

Spring启动时,会调⽤DubboConfigConfigurationRegistrar的registerBeanDefinitions⽅法,该⽅法

是利⽤Spring中的AnnotatedBeanDefinitionReader来读取:

  1. DubboConfigConfiguration.Single.class
  2. 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:

  1. ServiceAnnotationBeanPostProcessor
  2. 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服务,它有⼀些参数,⽐如:

  1. ref,表示服务的具体实现类
  2. interface,表示服务的接⼝
  3. parameters,表示服务的参数(@Service注解中所配置的信息)
  4. application,表示服务所属的应⽤
  5. protocols,表示服务所使⽤的协议
  6. registries,表示服务所要注册的注册中⼼
    所以在扫描到⼀个@Service注解后,其实会得到两个Bean:
  7. ⼀个就是服务实现类本身⼀个Bean对象
  8. ⼀个就是对应的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

但是在⽣成这个代理对象之前,还要考虑问题:

  1. 当前所需要引⼊的这个服务,是不是在本地就存在?不存在则要把按Dubbo的逻辑⽣成⼀个代理对象
  2. 当前所需要引⼊的这个服务,是不是已经被引⼊过了(是不是已经⽣成过代理对象了),如果是应该是
    不⽤再重复去⽣成了。
    ⾸先如何判断当前所引⼊的服务是本地的⼀个服务(就是当前应⽤⾃⼰所提供的服务)。
    我们前⾯提到,Dubbo通过@Service来提供⼀个服务,并且会⽣成两个Bean:
  3. ⼀个服务实现类本身Bean
  4. ⼀个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中是这么做的:

  1. ⾸先根据@Reference注解的所有信息+属性接⼝类型⽣成⼀个字符串
  2. 然后@Reference注解的所有信息+属性接⼝类型⽣成⼀个ReferenceBean对象(ReferenceBean对
    象中的get⽅法可以得到⼀个Dubbo⽣成的代理对象,可以理解为服务引⼊的⼊⼝⽅法)
  3. 把字符串作为beanName,ReferenceBean对象作为bean注册到Spring容器中,同时也会放⼊
    referenceBeanCache中。
    有了这些逻辑,@Reference注解服务引⼊的过程是这样的:
  4. 得到当前所引⼊服务对应的ServiceBean的beanName(源码中叫referencedBeanName)
    12
  5. 根据@Reference注解的所有信息+属性接⼝类型得到⼀个referenceBeanName
  6. 根据referenceBeanName从referenceBeanCache获取对应的ReferenceBean,如果没有则创建⼀
    个ReferenceBean
  7. 根据referencedBeanName(ServiceBean的beanName)判断Spring容器中是否存在该bean,如果
    存在则给ref属性所对应的bean取⼀个别名,别名为referenceBeanName。
    a. 如果Spring容器中不存在referencedBeanName对应的bean,则判断容器中是否存在
    referenceBeanName所对应的Bean,如果不存在则将创建出来的ReferenceBean注册到Spring
    容器中(此处这么做就⽀持了可以通过@Autowired注解也可以使⽤服务了,ReferenceBean是
    ⼀个FactoryBean)
  8. 如果referencedBeanName存在对应的Bean,则额外⽣成⼀个代理对象,代理对象的
    InvocationHandler会缓存在localReferenceBeanInvocationHandlerCache中,这样如果引⼊的是
    同⼀个服务,并且这个服务在本地,
  9. 如果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, 在配置上有两个⽐较重要的注解

  1. @PropertySource表示将dubbo-provider.properties中的配置项添加到Spring容器中,可以通过
    @Value的⽅式获取到配置项中的值
  2. @EnableDubbo(scanBasePackages = “org.apache.dubbo.demo.provider”)表示对指定包下的类
    进⾏扫描,扫描@Service与@Reference注解,并且进⾏处理

@EnableDubbo

在EnableDubbo注解上,有另外两个注解,也是研究Dubbo最重要的两个注解

  1. @EnableDubboConfig
  2. @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注解所导⼊的类:

  1. DubboConfigConfigurationRegistrar
  2. DubboComponentScanRegistrar
    Spring在启动时会解析这两个注解,并且执⾏对应的Registrar类中的registerBeanDefinitions⽅法(这
    是Spring中提供的扩展功能。)

DubboConfigConfigurationRegistrar

流程

Spring启动时,会调⽤DubboConfigConfigurationRegistrar的registerBeanDefinitions⽅法,该⽅法

是利⽤Spring中的AnnotatedBeanDefinitionReader来读取:

  1. DubboConfigConfiguration.Single.class
  2. 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:

  1. ServiceAnnotationBeanPostProcessor
  2. 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服务,它有⼀些参数,⽐如:

  1. ref,表示服务的具体实现类
  2. interface,表示服务的接⼝
  3. parameters,表示服务的参数(@Service注解中所配置的信息)
  4. application,表示服务所属的应⽤
  5. protocols,表示服务所使⽤的协议
  6. registries,表示服务所要注册的注册中⼼
    所以在扫描到⼀个@Service注解后,其实会得到两个Bean:
  7. ⼀个就是服务实现类本身⼀个Bean对象
  8. ⼀个就是对应的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

但是在⽣成这个代理对象之前,还要考虑问题:

  1. 当前所需要引⼊的这个服务,是不是在本地就存在?不存在则要把按Dubbo的逻辑⽣成⼀个代理对象
  2. 当前所需要引⼊的这个服务,是不是已经被引⼊过了(是不是已经⽣成过代理对象了),如果是应该是
    不⽤再重复去⽣成了。
    ⾸先如何判断当前所引⼊的服务是本地的⼀个服务(就是当前应⽤⾃⼰所提供的服务)。
    我们前⾯提到,Dubbo通过@Service来提供⼀个服务,并且会⽣成两个Bean:
  3. ⼀个服务实现类本身Bean
  4. ⼀个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中是这么做的:

  1. ⾸先根据@Reference注解的所有信息+属性接⼝类型⽣成⼀个字符串
  2. 然后@Reference注解的所有信息+属性接⼝类型⽣成⼀个ReferenceBean对象(ReferenceBean对
    象中的get⽅法可以得到⼀个Dubbo⽣成的代理对象,可以理解为服务引⼊的⼊⼝⽅法)
  3. 把字符串作为beanName,ReferenceBean对象作为bean注册到Spring容器中,同时也会放⼊
    referenceBeanCache中。
    有了这些逻辑,@Reference注解服务引⼊的过程是这样的:
  4. 得到当前所引⼊服务对应的ServiceBean的beanName(源码中叫referencedBeanName)
    12
  5. 根据@Reference注解的所有信息+属性接⼝类型得到⼀个referenceBeanName
  6. 根据referenceBeanName从referenceBeanCache获取对应的ReferenceBean,如果没有则创建⼀
    个ReferenceBean
  7. 根据referencedBeanName(ServiceBean的beanName)判断Spring容器中是否存在该bean,如果
    存在则给ref属性所对应的bean取⼀个别名,别名为referenceBeanName。
    a. 如果Spring容器中不存在referencedBeanName对应的bean,则判断容器中是否存在
    referenceBeanName所对应的Bean,如果不存在则将创建出来的ReferenceBean注册到Spring
    容器中(此处这么做就⽀持了可以通过@Autowired注解也可以使⽤服务了,ReferenceBean是
    ⼀个FactoryBean)
  8. 如果referencedBeanName存在对应的Bean,则额外⽣成⼀个代理对象,代理对象的
    InvocationHandler会缓存在localReferenceBeanInvocationHandlerCache中,这样如果引⼊的是
    同⼀个服务,并且这个服务在本地,
  9. 如果referencedBeanName不存在对应的Bean,则直接调⽤ReferenceBean的get()⽅法得到⼀个代
    理对象
相关文章
|
27天前
|
缓存 Java 开发者
【Spring】原理:Bean的作用域与生命周期
本文将围绕 Spring Bean 的作用域与生命周期展开深度剖析,系统梳理作用域的类型与应用场景、生命周期的关键阶段与扩展点,并结合实际案例揭示其底层实现原理,为开发者提供从理论到实践的完整指导。
|
26天前
|
人工智能 Java 开发者
【Spring】原理解析:Spring Boot 自动配置
Spring Boot通过“约定优于配置”的设计理念,自动检测项目依赖并根据这些依赖自动装配相应的Bean,从而解放开发者从繁琐的配置工作中解脱出来,专注于业务逻辑实现。
|
2月前
|
Java 关系型数据库 数据库
深度剖析【Spring】事务:万字详解,彻底掌握传播机制与事务原理
在Java开发中,Spring框架通过事务管理机制,帮我们轻松实现了这种“承诺”。它不仅封装了底层复杂的事务控制逻辑(比如手动开启、提交、回滚事务),还提供了灵活的配置方式,让开发者能专注于业务逻辑,而不用纠结于事务细节。
|
6月前
|
存储 人工智能 自然语言处理
RAG 调优指南:Spring AI Alibaba 模块化 RAG 原理与使用
通过遵循以上最佳实践,可以构建一个高效、可靠的 RAG 系统,为用户提供准确和专业的回答。这些实践涵盖了从文档处理到系统配置的各个方面,能够帮助开发者构建更好的 RAG 应用。
2780 114
|
3月前
|
缓存 安全 Java
Spring 框架核心原理与实践解析
本文详解 Spring 框架核心知识,包括 IOC(容器管理对象)与 DI(容器注入依赖),以及通过注解(如 @Service、@Autowired)声明 Bean 和注入依赖的方式。阐述了 Bean 的线程安全(默认单例可能有安全问题,需业务避免共享状态或设为 prototype)、作用域(@Scope 注解,常用 singleton、prototype 等)及完整生命周期(实例化、依赖注入、初始化、销毁等步骤)。 解析了循环依赖的解决机制(三级缓存)、AOP 的概念(公共逻辑抽为切面)、底层动态代理(JDK 与 Cglib 的区别)及项目应用(如日志记录)。介绍了事务的实现(基于 AOP
121 0
|
3月前
|
监控 架构师 NoSQL
spring 状态机 的使用 + 原理 + 源码学习 (图解+秒懂+史上最全)
spring 状态机 的使用 + 原理 + 源码学习 (图解+秒懂+史上最全)
|
5月前
|
前端开发 Java 数据库连接
Spring核心原理剖析与解说
每个部分都是将一种巨大并且复杂的技术理念传达为更易于使用的接口,而这就是Spring的价值所在,它能让你专注于开发你的应用,而不必从头开始设计每一部分。
174 32
|
5月前
|
Java 开发者 Spring
Spring框架 - 深度揭秘Spring框架的基础架构与工作原理
所以,当你进入这个Spring的世界,看似一片混乱,但细看之下,你会发现这里有个牢固的结构支撑,一切皆有可能。不论你要建设的是一座宏大的城堡,还是个小巧的花园,只要你的工具箱里有Spring,你就能轻松搞定。
210 9
|
6月前
|
安全 前端开发 Java
Spring Boot 项目中触发 Circular View Path 错误的原理与解决方案
在Spring Boot开发中,**Circular View Path**错误常因视图解析与Controller路径重名引发。当视图名称(如`login`)与请求路径相同,Spring MVC无法区分,导致无限循环调用。解决方法包括:1) 明确指定视图路径,避免重名;2) 将视图文件移至子目录;3) 确保Spring Security配置与Controller路径一致。通过合理设定视图和路径,可有效避免该问题,确保系统稳定运行。
382 0
|
9月前
|
监控 Java 应用服务中间件
SpringBoot是如何简化Spring开发的,以及SpringBoot的特性以及源码分析
Spring Boot 通过简化配置、自动配置和嵌入式服务器等特性,大大简化了 Spring 应用的开发过程。它通过提供一系列 `starter` 依赖和开箱即用的默认配置,使开发者能够更专注于业务逻辑而非繁琐的配置。Spring Boot 的自动配置机制和强大的 Actuator 功能进一步提升了开发效率和应用的可维护性。通过对其源码的分析,可以更深入地理解其内部工作机制,从而更好地利用其特性进行开发。
290 6