本次的文章的版本都是基于 2.7.7 版本的。 2.7.7提供了@EnableDubbo注解来用于和spring整合。
对于Import注解的源码的讲解,推荐看下这篇博客, 讲的十分清楚明白: https://blog.csdn.net/boling_cavalry/article/details/82530167
从Spring中 Dubbo的使用上面来看,有两个重要的注解,即@DubboService 和 @DubboReference。
对两个注解的解析分别在 ServiceAnnotationBeanPostProcessor和ReferenceAnnotationBeanPostProcessor。 本文章会从Dubbo的注解启动开始追溯,最后到ServiceAnnotationBeanPostProcessor的作用原理解析, 关于ReferenceAnnotationBeanPostProcessor的作用原理在下篇文章中说明
@EnableDubbo注解
这个注解上面也有@EnableDubboConfig和@DubboComponentScan两个注解。
Spring已经提供了注解的注解功能, 可以实现类似的注解的继承的功能。 比如springboot的著名的@SpringBootApplication注解。 所以实际是EnableDubboConfig和DubboComponentScan两个注解真正起作用。
@EnableDubboConfig注解
可以看到@Import 注解,@Import又是一个spring很重要的注解,并且必须和Configuration注解配置和使用,才会生效 ,所以可以推导知道EnableDubbo注解也必须要和Configuration注解一起配合使用。
此处勘误, 经过我自己测试发现, 配合@Component注解一起使用也可以。
@Import注解可以实现导入第三方的包的bean到容器的功能,配合注解Configuration一起使用, 可以实现一个注解就可以注入第三方bean的能力,也就是EnableDubbo这一个注解可以标识dubbo启动与否的原理。
可以看到图中的@Import注解的参数DubboConfigConfigurationRegistrar类。
DubboConfigConfigurationRegistrar 类
图中方法名可以看到有三个方法都注入bean到容器中了
registerBeans(registry, DubboConfigConfiguration.Single.class);
/**
* Register Beans if not present in {@link BeanDefinitionRegistry registry}
*
* @param registry {@link BeanDefinitionRegistry}
* @param annotatedClasses {@link Annotation annotation} class
*/
public static void registerBeans(BeanDefinitionRegistry registry, Class<?>... annotatedClasses) {
if (ObjectUtils.isEmpty(annotatedClasses)) {
return;
}
Set<Class<?>> classesToRegister = new LinkedHashSet<Class<?>>(asList(annotatedClasses));
// Remove all annotated-classes that have been registered
Iterator<Class<?>> iterator = classesToRegister.iterator();
while (iterator.hasNext()) {
Class<?> annotatedClass = iterator.next();
if (isPresentBean(registry, annotatedClass)) {
iterator.remove();
}
}
AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(registry);
if (logger.isDebugEnabled()) {
logger.debug(registry.getClass().getSimpleName() + " will register annotated classes : " + asList(annotatedClasses) + " .");
}
//这里就会包传入的Class参数d对应的类加入到容器中
reader.register(classesToRegister.toArray(EMPTY_CLASS_ARRAY));
}
源码中会使用AnnotatedBeanDefinitionReader 将传入的DubboConfigConfiguration.Single类放入容器中。
DubboConfigConfiguration.Single类
可以看到最后使用的还是使用了EnableConfigurationBeanBinding注解,这个注解是Spring 自带的
再往深层追溯, ConfigurationBeanBindingRegistrar类,代码太多了,只截图重点方法。
从源码中可以看到,会把EnableConfigurationBeanBinding注解中传入的type参数作为BeanDefinition放入容器, 同时还会把配置中以prefix参数值作为配置的key的前缀的value注入到这个Bean中。 这个至于怎么把配置注入bean中, 我还没有明白, 需要后面查找。
到这一步就把dubbo相关的config对象全部注入到容器中了。
同时也把一些配置信息注入到正确的config了
@DubboComponentScan
上面只是弄明白了dubbo的一些配置对象如何注入容器中, 但是关于Dubbo的一些Service是如何注入到容器的。 我们看下DubboComponentScan注解的源码。
看下DubboComponentScanRegistrar类
/**
* Dubbo {@link DubboComponentScan} Bean Registrar
*
* @see Service
* @see DubboComponentScan
* @see ImportBeanDefinitionRegistrar
* @see ServiceAnnotationBeanPostProcessor
* @see ReferenceAnnotationBeanPostProcessor
* @since 2.5.7
*/
public class DubboComponentScanRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//看方法名猜到是获取包路径 , 进入下面的源码中看下
Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
registerServiceAnnotationBeanPostProcessor(packagesToScan, registry);
// @since 2.7.6 Register the common beans
registerCommonBeans(registry);
}
/**
* Registers {@link ServiceAnnotationBeanPostProcessor}
*
* @param packagesToScan packages to scan without resolving placeholders
* @param registry {@link BeanDefinitionRegistry}
* @since 2.5.8
*/
private void registerServiceAnnotationBeanPostProcessor(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
//先放入ServiceAnnotationBeanPostProcessor类的bean到容器中。 并且把路径作为参数传入这个类
BeanDefinitionBuilder builder = rootBeanDefinition(ServiceAnnotationBeanPostProcessor.class);
builder.addConstructorArgValue(packagesToScan);
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry);
}
/**
* 获取注解中配置的需要扫描的包的路径信息
*/
private Set<String> getPackagesToScan(AnnotationMetadata metadata) {
//获取注解中的所有属性
AnnotationAttributes attributes = AnnotationAttributes.fromMap(
metadata.getAnnotationAttributes(DubboComponentScan.class.getName()));
//获取配置的路径信息
String[] basePackages = attributes.getStringArray("basePackages");
Class<?>[] basePackageClasses = attributes.getClassArray("basePackageClasses");
String[] value = attributes.getStringArray("value");
// Appends value array attributes
Set<String> packagesToScan = new LinkedHashSet<String>(Arrays.asList(value));
packagesToScan.addAll(Arrays.asList(basePackages));
for (Class<?> basePackageClass : basePackageClasses) {
packagesToScan.add(ClassUtils.getPackageName(basePackageClass));
}
if (packagesToScan.isEmpty()) {
return Collections.singleton(ClassUtils.getPackageName(metadata.getClassName()));
}
return packagesToScan;
}
}
ServiceAnnotationBeanPostProcessor类
ServiceAnnotationBeanPostProcessor类在我的上一篇文章中有讲解
https://blog.csdn.net/leisurelen/article/details/106993692
这个类的作用就是扫描传入的路径, 并且扫描路径中所有类的注解, 如果有DubboService 和Service 注解就会生成对应的Beandefinition放入容器中。
理解这个类有一个前置知识必须了解, 就是ClassPathBeanDefinitionScanner类, 这个是Spring的类,用来扫描项目中特定的注解的类,并把这个类放入容器中, Dubbo就是使用这个类的功能来扫描项目中的DubboService注解并生成BeanDefinition放入容器中的。
具体流程:
1.生成事件监听器类DubboBootstrapApplicationListener 的BeanDefinition放入容器中,这个是 @since 2.7.5版本新增的
2.解析处理下传入的路径
3.生成DubboClassPathBeanDefinitionScanner类来扫描路径下的类。这个类继承自ClassPathBeanDefinitionScanner类,dubbo没做很大修改,只是传入environment 和resourceLoader 两个对象, 同时在scanner中 增加了两个注解过滤器DubboService和Service, 并且禁用了spring的默认过滤器,也就是Spring的bean不会被扫描到。下图就是传入参数禁用useDefaultFilters。
org.apache.dubbo.config.spring.context.annotation.DubboClassPathBeanDefinitionScanner
4.扫描路径下的类,这时候scanner就会把DubboService和Service注解的类扫描到容器中了, spring的正常类不会扫描进去
5.获取到扫描的所有放入容器中的BeanDefinintion. 遍历每个BeanDefinintion并获取这个bean的第一个接口 , 并且为每一个类生成一个ServiceBean的BeanDefintion放入容器, 并且把DubboService的注解的一些信息注入ServiceBean中,第一个接口 也作为属性注入进去了, ServiceBean 是继承自ServiceConfig类的。
生成ServiceBean的方法源码在org.apache.dubbo.config.spring.beans.factory.annotation.ServiceClassPostProcessor#buildServiceBeanDefinition 在这里, 我就不贴出来了。
到这里dubbo的Servcie的注入原理就清晰了,
实际上本质还是为每个类生成了ServiceConfig 对象。
还有一个疑问就是Reference 注解是如何起作用的,这个会留在下个文章中说明。
对于dubbo的xml的整合spring的方式有疑问的可以参考
声明:
转载原文:https://blog.csdn.net/leisurelen/article/details/107019516
版权声明:本文为原文博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。转载仅用于学习记录,如有侵权请联系删除。