Fork me on GitHub

duubo注解整合spring原理

dubbo整合Spring使用的注解

对于一个Provider的启动类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Application {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ProviderConfiguration.class);
context.start();
System.in.read();
}

@Configuration
@EnableDubbo(scanBasePackages = "org.apache.dubbo.demo.provider")
@PropertySource("classpath:/spring/dubbo-provider.properties")
static class ProviderConfiguration {
@Bean
public RegistryConfig registryConfig() {
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setAddress("zookeeper://127.0.0.1:2181");
return registryConfig;
}
}
}

可以看到在配置类中加入了@EnableDubbo注解,还利用Spring的@PropertySource(path)导入了provider的配置,后者其实就是把配置文件的properties属性导入到Spring的Environment中,方便使用。

@EnableDubbo肯定也是采用了@Enable + @Import模式导入了BeanDefinition来实现Dubbo和Spring的整合。

暴露一个Dubbo接口使用@Service注解,引用一个Dubbo服务使用
Dubbo@Reference注解。

Dubbo和Spring整合要做的事情

总结一下Dubbo和Spring整合要做的一些事情:

  • 把配置properties文件实例化为一个个bean来管理,方便注入到ServiceBean(dubbo暴露的服务bean)和ReferenceBean(@Reference注解引用的bean)。这里可以看下ProviderConfig、ApplicationConfig等配置类,内部的变量属性就是配置文件中的key。
  • 扫描应用中使用@Service注解的服务,生成其对应的ServiceBean,表示其为一个dubbo服务。然后在一定的时机(比如收到上下文刷新的事件之后)触发此接口的export导出逻辑。export是dubbo内部的逻辑,在Spring整合Dubbo这块不纠结。
  • 扫描@Reference注解的属性或者方法,注入引用的Dubbo服务代理对象。这里要完成对dubbo对象的属性注入,且完成其需要的代理逻辑,然后调用dubbo内部的refer()方法。

整体流程和原理

image-20220701063232019

  • @Service的处理在Spring扩展点:BeanDefinitionRegistryPostProcessor.postProcessBeanDefinitionRegistry。
  • @Reference的处理在Spring扩展点:InstantiationAwareBeanPostProcessorAdapter.postProcessPropertyValues。
  • @Service注解会先注册Spring bean(和dubbo无关),比如DemoServiceImpl,这个就是最普通的bean。还会生成Dubbo标识暴露服务的ServiceBean。
  • 在Spring启动完成之后,会通过事件或者回调函数来完成export即dubbo接口导出的工作。
  • @Reference的解析会在Spring容器中存放ReferenceBean,如果是本地的Dubbo服务,会直接将ServiceBean作为ReferenceBean,且代理逻辑会直接调用类的方法;而如果是远程的Dubbo服务,会调用get()方法内部走代理逻辑,其中也会走refer()方法。

@EnableDubbo注解

1
2
3
4
5
6
@EnableDubboConfig
@DubboComponentScan
// @EnableDubbo注解来标注在spring boot应用上 来开启dubbo接口暴露、引用(dubbo的@Service、@Reference注解的扫描)。还有dubbo config配置初始化为bean的过程
// 主要功能在@EnableDubboConfig、@EnableComponentScan
public @interface EnableDubbo {
}

主要是由内部两个注解完成。

@EnableDubboConfig

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Import(DubboConfigConfigurationRegistrar.class)
// 解析Dubbo 配置 (properties文件)的支持 导入了DubboConfigConfigurationRegistrar
public @interface EnableDubboConfig {

/**
* It indicates whether binding to multiple Spring Beans.
*
* @return the default value is <code>false</code>
* @revised 2.5.9
*/
boolean multiple() default true;

}

可以看到了Import了DubboConfigConfigurationRegistrar这个bean定义的注册器,这个注册器其中主要注册了两个DubboConfigConfiguration内部类的Bean定义,这两个Bean上的又注解了@EnableConfigurationBeanBindings。

