dubbo整合Spring使用的注解
对于一个Provider的启动类:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19public class Application {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ProviderConfiguration.class);
context.start();
System.in.read();
}
"org.apache.dubbo.demo.provider") (scanBasePackages =
"classpath:/spring/dubbo-provider.properties") (
static class ProviderConfiguration {
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()方法。
整体流程和原理
- @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 |
|
主要是由内部两个注解完成。
@EnableDubboConfig
1 | ({ElementType.TYPE}) |
可以看到了Import了DubboConfigConfigurationRegistrar这个bean定义的注册器,这个注册器其中主要注册了两个DubboConfigConfiguration内部类的Bean定义,这两个Bean上的又注解了@EnableConfigurationBeanBindings。
@EnableConfigurationBeanBindings注解import了ConfigurationBeanBindingPostProcessor,这个bean定义注册器中:
- 开启import各个具体的配置bean 解析内部的每一个BeanBinding 生成对应的configBean。
- 注册一个后置处理器(ConfigurationBeanBindingPostProcessor),去利用Spring的DataBinder去将properties中的配置值映射到对应的configBean中。 比如处理ApplicationConfig对象中的所有属性字段,值从properties文件中获取。
1 | public class DubboConfigConfigurationRegistrar implements ImportBeanDefinitionRegistrar { |
1 | public class ConfigurationBeanBindingsRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware { |
@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 {
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注解
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如下:
- 扫描DubboClass路径,将实现类本身的Bean注册到容器中。
- 为@Service注解标注的Dubbo服务实现类去往容器中注册一个ServiceBean。
- 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 + "]");
}
}
}
}
- Bean生成之后,监听上下文刷新事件来触发ServiceBean去真正的导出服务。
ReferenceServiceAnnotationBeanPostProcessor
ReferenceServiceAnnotationBeanPostProcessor是继承的Spring的扩展点后置处理器InstantiationAwareBeanPostProcessorAdapter,实现了其postProcessPropertyValues方法来为@Reference注解标注的字段来注入对应的ReferenceBean。
1 |
|
这里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
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
*/
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
13private 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
7getObject()方法:
public Object getObject() {
// 调用get生成代理对象
return get();
}