本次的文章的版本都是基于 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的作用原理在下篇文章中说明
这个注解上面也有@EnableDubboConfig和@DubboComponentScan两个注解。
Spring已经提供了注解的注解功能, 可以实现类似的注解的继承的功能。 比如springboot的著名的@SpringBootApplication注解。 所以实际是EnableDubboConfig和DubboComponentScan两个注解真正起作用。
可以看到@Import 注解,@Import又是一个spring很重要的注解,并且必须和Configuration注解配置和使用,才会生效 ,所以可以推导知道EnableDubbo注解也必须要和Configuration注解一起配合使用。
此处勘误, 经过我自己测试发现, 配合@Component注解一起使用也可以。
@Import注解可以实现导入第三方的包的bean到容器的功能,配合注解Configuration一起使用, 可以实现一个注解就可以注入第三方bean的能力,也就是EnableDubbo这一个注解可以标识dubbo启动与否的原理。
可以看到图中的@Import注解的参数DubboConfigConfigurationRegistrar类。
/**
* 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类放入容器中。
可以看到最后使用的还是使用了EnableConfigurationBeanBinding注解,这个注解是Spring 自带的
再往深层追溯, ConfigurationBeanBindingRegistrar类,代码太多了,只截图重点方法。
从源码中可以看到,会把EnableConfigurationBeanBinding注解中传入的type参数作为BeanDefinition放入容器, 同时还会把配置中以prefix参数值作为配置的key的前缀的value注入到这个Bean中。 这个至于怎么把配置注入bean中, 我还没有明白, 需要后面查找。
到这一步就把dubbo相关的config对象全部注入到容器中了。 同时也把一些配置信息注入到正确的config了
上面只是弄明白了dubbo的一些配置对象如何注入容器中, 但是关于Dubbo的一些Service是如何注入到容器的。 我们看下DubboComponentScan注解的源码。
/**
* 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类在我的上一篇文章中有讲解
https://blog.csdn.net/leisurelen/article/details/106993692
这个类的作用就是扫描传入的路径, 并且扫描路径中所有类的注解, 如果有DubboService 和Service 注解就会生成对应的Beandefinition放入容器中。
理解这个类有一个前置知识必须了解, 就是ClassPathBeanDefinitionScanner
类, 这个是Spring的类,用来扫描项目中特定的注解的类,并把这个类放入容器中, Dubbo就是使用这个类的功能来扫描项目中的DubboService注解并生成BeanDefinition放入容器中的。
具体流程:
org.apache.dubbo.config.spring.context.annotation.DubboClassPathBeanDefinitionScanner
org.apache.dubbo.config.spring.beans.factory.annotation.ServiceClassPostProcessor#buildServiceBeanDefinition
在这里, 我就不贴出来了。到这里dubbo的Servcie的注入原理就清晰了,实际上本质还是为每个类生成了ServiceConfig 对象。
还有一个疑问就是Reference 注解是如何起作用的,这个会留在下个文章中说明。
对于dubbo的xml的整合spring的方式有疑问的可以参考