@EnableConfigurationBeanBindings注解import了ConfigurationBeanBindingPostProcessor,这个bean定义注册器中:

  • 开启import各个具体的配置bean 解析内部的每一个BeanBinding 生成对应的configBean。
  • 注册一个后置处理器(ConfigurationBeanBindingPostProcessor),去利用Spring的DataBinder去将properties中的配置值映射到对应的configBean中。 比如处理ApplicationConfig对象中的所有属性字段,值从properties文件中获取。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class DubboConfigConfigurationRegistrar implements ImportBeanDefinitionRegistrar {

@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

// 获取EnableDubboConfig注解信息
AnnotationAttributes attributes = AnnotationAttributes.fromMap(
importingClassMetadata.getAnnotationAttributes(EnableDubboConfig.class.getName()));

// multiple的配置值 multiple就是 dubbo.applications(复数)这种多个配置
boolean multiple = attributes.getBoolean("multiple");

// Single Config Bindings
// 注册Single配置Bean定义 内部主要是@EnableConfigurationBeanBindings注解再次import了具体配置bean定义的注册(ConfigurationBeanBindingsRegister)—
// ,并且注册了对应的后置处理器去解析具体配置值
registerBeans(registry, DubboConfigConfiguration.Single.class);

if (multiple) { // Since 2.6.6 https://github.com/apache/dubbo/issues/3193
registerBeans(registry, DubboConfigConfiguration.Multiple.class);
}

// Since 2.7.6
registerCommonBeans(registry);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class ConfigurationBeanBindingsRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware {

private ConfigurableEnvironment environment;

@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

AnnotationAttributes attributes = AnnotationAttributes.fromMap(
importingClassMetadata.getAnnotationAttributes(EnableConfigurationBeanBindings.class.getName()));

AnnotationAttributes[] annotationAttributes = attributes.getAnnotationArray("value");

ConfigurationBeanBindingRegistrar registrar = new ConfigurationBeanBindingRegistrar();

registrar.setEnvironment(environment);

for (AnnotationAttributes element : annotationAttributes) {
// 对定义好的每个binding(其实就是properties文件中的属性)去注册对应的Bean定义 且注册一个bean后置处理器(ConfigurationBeanBindingPostProcessor)去绑定配置中的值(值来源于properties文件解析之后放入的environment中)
registrar.registerConfigurationBeanDefinitions(element, registry);
}
}

@DubboComponentScan

可以看到这个注解就是处理@Service和@Reference注解的扫描生成对应的Bean。
注解import了DubboComponentScanRegistrar这个注册器(ImportBeanDefinitionRegistrar的实现)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// DubboComponentScanRegistrar dubbo扫描@Service和@Reference注解 解析
public class DubboComponentScanRegistrar implements ImportBeanDefinitionRegistrar {

@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

// 获取要扫描的包路径
Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);

// 注册路径下处理@Service注解的Bean后置处理器
registerServiceAnnotationBeanPostProcessor(packagesToScan, registry);

// @since 2.7.6 Register the common beans
registerCommonBeans(registry);
}

可以看到注册了ServiceAnnotationBeanPostProcessor和ReferenceServiceAnnotationBeanPostProcessor两个bean的后置处理器。

ServiceAnnotationBeanPostProcessor

这个后置处理器是实现的Spring的扩展点:BeanDefinitionRegistryPostProcessor 。 会调用其postProcessBeanDefinitionRegistry方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//此方法中处理 dubbo @Service注解
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {

// @since 2.7.5
// 注册DubboBootstrapApplicationListener 监听事件之后启动Dubbo
registerBeans(registry, DubboBootstrapApplicationListener.class);

// 注解上解析出需要扫描的路径
Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);

if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
// 去注册ServiceBean
registerServiceBeans(resolvedPackagesToScan, registry);
} else {
if (logger.isWarnEnabled()) {
logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
}
}

}

注册ServiceBean的方法registerServiceBeans如下:

    1. 扫描DubboClass路径,将实现类本身的Bean注册到容器中。
    1. 为@Service注解标注的Dubbo服务实现类去往容器中注册一个ServiceBean。
    1. Bean生成之后,监听上下文刷新事件来触发ServiceBean去真正的导出服务。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      /**
      * Registers Beans whose classes was annotated {@link Service}
      *
      * @param packagesToScan The base packages to scan
      * @param registry {@link BeanDefinitionRegistry}
      */
      private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {

      // 创建一个dubbo类路径扫描器 去扫描
      DubboClassPathBeanDefinitionScanner scanner =
      new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);

      BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);

      scanner.setBeanNameGenerator(beanNameGenerator);

      // refactor @since 2.7.7
      serviceAnnotationTypes.forEach(annotationType -> {
      // 要扫描的@Service注解 兼容版本
      scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType));
      });

      for (String packageToScan : packagesToScan) {

      // Registers @Service Bean first
      // scan去扫描路径下的 @Service注解Bean
      // 1. 先注册类本身的bean到Spring容器中 scanner内部扫描到会注册bean定义
      scanner.scan(packageToScan);

      // Finds all BeanDefinitionHolders of @Service whether @ComponentScan scans or not.
      Set<BeanDefinitionHolder> beanDefinitionHolders =
      findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);

      if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {

      for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
      // 2. 再去去注册每个Dubbo服务接口的ServiceBean 内部会构建ServiceBean定义并注册在Spring容器中
      registerServiceBean(beanDefinitionHolder, registry, scanner);
      }

      if (logger.isInfoEnabled()) {
      logger.info(beanDefinitionHolders.size() + " annotated Dubbo's @Service Components { " +
      beanDefinitionHolders +
      " } were scanned under package[" + packageToScan + "]");
      }

      } else {

      if (logger.isWarnEnabled()) {
      logger.warn("No Spring Bean annotating Dubbo's @Service was found under package["
      + packageToScan + "]");
      }

      }

      }

      }

ReferenceServiceAnnotationBeanPostProcessor

ReferenceServiceAnnotationBeanPostProcessor是继承的Spring的扩展点后置处理器InstantiationAwareBeanPostProcessorAdapter,实现了其postProcessPropertyValues方法来为@Reference注解标注的字段来注入对应的ReferenceBean。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {

InjectionMetadata metadata = findInjectionMetadata(beanName, bean.getClass(), pvs);
try {
// 也是借助metadata.inject来实现的注入字段
metadata.inject(bean, beanName, pvs);
} catch (BeanCreationException ex) {
throw ex;
} catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of @" + getAnnotationType().getSimpleName()
+ " dependencies is failed", ex);
}
return pvs;
}

