dubbo集成springboot的原理
1、@DubboComponentScan
- 一般使用springboot集成dubbo的时候会使用类似的注解
@DubboComponentScan("com..**.service"),首先,看看@DubboComponentScan,它被@Import所注解,并引入了DubboComponentScanRegistrar.class
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(DubboComponentScanRegistrar.class)
public @interface DubboComponentScan {
/**
* Alias for the {@link #basePackages()} attribute. Allows for more concise annotation
* declarations e.g.: {@code @DubboComponentScan("org.my.pkg")} instead of
* {@code @DubboComponentScan(basePackages="org.my.pkg")}.
*
* @return the base packages to scan
*/
String[] value() default {};
/**
* Base packages to scan for annotated @Service classes. {@link #value()} is an
* alias for (and mutually exclusive with) this attribute.
* <p>
* Use {@link #basePackageClasses()} for a type-safe alternative to String-based
* package names.
*
* @return the base packages to scan
*/
String[] basePackages() default {};
/**
* Type-safe alternative to {@link #basePackages()} for specifying the packages to
* scan for annotated @Service classes. The package of each class specified will be
* scanned.
*
* @return classes from the base packages to scan
*/
Class<?>[] basePackageClasses() default {}; 支线,
}
2、DubboComponentScanRegistrar.class
创建一个例子
@Service(token = "shihy")
public class MainWorkForeignServiceImpl implements MainWorkForeignService {
}
来看看DubboComponentScanRegistrar.class类,如下面的代码清单,该类实现了ImportBeanDefinitionRegistrar接口,该接口的作用就是在spring容器启动的时候,在ConfigurationClassPostProcessor中对@Import进行解析的时候,会执行它的registerBeanDefinitions方法直接向容器中注册BeanDefinition
public class DubboComponentScanRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//获取@DubboComponentScan("com.ay.**.service")注解中的值,即要被扫描的包的路径
Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
//向容器中注册ServiceAnnotationBeanPostProcessor.class的BeanDefinition,后续扫描@Service注解的类,创建对应的 ServiceBean 对象
registerServiceAnnotationBeanPostProcessor(packagesToScan, registry);
//向容器中注册ReferenceAnnotationBeanPostProcessor.class的BeanDefinition,后续扫描@Reference注解的类,创建对应的 ReferenceBean 对象
registerReferenceAnnotationBeanPostProcessor(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) {
BeanDefinitionBuilder builder = rootBeanDefinition(ServiceAnnotationBeanPostProcessor.class);
builder.addConstructorArgValue(packagesToScan);
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry);
}
/**
* Registers {@link ReferenceAnnotationBeanPostProcessor} into {@link BeanFactory}
*
* @param registry {@link BeanDefinitionRegistry}
*/
private void registerReferenceAnnotationBeanPostProcessor(BeanDefinitionRegistry registry) {
// Register @Reference Annotation Bean Processor
registerInfrastructureBean(registry,
ReferenceAnnotationBeanPostProcessor.BEAN_NAME, ReferenceAnnotationBeanPostProcessor.class);
}
private Set<String> getPackagesToScan(AnnotationMetadata metadata) {
//获得 @DubboComponentScan 注解
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
// 情况一,将属性添加到 packagesToScan 集合中
Set<String> packagesToScan = new LinkedHashSet<String>(Arrays.asList(value));
packagesToScan.addAll(Arrays.asList(basePackages));
for (Class<?> basePackageClass : basePackageClasses) {
packagesToScan.add(ClassUtils.getPackageName(basePackageClass));
}
// 情况二,如果 packagesToScan 为空,则默认使用该注解的类所在的包
if (packagesToScan.isEmpty()) {
return Collections.singleton(ClassUtils.getPackageName(metadata.getClassName()));
}
return packagesToScan;
}
}
3、ServiceAnnotationBeanPostProcessor.class
看看ServiceAnnotationBeanPostProcessor.class这个BeanDefinitionRegistryPostProcessor接口的子类。它的postProcessBeanDefinitionRegistry方法会在spring容器启动的时候被执行。
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// @since 2.7.5
registerBeans(registry, DubboBootstrapApplicationListener.class);
//解析 packagesToScan 集合。因为,可能存在占位符
Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);
//扫描 packagesToScan 包,创建对应的 Spring BeanDefinition 对象,从而创建 Dubbo ServiceBean 对象。
if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
//触发ServiceBean定义和注入
registerServiceBeans(resolvedPackagesToScan, registry);
} else {
if (logger.isWarnEnabled()) {
logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
}
}
}
/**
* 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) {
//创建 DubboClassPathBeanDefinitionScanner 对象
DubboClassPathBeanDefinitionScanner scanner =
new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
//获得 BeanNameGenerator 对象,并设置 beanNameGenerator 到 scanner 中
BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);
scanner.setBeanNameGenerator(beanNameGenerator);
//设置过滤器,用于获得带有 @Service 注解的类
scanner.addIncludeFilter(new AnnotationTypeFilter(Service.class));
/**
* Add the compatibility for legacy Dubbo's @Service
*
* The issue : https://github.com/apache/dubbo/issues/4330
* @since 2.7.3
*/
//为了兼容,也识别alibaba jar包中的Service注解
scanner.addIncludeFilter(new AnnotationTypeFilter(com.alibaba.dubbo.config.annotation.Service.class));
for (String packageToScan : packagesToScan) {
// Registers @Service Bean first
//执行扫描,这里只是将标注有dubbo的@Service注解的类的BeanDefinition注册到容器中,这样的目的可能只是
//想让@Service类作为一个普通的bean被本地代码所引用。
scanner.scan(packageToScan);
// Finds all BeanDefinitionHolders of @Service whether @ComponentScan scans or not.
//再次执行扫描,不管上一步的扫描,创建每个在 packageToScan 扫描到的类,对应的 BeanDefinitionHolder 对象,返回 BeanDefinitionHolder 集合,用于生成ServiceBean的BeanDefinition
Set<BeanDefinitionHolder> beanDefinitionHolders =
findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);
if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
//注册ServiceBean的BeanDefinition并做数据绑定和解析
registerServiceBean(beanDefinitionHolder, registry, scanner);
}
if (logger.isInfoEnabled()) {
.................
}
} else {
...................
}
}
}
/**
* Registers {@link ServiceBean} from new annotated {@link Service} {@link BeanDefinition}
*
* @param beanDefinitionHolder
* @param registry
* @param scanner
* @see ServiceBean
* @see BeanDefinition
*/
private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry,
DubboClassPathBeanDefinitionScanner scanner) {
//获取beanDefinitionHolder所对应的class对象
Class<?> beanClass = resolveClass(beanDefinitionHolder);
//获得 @Service 注解的对象,是个代理对象
Annotation service = findServiceAnnotation(beanClass);
/**
* The {@link AnnotationAttributes} of @Service annotation
*/
//获取我们在 @Service 注解对象上的设置的所有属性值
AnnotationAttributes serviceAnnotationAttributes = getAnnotationAttributes(service, false, false);
//获得beanClass所实现的接口
Class<?> interfaceClass = resolveServiceInterfaceClass(serviceAnnotationAttributes, beanClass);
//获得beanDefinitionHolder所存储的名字
String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();
//生成ServiceBean的对应的BeanDefinition,并根据serviceAnnotationAttributes对象来设置这个BeanDefinition
//对象的propertyValues的值。理解成,类似于将以前在xml里面配置的property属性的值读到这个propertyValues属性中
//其实propertyValues底层就是个list,见下图,我们在例子中给的(token = "shihy")被读到了该属性中。每个ServiceBean的beanClass属性都是org.apache.dubbo.config.spring.ServiceBean类型。
AbstractBeanDefinition serviceBeanDefinition =
buildServiceBeanDefinition(service, serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName);
// ServiceBean Bean name
//生成 ServiceBean 的名字
String beanName = generateServiceBeanName(serviceAnnotationAttributes, interfaceClass);
//校验在 scanner 中,已经存在 beanName 。若不存在,则进行注册。
if (scanner.checkCandidate(beanName, serviceBeanDefinition)) { // check duplicated candidate bean
//将ServiceBean所对应的BeanDefinition注册到容器中
registry.registerBeanDefinition(beanName, serviceBeanDefinition);
if (logger.isInfoEnabled()) {
.................
}
} else {
if (logger.isWarnEnabled()) {
....................
}
}
}
4、ReferenceAnnotationBeanPostProcessor.class
看看ReferenceAnnotationBeanPostProcessor.class,继承 AnnotationInjectedBeanPostProcessor 抽象类,实现 ApplicationContextAware、ApplicationListener 接口,扫描 @Reference 注解的类,创建对应的 Spring BeanDefinition 对象,从而创建 Dubbo Reference Bean 对象。它实际是一个BeanPostProcessor的子类。
BeanPostProcessor类型会在容器启动时候,在registerBeanPostProcessors(beanFactory)方法中进行实例化并放入到容器的beanPostProcessors这个List属性中,这个动作,在实例化我们自定义的单例bean之前发生。
上面是spring里面的知识,现在下面结合代码和截图来看看ReferenceAnnotationBeanPostProcessor工作的原理:
定义一个controller和service类
@Slf4j
@RestController
@RequestMapping(path = "/car/")
public class CarOperationController {
@Autowired
CarOperationService carOperationService;
/*
* @Description: 车辆作业计划基础信息列表查询
* @param: paramsMap
* @return: com.ay.framework.base.utils.JSONResult
* @Creator: shihy
* @Date: 2020/7/3 11:47
*/
@GetMapping("getCarPlanList")
public JSONResult getCarPlanList(@RequestParam Map<String, Object> paramsMap) {
return carOperationService.getCarPlanList(paramsMap);
}
}
//想在CarOperationServiceImpl类中引入iFeignPlan
@Service
public class CarOperationServiceImpl implements CarOperationService {
@Reference
IFeignPlan iFeignPlan;
}
我们来看看ReferenceAnnotationBeanPostProcessor.class的执行时机,carOperationServiceImpl是我们要生成的自定义的bean,它是CarOperationServiceImpl类型,在实例化carOperationServiceImpl的时候,会在doCreateBean`方法中,
<1> 调用applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName)方法,在该方法中,该方法会调用
<2>getBeanPostProcessors()方法获取容器中所有的postProcessor,然后进行循环的调用MergedBeanDefinitionPostProcessor类型的postProcessor的postProcessMergedBeanDefinition方法,ReferenceAnnotationBeanPostProcessor正好是类型的子类。所以会执行它的postProcessMergedBeanDefinition方法(其实这个方法还在它的上层父接口中),在该方法中会事先调用
<3>findInjectionMetadata方法,先查以前是否有缓存过AnnotatedInjectionMetadata(它的作用就是记录这个要被实例化的类即,CarOperationServiceImpl.class,它的哪些属性上标注了某个注解),如果没有,
<4> 通过buildAnnotatedMetadata(clazz)方法解析被注入类(即CarOperationServiceImpl.class,其实clazz这个形参其实就是CarOperationServiceImpl.class)的哪些成员变量上标注了某个注解,那么它是怎么知道是什么具体的注解呢?其实这个注解是写死的,即,在ReferenceAnnotationBeanPostProcessor的构造方法里面,已经给它的成员变量annotationTypes赋值了Reference.class, com.alibaba.dubbo.config.annotation.Reference.class这两个注解。
所以,在执行buildAnnotatedMetadata(clazz)方法的时候,就会根据annotationTypes里面的两个注解,去被实例化类上即CarOperationServiceImpl类里面,找到它的哪些成员变量上,标注了这两个注解,并生成对应的AnnotatedInjectionMetadata对象。
<5> 接着doCreateBean方法中调用populateBean方法,populateBean方法中会再一次执行getBeanPostProcessors()方法,方法获取容器中所有的postProcessor,若是InstantiationAwareBeanPostProcessor类型的,然后进行循环的调用它们的postProcessPropertyValues方法,在该方法中会再次调用上面提到过的findInjectionMetadata方法,由于已经解析过了,所以可以直接从缓存中获取AnnotatedInjectionMetadata对象。
<6>接着,会执行AnnotatedInjectionMetadata对象的inject方法。AnnotatedInjectionMetadata对象的inject方法会调用到``ReferenceAnnotationBeanPostProcessor的doGetInjectedBean`方法,在该方法里面会根据每个@Reference注解生成一个Reference对象(这一点和上面的@Service对象一样),该对象的内容见下图。
<7>最终调用到getOrCreateProxy方法,生成那个代理对象,这个代理对象里面包含了注册中心的信息,服务名字等等重要的信息,这些信息怎么得来的可以查看org.apache.dubbo.config.AbstractConfig#refresh方法中的CompositeConfiguration compositeConfiguration = env.getConfiguration(getPrefix(), getId());这段代码,大概是从配置文件中读取的,这个有待研究。这个代理对象会最终被注入到carOperationServiceImpl这个对象的成员变量iFeignPlan中。