这里inject会走到AnnotatedMethodElement.inject方法,内部会调用子类实现的doGetInjectedBean方法。Dubbo在ReferenceAnnotationBeanPostProcessor中实现了这个方法doGetInjectedBean。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Override
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {

Class<?> injectedType = pd.getPropertyType();

Object injectedObject = getInjectedObject(attributes, bean, beanName, injectedType, this);

ReflectionUtils.makeAccessible(method);

method.invoke(bean, injectedObject);

}

// getInjectedObject方法
protected Object getInjectedObject(AnnotationAttributes attributes, Object bean, String beanName, Class<?> injectedType,
InjectionMetadata.InjectedElement injectedElement) throws Exception {

String cacheKey = buildInjectedObjectCacheKey(attributes, bean, beanName, injectedType, injectedElement);

Object injectedObject = injectedObjectsCache.get(cacheKey);

if (injectedObject == null) {
// doGetInjectedBean 方法 dubbo实现了ReferenceBean注入的关键
injectedObject = doGetInjectedBean(attributes, bean, beanName, injectedType, injectedElement);
// Customized inject-object if necessary
injectedObjectsCache.putIfAbsent(cacheKey, injectedObject);
}

return injectedObject;

}

doGetInjectedBean方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/**
* 实现的doGetInjectedBean方法来解析@DubboReference字段 来注入dubbo服务(最终是ReferenceBean的代理对象)
* @param attributes
* @param bean
* @param beanName
* @param injectedType
* @param injectedElement
* @return
* @throws Exception
*/
@Override
protected Object doGetInjectedBean(AnnotationAttributes attributes, Object bean, String beanName, Class<?> injectedType,
InjectionMetadata.InjectedElement injectedElement) throws Exception {
/**
* The name of bean that annotated Dubbo's {@link Service @Service} in local Spring {@link ApplicationContext}
* 先在本地的Spring上下文中查找ServiceBean是否存在
* ServiceBean: com.xxx.DemoService:group:version
*/
String referencedBeanName = buildReferencedBeanName(attributes, injectedType);

/**
* The name of bean that is declared by {@link Reference @Reference} annotation injection
* 根据@Reference注解的属性和注入的类属性 来生成referenceBean的名称
*/
String referenceBeanName = getReferenceBeanName(attributes, injectedType);

// 从缓存中取ReferenceBean 如果不存在 会去创建(内部的配置也会注入)
ReferenceBean referenceBean = buildReferenceBeanIfAbsent(referenceBeanName, attributes, injectedType);

// 是否是本地的bean 上面的ServiceBeanName来在容器中寻找
boolean localServiceBean = isLocalServiceBean(referencedBeanName, referenceBean, attributes);

// 容器中注册referenceBean
registerReferenceBean(referencedBeanName, referenceBean, attributes, localServiceBean, injectedType);

cacheInjectedReferenceBean(referenceBean, injectedElement);

// 最终返回注入给@Reference变量是 ReferenceBean的代理对象 是个FactoryBean 代理逻辑在其get()方法中(远程bean)
return getOrCreateProxy(referencedBeanName, referenceBean, localServiceBean, injectedType);
}

因为客户端去引用Dubbo远程服务bean时要屏蔽一些细节,让引用的dubbo bean具有调用远程接口的能力,所以这里为ReferenceBean去生成代理,走的是getOrCreateProxy方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
private Object getOrCreateProxy(String referencedBeanName, ReferenceBean referenceBean, boolean localServiceBean,
Class<?> serviceInterfaceType) {
if (localServiceBean) { // If the local @Service Bean exists, build a proxy of Service
// 本地的dubbo服务 创建jdk动态代理 内部直接调用ServiceBean的ref的方法
return newProxyInstance(getClassLoader(), new Class[]{serviceInterfaceType},
newReferencedBeanInvocationHandler(referencedBeanName));
} else {
// 如果需要export 帮助依赖的ServiceBean 去 export
exportServiceBeanIfNecessary(referencedBeanName); // If the referenced ServiceBean exits, export it immediately
// get方法创建代理对象
return referenceBean.get();
}
}

这里是直接调用referenceBean.get()方法去生成的代理对象。

在Spring容器中存放的是ReferenceBean对象,但本身这个Bean也是实现FactoryBean接口的,所以会调用其getObject()方法来取出真正的代理之后的bean。其实getObejct方法也是调用自身的get()方法来走代理逻辑的:

1
2
3
4
5
6
7
getObject()方法:

@Override
public Object getObject() {
// 调用get生成代理对象
return get();
}

-------------本文结束感谢您的阅读-------------

本文标题:duubo注解整合spring原理

文章作者:夸克

发布时间:2020年06月01日 - 06:06

最后更新:2022年07月01日 - 06:07

原始链接:https://zhanglijun1217.github.io/2020/06/01/duubo注解整合spring原理/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。