本系列dubbo源码解析文章基于apache dubbo 2.7.7版本
特殊说明:在2.7.7版本中dubbo新引入了两个新的注解@DubboService和@DubboReference来分别代替原来的服务暴露注解@service和服务引用依赖注入的注解@Reference。所以在2.7.7版本中会同时兼容存在三个版本的注解
服务暴露的注解:@DubboService(apache/dubbo 2.7.7),@Service(apache/dubbo 2.7.7 before),@Service(aibaba)
服务引用注解:@DubboReference(apache/dubbo 2.7.7),@Reference(apache/dubbo 2.7.7 before),@Reference(alibaba)
接下来的文章中都会使用最新版的注解
在上一篇文章聊聊dubbo那些事儿之注解驱动外部化配置中我们详细介绍了dubbo外部化配置自动绑定到dubbo配置Bean的整个过程,这篇文章我们来介绍下@DubboService,@DubboReference注解如何来驱动dubbo服务的,背后到底发生了什么。我们一起来探索下这两个注解披的这层神秘面纱。
@DubboComponentScan
在上篇文章介绍@EnableDubbo的时候我们略有介绍到@DubboComponentScan注解中的属性,现在我们来正式介绍下这个注解。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(DubboComponentScanRegistrar.class)
public @interface DubboComponentScan {
/**
* basePackages 的别名,这样提供了一种更加简洁的配置方式
* 比如: 用@DubboComponentScan("org.my.pkg")} 代替
* @DubboComponentScan(basePackages="org.my.pkg")
* @return the base packages to scan
*/
String[] value() default {};
/**
* 指定被注解@DubboService标注的dubbo服务类所在包的根路径
* @return the base packages to scan
*/
String[] basePackages() default {};
/**
* 用该属性指定的类所在包以及下面的子包作为scanBasePackages
*/
Class<?>[] basePackageClasses() default {};
}
这个注解主要做的事情:
- 根据注解配置中提供的包路径,在这个包路径下扫描被@DubboService标注的class,并将其自动注册为spring bean,并封装成即将要暴露用的serverBean。包路径支持占位符
- 扫描所有spring中的bean,找出被注解@DubboReference标注的Field和Method进行依赖注入dubbo的服务代理。
这里需要注意的是:标注@DubboReference的Field或者Method所在类,必须首先是spring bean,否则是不会对其依赖注入dubbo服务的。
注意@DubboComponentScan注解@import进来的类是DubboComponentScanRegistrar.class,这个类正式处理上述逻辑的地方。
DubboComponentScanRegistrar.class
我们看到DubboComponentScanRegistrar类继承了ImportBeanDefinitionRegistrar 接口,在上篇文章中我们详细的介绍了ImportBeanDefinitionRegistrar 接口,这里在简单回顾下:
- ImportBeanDefnitionRegistor类是用来在spring处理Configuration配置类的时候,注册额外的自定义的bean
- 当处理Java编程式配置类(使用了@Configuration的类)的时候ImportBeanDefinitionRegistrar接口的实现类可以注册额外的bean definitions。
- ImportBeanDefinitionRegistrar接口的实现类必须提供给@Import注解或者是ImportSelector接口返回值
registerBeanDefinitions
该方法用来根据定义的注解原信息处理自定义bean的注册,在DubboComponentScanRegistrar类中主要做下面几件事情:
- 根据@DubboComponentScan注解中得原信息解析需要扫描的路径
- 扫描指定包路径下标注@DubboService的类将其注册为spring bean并封装成即将要暴露用的serverBean 这里主要是处理@DubboService 不会处理@DubboReference
- 注册ReferenceAnnotationBeanPostProcessor类 依赖注入被@DubboReference标注的Field和Method。注意引用dubbo服务的类必须是spring bean,dubbo并不会将@DubboReference标注的类自动注册为spring bean。
public class DubboComponentScanRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//这个路径只是去处理@DubboService
Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
// 注册ServiceAnnotationBeanPostProcessor类 用来处理 serviceBean的 注册
registerServiceAnnotationBeanPostProcessor(packagesToScan, registry);
// @since 2.7.6 Register the common beans
//注册ReferenceAnnotationBeanPostProcessor类 用来处理 referenceBean的注册
//在所有注册的bean中 去查找标注@DubboReference的类 创建服务引用 ,和扫描路径 packageToScan没关系
registerCommonBeans(registry);
}
}
解析扫描路径packagesToScan
- 将注解@DubboComponentScan中标注的value,basePackages,basePackageClasses标注的扫描路径聚合成Set集合返回汇总后的扫描路径。
- 如果用户没有指定扫描路径,则使用被@DubboComponentScan注解标注的配置类所在的路径。
比如:
package org.apache.dubbo.demo.provider;
public class Application {
@Configuration
@EnableDubbo()//不指定scanPackages
@PropertySource("classpath:/spring/dubbo-provider.properties")
static class ProviderConfiguration {
@Bean
public RegistryConfig registryConfig() {
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setId("");
registryConfig.setAddress("zookeeper://127.0.0.1:2181");
return registryConfig;
}
}
}
这里得到的默认扫描路径为:ProviderConfiguration 类所在包路径org.apache.dubbo.demo.provider
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));
}
//如果用户没有指定扫描路径,则使用被@DubboComponentScan注解标注的配置类所在的路径。
if (packagesToScan.isEmpty()) {
return Collections.singleton(ClassUtils.getPackageName(metadata.getClassName()));
}
return packagesToScan;
}
registerServiceAnnotationBeanPostProcessor
这里主要是注册ServiceClassPostProcessor用来扫描指定路径下被@DubboService注解标注的类,将其注册为spring bean并封装成即将要暴露用的serverBean(DubboBootStrap.start中暴露dubbo服务)
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);
}
ServiceClassPostProcessor
该类实现了BeanDefinitionRegistryPostProcessor接口,在接口方法postProcessBeanDefinitionRegistry中实现对@DubboService注解处理的逻辑。
我们先来介绍下BeanDefinitionRegistryPostProcessor这个接口。
BeanDefinitionRegistryPostProcessor
当spring的application context完成标准的初始化之后,这时候所有的bean definition都已经被加载但是还没有实例化,在这时候这个接口提供的回调方法中还可以去修改application context中得 bean definition。可以进一步加载一些自定义的bean definition。既可以获取和修改BeanDefinition的元数据,也可以实现BeanDefinition的注册、移除等操作。
BeanDefinitionRegistryPostProcessor中的回调方法会被先触发然后触发BeanFactoryPostProcessor中的回调方法。
为什么要实现这个接口?
因为dubbo serviceBean的初始化需要其他的dubbo config bean 比如 applicationConfig, protocalConfig等,所以在注册service bean definition的时候 需要等到所有的 bean definition都已经加载后 在加载service bean。
postProcessBeanDefinitionRegistry
- 我们看到这里会兼容处理文章开头特殊说明中提到的三个版本的dubbo服务注解:@DubboService(apache/dubbo 2.7.7),@Service(apache/dubbo 2.7.7 before),@Service(aibaba)。
- 将需要兼容处理的注解存放在serviceAnnotationTypes字段中。
处理@DubboService注解:
- 注册spring容器启动,关闭事件的监听器,在事件回调方法中调用dubbo启动类DubboBootStrap的start,stop方法来启动和关闭dubbo应用。
- 解析扫描路径scanPackages中的占位符得到真正的扫描路径。(扫描路径支持占位符)
- 将扫描路径下标注@DubboService的类包装成ServerBean,并注册为spring bean。
public class ServiceClassPostProcessor implements BeanDefinitionRegistryPostProcessor, EnvironmentAware,
ResourceLoaderAware, BeanClassLoaderAware {
private final static List<Class<? extends Annotation>> serviceAnnotationTypes = asList(
// @since 2.7.7 Add the @DubboService , the issue : https://github.com/apache/dubbo/issues/6007
DubboService.class,
// @since 2.7.0 the substitute @com.alibaba.dubbo.config.annotation.Service
Service.class,
// @since 2.7.3 Add the compatibility for legacy Dubbo's @Service , the issue : https://github.com/apache/dubbo/issues/4330
com.alibaba.dubbo.config.annotation.Service.class
);
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// @since 2.7.5 注册spring启动 关闭事件的listener
//在事件回调中中调用启动类 DubboBootStrap的start stop来启动 关闭dubbo应用
registerBeans(registry, DubboBootstrapApplicationListener.class);
//解析@EnableDubbo中得basePaclages中的配置 解析占位符 这里支持占位符
//得到真正的扫描包路径
Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);
if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
//将扫描路径下 标注@DubboService的类 提升为 spring bean 后续再DubboBootStrap.start中暴露dubbo服务
// 将dubbo服务封装成serverBean,在spring容器启动时暴露dubbo服务
registerServiceBeans(resolvedPackagesToScan, registry);
} else {
if (logger.isWarnEnabled()) {
logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
}
}
}
}
DubboBootstrapApplicationListener
- 监听ApplicationContextEvent事件
- ContextRefreshedEvent事件回调中启动dubbo服务
- ContextClosedEvent事件回调中关闭dubbo服务
public class DubboBootstrapApplicationListener extends OneTimeExecutionApplicationContextEventListener
implements Ordered {
public DubboBootstrapApplicationListener() {
this.dubboBootstrap = DubboBootstrap.getInstance();
}
@Override
public void onApplicationContextEvent(ApplicationContextEvent event) {
if (event instanceof ContextRefreshedEvent) {
onContextRefreshedEvent((ContextRefreshedEvent) event);
} else if (event instanceof ContextClosedEvent) {
onContextClosedEvent((ContextClosedEvent) event);
}
}
private void onContextRefreshedEvent(ContextRefreshedEvent event) {
dubboBootstrap.start();
}
private void onContextClosedEvent(ContextClosedEvent event) {
dubboBootstrap.stop();
}
}
解析baseScanPackages中的占位符
private Set<String> resolvePackagesToScan(Set<String> packagesToScan) {
Set<String> resolvedPackagesToScan = new LinkedHashSet<String>(packagesToScan.size());
for (String packageToScan : packagesToScan) {
if (StringUtils.hasText(packageToScan)) {
//解析配置路径中的占位符,得到真正的扫描路径
String resolvedPackageToScan = environment.resolvePlaceholders(packageToScan.trim());
resolvedPackagesToScan.add(resolvedPackageToScan);
}
}
return resolvedPackagesToScan;
}
扫描路径下的dubbo服务注册为spring bean
- 初始化DubboClassPathBeanDefinitionScanner
- 为Scanner指定需要扫描哪些类,这里指被@DubboService等多版本的dubbo服务注解标注的类。
- scanner.scan(packageToScan)扫描路径下的指定类,并将扫描到的类的bean definition注册到spring中。
- scanner.findCandidateComponents(packageToScan)得到指定路径下 被@DubboService等注解标注的类的bean definition
- 将这些beanDefinition封装成BeanDefinitionHolder。
- 构造ServerBean的bean definition,并注册到spting中。
private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
DubboClassPathBeanDefinitionScanner scanner =
new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);
scanner.setBeanNameGenerator(beanNameGenerator);
// refactor @since 2.7.7 指定定扫描哪些注解标注的类
serviceAnnotationTypes.forEach(annotationType -> {
scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType));
});
for (String packageToScan : packagesToScan) {
// Registers @Service Bean first 将扫描到得指定类的 bean definition注册
//提升dubbo服务类为spring bean的地方
scanner.scan(packageToScan);
// Finds all BeanDefinitionHolders of @Service whether @ComponentScan scans or not.
//解决 同时标注dubbo @service注解 和spring @service注解的类 导致 这个dubbo服务没有暴露
//scanner中保存了指定路径下扫描到得指定类的beanDefinition 并将其封装程 beanDefinitionHolder
Set<BeanDefinitionHolder> beanDefinitionHolders =
findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);
if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
//构造ServerBean的bean definition,并注册到spting中
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 + "]");
}
}
}
}
注册ServiceBean
通过@DubboService注解中的配置属性以及标注@DubboService的类的BeanDefinition构建ServiceBean的BeanDefinition。
- 获取dubbo服务的实现类。
- 获取dubbo服务上标注的@DubboService等服务注解标注的属性。
- 获取dubbo服务接口
- 通过@DubboService上标注的属性来填充ServiceBean的属性。比如:ref,method,application,registery等,如果注解上没有标注则不填充,后续会自动填充默认的。
- 获取ServiceBean的BeanName->ServiceBean:interfaceClassName:version:group
- 将构造好的ServiceBean注册到spring上下文中,后续暴露服务使用。
private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry,
DubboClassPathBeanDefinitionScanner scanner) {
//获取dubbo服务impl类
Class<?> beanClass = resolveClass(beanDefinitionHolder);
//获取service类上标注的注解 @service
Annotation service = findServiceAnnotation(beanClass);
/**
* The {@link AnnotationAttributes} of @Service annotation
*/
AnnotationAttributes serviceAnnotationAttributes = getAnnotationAttributes(service, false, false);
//获取暴露的service接口
Class<?> interfaceClass = resolveServiceInterfaceClass(serviceAnnotationAttributes, beanClass);
//获取暴露的服务名称 serviceBean中的ref属性
String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();
//通过@service注解上的属性 填充serviceBean的属性 比如 ref method application registery......(如果没有 则不填充 还没到默认填充的时候)
AbstractBeanDefinition serviceBeanDefinition =
buildServiceBeanDefinition(service, serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName);
// ServiceBean Bean name:ServiceBean:${interfaceClassName}:${version}:${group}
String beanName = generateServiceBeanName(serviceAnnotationAttributes, interfaceClass);
if (scanner.checkCandidate(beanName, serviceBeanDefinition)) { // check duplicated candidate bean
registry.registerBeanDefinition(beanName, serviceBeanDefinition);
if (logger.isInfoEnabled()) {
logger.info("The BeanDefinition[" + serviceBeanDefinition +
"] of ServiceBean has been registered with name : " + beanName);
}
} else {
if (logger.isWarnEnabled()) {
logger.warn("The Duplicated BeanDefinition[" + serviceBeanDefinition +
"] of ServiceBean[ bean name : " + beanName +
"] was be found , Did @DubboComponentScan scan to same package in many times?");
}
}
}
获取dubbo服务实现类
private Class<?> resolveClass(BeanDefinition beanDefinition) {
String beanClassName = beanDefinition.getBeanClassName();
return resolveClassName(beanClassName, classLoader);
}
获取dubbo服务类上标注的@DubboService等服务注解属性
先将标注多个相同注解合并为一个注解。然后返回第一个标注的注解。
private Annotation findServiceAnnotation(Class<?> beanClass) {
return serviceAnnotationTypes
.stream()
.map(annotationType -> findMergedAnnotation(beanClass, annotationType))
.filter(Objects::nonNull)
.findFirst()
.orElse(null);
}
获取暴露的dubbo service接口
- 注解中是否明确指定了服务接口interfaceClass,有的话直接返回。
- 注解中是否明确指定了服务接口名称interfaceName,有的话通过interfaceName获取到interfaceClass。
- 既没有指定interfaceClass也没有指定interfaceName,则会递归一层一层往上找dubbo服务实现类所有父类实现的接口,然后聚合成一个list,再取第一个。
在dubbo 2.6.6之前,dubbo是不会一层一层向上查找父类实现的接口的,而是会直接使用service申明的父类作为服务暴露接口,这样就会导致下面这种情况发生:
比如这样的继承结构:接口 ---> 抽象类 ---> 服务实现类,@service注解加在服务实现类上
2.6.6之前dubbo会用这个抽像类作为dubbo服务暴露的接口,这显然是错的。
相关issue
同时也解决了dubbo服务注解@service支持spring aop功能。之前使用aop的时候,在dubbo服务实现类上增加@Service,@Transactional那么dubbo获取的服务暴露接口是spring增加的代理类接口,导致dubbo服务暴露失败。相关issue
public static Class<?> resolveServiceInterfaceClass(AnnotationAttributes attributes, Class<?> defaultInterfaceClass)
throws IllegalArgumentException {
ClassLoader classLoader = defaultInterfaceClass != null ? defaultInterfaceClass.getClassLoader() : Thread.currentThread().getContextClassLoader();
//@service注解中明确指定了 interfaceClass
Class<?> interfaceClass = getAttribute(attributes, "interfaceClass");
if (void.class.equals(interfaceClass)) { // default or set void.class for purpose.
interfaceClass = null;
//@service注解中明确指定了 interfaceName 则根据name 加载interfaceClass
String interfaceClassName = getAttribute(attributes, "interfaceName");
if (hasText(interfaceClassName)) {
if (ClassUtils.isPresent(interfaceClassName, classLoader)) {
interfaceClass = resolveClassName(interfaceClassName, classLoader);
}
}
}
//如果@service 没有明确指定接口 则从service类一层一层向上查找 获取其实现的第一个接口
if (interfaceClass == null && defaultInterfaceClass != null) {
// Find all interfaces from the annotated class
// To resolve an issue : https://github.com/apache/dubbo/issues/3251
/*
* 如果不在service注解中明确指定interface 那么在2.6.6之前 dubbo不会一层一层向上找 接口
* 而是会直接使用service声明的父类 而不是在父类中在一层一层向上找。就会导致下面这种情况的发生
* 接口 ---> 抽象类 ---> 服务实现类,@service注解加在服务实现类上
*
* 递归一层一层往上找所有父类实现的接口,然后聚合成一个list,再取第一个
* */
/*
* 这里也同时解决了 spring aop功能的支持问题
* @Service
* @Transactional
* 同时增加这两个注解 dubbo service会正确的暴露出来
* 否则取到的Interface则为 spring增加的代理类接口
* */
Class<?>[] allInterfaces = getAllInterfacesForClass(defaultInterfaceClass);
if (allInterfaces.length > 0) {
interfaceClass = allInterfaces[0];
}
}
Assert.notNull(interfaceClass,
"@Service interfaceClass() or interfaceName() or interface class must be present!");
Assert.isTrue(interfaceClass.isInterface(),
"The annotated type must be an interface!");
return interfaceClass;
}
填充ServiceBean Definition
在ServiceBean中有一些属性是可以直接从@DubboService注解中直接赋值过去。比如
/**
* The service version
*/
protected String version;
/**
* The service group
*/
protected String group;
/**
* whether the service is deprecated
*/
protected Boolean deprecated = false;
/**
* The time delay register service (milliseconds)
*/
protected Integer delay;
/**
* Whether to export the service
*/
protected Boolean export;
/**
* The service weight
*/
protected Integer weight;
/**
* Document center
*/
protected String document;
而另外的一些属性比如:
/**
* The protocol list the service will export with
* Also see {@link #protocolIds}, only one of them will work.
*/
protected List<ProtocolConfig> protocols;
/**
* The reference of the interface implementation
*/
protected T ref;
/**
* The provider configuration
*/
protected ProviderConfig provider;
/**
* Service monitor
*/
protected MonitorConfig monitor;
/**
* The application info
*/
protected ApplicationConfig application;
这些属性是需要依赖dubbo的config bean reference配置类的引用。这些属性需要先解析成RuntimeBeanReference,然后在赋值到ServiceBean Definition中。
所以ServiceBean的属性填充分为两部分,第一部分直接赋值一些非引用的属性。第二部分赋值引用属性。
- 将依赖引用的属性名称添加到ignoreAttributeNames集合中(这个集合中的属性后续会单独填充)
- 无脑一次性填充除ignoreAttributeNames集合之外的其他非引用属性。
- 单独设置引用属性。引用属性在@DubboService注解中配置的是dubbo config bean对应的beanName。通过beanName封装成RuntimeBeanReference,填充到serviceBeanDefinition中。
这里method和paramsers属性会比较特殊,他们在注解中配置的都是值,这里将这些配置转换成对应的config类 赋值到service bean definition中。
比如method对应的 List<MethodConfig>,paramers对应的Map<String, String>。
在@DubboService中的注解配置形式如下:
@Service(version = AnnotationConstants.VERSION, methods = {@Method(name = "sayGoodbye", timeout = 250, retries = 0)})
public class AnnotationHelloServiceImpl implements HelloService {
private AbstractBeanDefinition buildServiceBeanDefinition(Annotation serviceAnnotation,
AnnotationAttributes serviceAnnotationAttributes,
Class<?> interfaceClass,
String annotatedServiceBeanName) {
BeanDefinitionBuilder builder = rootBeanDefinition(ServiceBean.class);
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
//后续通过MutablePropertyValues 设置serviceBean的 beanDefinition
MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
//在AnnotationPropertyValuesAdapter中忽略下列属性,后续单独设置
String[] ignoreAttributeNames = of("provider", "monitor", "application", "module", "registry", "protocol",
"interface", "interfaceName", "parameters");
//将@service注解上的属性添加到 beanDefinition中
propertyValues.addPropertyValues(new AnnotationPropertyValuesAdapter(serviceAnnotation, environment, ignoreAttributeNames));
//开始单独设置ignoreAttributeNames中的属性
// References "ref" property to annotated-@Service Bean
addPropertyReference(builder, "ref", annotatedServiceBeanName);
// Set interface
builder.addPropertyValue("interface", interfaceClass.getName());
// Convert parameters into map
builder.addPropertyValue("parameters", convertParameters(serviceAnnotationAttributes.getStringArray("parameters")));
// Add methods parameters
List<MethodConfig> methodConfigs = convertMethodConfigs(serviceAnnotationAttributes.get("methods"));
if (!methodConfigs.isEmpty()) {
builder.addPropertyValue("methods", methodConfigs);
}
/**
* Add {@link org.apache.dubbo.config.ProviderConfig} Bean reference
*/
String providerConfigBeanName = serviceAnnotationAttributes.getString("provider");
if (StringUtils.hasText(providerConfigBeanName)) {
addPropertyReference(builder, "provider", providerConfigBeanName);
}
/**
* Add {@link org.apache.dubbo.config.MonitorConfig} Bean reference
*/
String monitorConfigBeanName = serviceAnnotationAttributes.getString("monitor");
if (StringUtils.hasText(monitorConfigBeanName)) {
addPropertyReference(builder, "monitor", monitorConfigBeanName);
}
/**
* Add {@link org.apache.dubbo.config.ApplicationConfig} Bean reference
*/
String applicationConfigBeanName = serviceAnnotationAttributes.getString("application");
if (StringUtils.hasText(applicationConfigBeanName)) {
addPropertyReference(builder, "application", applicationConfigBeanName);
}
/**
* Add {@link org.apache.dubbo.config.ModuleConfig} Bean reference
*/
String moduleConfigBeanName = serviceAnnotationAttributes.getString("module");
if (StringUtils.hasText(moduleConfigBeanName)) {
addPropertyReference(builder, "module", moduleConfigBeanName);
}
/**
* Add {@link org.apache.dubbo.config.RegistryConfig} Bean reference
*/
String[] registryConfigBeanNames = serviceAnnotationAttributes.getStringArray("registry");
//在解析到serviceBean 依赖的bean的时候 会创建RuntimeBeanReference(这个是一个引用的bean 需要在runtime时候解析)
//比如serverBean在暴露的时候是需要依赖 registery 和 protocal
List<RuntimeBeanReference> registryRuntimeBeanReferences = toRuntimeBeanReferences(registryConfigBeanNames);
if (!registryRuntimeBeanReferences.isEmpty()) {
builder.addPropertyValue("registries", registryRuntimeBeanReferences);
}
/**
* Add {@link org.apache.dubbo.config.ProtocolConfig} Bean reference
*/
String[] protocolConfigBeanNames = serviceAnnotationAttributes.getStringArray("protocol");
List<RuntimeBeanReference> protocolRuntimeBeanReferences = toRuntimeBeanReferences(protocolConfigBeanNames);
if (!protocolRuntimeBeanReferences.isEmpty()) {
builder.addPropertyValue("protocols", protocolRuntimeBeanReferences);
}
return builder.getBeanDefinition();
}
//向beanDefinition中添加beanReference引用
private void addPropertyReference(BeanDefinitionBuilder builder, String propertyName, String beanName) {
String resolvedBeanName = environment.resolvePlaceholders(beanName);
builder.addPropertyReference(propertyName, resolvedBeanName);
}
自此注解驱动dubbo服务暴露相关的内容,我们就完整的介绍完了。
下面开始介绍dubbo是如何利用注解驱动服务引用的。
ReferenceAnnotationBeanPostProcessor
这个类正是dubbo处理@DubboReference等dubbo服务引用注解依赖注入dubbo服务代理的地方。
public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBeanPostProcessor implements
ApplicationContextAware, ApplicationListener<ServiceBeanExportedEvent>
我们看到这个类签名中继承了一个非常重要的抽象类com.alibaba.spring.beans.factory.annotation.AbstractAnnotationBeanPostProcessor,这里是dubbo处理服务依赖注入的核心所在。
这个类写的非常的棒,比spring官方处理依赖注入@Autowire,@Value等注解的processor实现的更加灵活更加通用,它可以同时支持多个自定义注解实现依赖注入。只需要在这个抽象类的实现类中的构造器中传入 自定义依赖注入的注解,然后实现doGetInjectedBean方法 就可以实现多个自定义注解同时依赖注入。
我们来看下这个抽像类的实现。
AbstractAnnotationBeanPostProcessor
public abstract class AbstractAnnotationBeanPostProcessor extends
InstantiationAwareBeanPostProcessorAdapter implements MergedBeanDefinitionPostProcessor, PriorityOrdered,
BeanFactoryAware, BeanClassLoaderAware, EnvironmentAware, DisposableBean
该类实现了两个重要的接口org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor和org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter。
这两个接口分别提供了在spring生命周期中处理依赖注入时的回调方法,我们先来看MergedBeanDefinitionPostProcessor这个接口。
MergedBeanDefinitionPostProcessor
从字面意思上来讲,这个接口主要是处理beanDefinition的合并,那么合并哪些beanDefinition呢?
@Component("demoServiceComponent")
public class DemoServiceComponent implements DemoService {
@Reference
private DemoService demoService;
@Override
public String sayHello(String name) {
return demoService.sayHello(name);
}
}
针对嵌套bean的合并:
例如:在DemoServiceComponent 这个spring bean中它依赖了DemoService 这个bean,需要在DemoServiceComponent 这个bean中对DemoService 进行依赖注入,那么在处理DemoServiceComponent 的bean definition中需要将DemoService的bean definition合并到DemoServiceComponent 的bean definiton中。将需要依赖注入的信息存储在org.springframework.beans.factory.support.RootBeanDefinition#externallyManagedConfigMembers中。
针对父子beanDefiinition的合并
例如:<bean id='childBeanId ' class='childBeanClass' parent='parentBean'>
在Spring bean definition megerd阶段 会将parentBean的BeanDefinition创建副本然后合并到childBean的BeanDefinition中。
在这个接口提供的回调方法org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition中此时所有的bean definition都已经注册到了spring中,在这个回调方法里我们会解析被自定义注解@DubboReference所标注的Field和Method,然后将这些需要依赖注入的Field和Method封装成InjectionMetadata,存入当前加载bean的beanDefinition里的externallyManagedConfigMembers集合中,等待后续注入。
方法签名中的参数含义:
- beanDefinition为当前加载bean的beanDefinition,这里是指DemoServiceComponent的bean
- beanType为DemoServiceComponent.class
- beanName为demoServiceComponent
在创建每一个spring bean的时候 都会回调该方法。
这里就是spring处理依赖注入的地方,spring默认 的几个MergedBeanDefinitionPostProcessor实现都是处理@Autowire,@Value等注解的地方,dubbo中的AbstractAnnotationBeanPostProcessor实现这个接口,正是处理@DubboReference等dubbo引用服务注解的地方,将这些标注@DubboReference等引用注解的filed和method都缓存起来 等待后边的注入。
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
if (beanType != null) {
//查找被自定义注解@DubboReference标注的field和method,找到需要依赖注入的field和method封装成InjectionMetadata ,并缓存起来等待后续注入。这里只是查找依赖的点
InjectionMetadata metadata = findInjectionMetadata(beanName, beanType, null);
//将需要依赖注入的InjectionMetadata ,存放到当前加载bean的beanDefinition里的externallyManagedConfigMembers集合中
metadata.checkConfigMembers(beanDefinition);
}
}
findInjectionMetadata
private final static int CACHE_SIZE = Integer.getInteger("", 32);
//缓存spring bean中需要依赖注入dubbo服务引用的Field和Method信息。
//key: beanName
//value: AnnotatedInjectionMetadata封装了需要依赖注入dubbo服务引用的field和method信息。
private final ConcurrentMap<String, AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata> injectionMetadataCache =
new ConcurrentHashMap<String, AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata>(CACHE_SIZE);
private InjectionMetadata findInjectionMetadata(String beanName, Class<?> clazz, PropertyValues pvs) {
// Fall back to class name as cache key, for backwards compatibility with custom callers.
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
// Quick check on the concurrent map first, with minimal locking.
AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
synchronized (this.injectionMetadataCache) {
metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
if (metadata != null) {
metadata.clear(pvs);
}
try {
//查找被@DubboReference等服务引用注解标注的Field和Method,并封装成AnnotatedInjectionMetadata
metadata = buildAnnotatedMetadata(clazz);
//将需要依赖注入dubbo服务的field和method缓存起来,等待后续注入。
this.injectionMetadataCache.put(cacheKey, metadata);
} catch (NoClassDefFoundError err) {
throw new IllegalStateException("Failed to introspect object class [" + clazz.getName() +
"] for annotation metadata: could not find class that it depends on", err);
}
}
}
}
return metadata;
}
buildAnnotatedMetadata
查找当前加载bean中需要依赖注入dubbo服务的Field和Method,将这些需要依赖注入dubbo服务的field和method封装成AnnotatedInjectionMetadata。
private AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata buildAnnotatedMetadata(final Class<?> beanClass) {
//查找需要依赖注入dubbo服务的field
Collection<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement> fieldElements = findFieldAnnotationMetadata(beanClass);
//查找需要依赖注入dubbo服务的method
Collection<AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement> methodElements = findAnnotatedMethodMetadata(beanClass);
return new AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata(beanClass, fieldElements, methodElements);
}
findFieldAnnotationMetadata
- 扫描所有spring加载的bean中被@DubboReference等服务引用注解标注的字段信息。
- 将扫描到的Field以及@DubboReference注解属性封装成AnnotatedFieldElement,添加到elements 中。
@DubboReference等服务引用注解不能标注在static field上。
这里需要注意一下:在扫描加载bean中的所有字段时,使用了ReflectionUtils.doWithFields这个方法,这个方法会垂直一直向上查找父类申明的字段信息。这里之前有个bug,之前只是会扫描当前类的Field,这样就会导致@Reference不支持字段的继承。什么意思呢?比如你在父类中声明一个字段并且用@Reference标注,然后在子类调用父类中这个dubbo服务引用,会报NullPointerException 异常。通过使用ReflectionUtils.doWithFields,垂直向上一层一层扫描父类所有声明的字段,这样就保证了@Reference注解的继承性。父类中依赖dubbo服务,在子类中也可以继承并且调用。
public abstract class BaseAction {
@Reference
private AnnotationService annotationService;
protected AnnotationService getAnnotationService() {
return annotationService;
}
}
@Component("annotationAction")
public class AnnotationAction extends BaseAction {
public String doSayHello(String name) {
return getAnnotationService().sayHello(name);
}
}
比如这样的一个例子,当我们调用doSayHello方法时候,如果不使用ReflectionUtils.doWithFields方法来扫描父类所有字段的话,那么父类标注@DubboReference等服务引用注解的Field将不会被扫描到,也就没法依赖注入dubbo服务代理,在子类中调用将会抛NullPointerException 异常。
/**
* Finds {@link InjectionMetadata.InjectedElement} Metadata from annotated fields
*
* @param beanClass The {@link Class} of Bean
* @return non-null {@link List}
*/
private List<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement> findFieldAnnotationMetadata(final Class<?> beanClass) {
final List<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement> elements = new LinkedList<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement>();
//垂直向上一层一层查找父类申明的所有字段,保证@Reference的字段继承性。父类依赖的dubbo服务,在子类中也可以继承调用。
ReflectionUtils.doWithFields(beanClass, new ReflectionUtils.FieldCallback() {
@Override
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
//扫描被@DubboReference(apache/dubbo 2.7.7),@Reference(apache/dubbo 2.7.7 before),@Reference(alibaba)这三个版本的dubbo引用注解标注的Field.
for (Class<? extends Annotation> annotationType : getAnnotationTypes()) {
//获取字段上标注的@DubboReference注解属性
AnnotationAttributes attributes = getAnnotationAttributes(field, annotationType, getEnvironment(), true, true);
if (attributes != null) {
//注意:@DubboReference等服务引用注解不能标注在static field上。
if (Modifier.isStatic(field.getModifiers())) {
if (logger.isWarnEnabled()) {
logger.warn("@" + annotationType.getName() + " is not supported on static fields: " + field);
}
return;
}
elements.add(new AnnotatedFieldElement(field, attributes));
}
}
}
});
return elements;
}
public class AnnotatedFieldElement extends InjectionMetadata.InjectedElement {
private final Field field;
private final AnnotationAttributes attributes;
private volatile Object bean;
protected AnnotatedFieldElement(Field field, AnnotationAttributes attributes) {
super(field, null);
this.field = field;
this.attributes = attributes;
}
}
findAnnotatedMethodMetadata
- 垂直向上查找当前类以及父类的所有方法。
- 如果该方法是一个桥接方法,则获取它的原始方法(被桥接的方法)。
- 查找被@DubboReference(apache/dubbo 2.7.7),@Reference(apache/dubbo 2.7.7 before),@Reference(alibaba)这三个版本的dubbo引用注解标注的method。
- 获取方法上标注@DubboReference注解的属性。
- 获取该Javabean 方法对应的PropertyDescriptor,后边通过method实现依赖注入的时候,会根据PropertyDescriptor获取该method要依赖注入的属性类型从而创建dubbo服务代理。
- 将扫描到的method信息:Method,PropertyDescriptor,AnnotationAttributes 封装成AnnotatedMethodElement添加到elements 中。
被@DubboReference注解标注的方法不能是static方法,并且方法参数不能为空。
这里我们看到在查找当前加载bean的方法时,也使用到了ReflectionUtils.doWithMethods,这个方法会查找当前类及其父类所有的方法。保证了@Reference方法的继承性。
/**
* Finds {@link InjectionMetadata.InjectedElement} Metadata from annotated methods
*
* @param beanClass The {@link Class} of Bean
* @return non-null {@link List}
*/
private List<AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement> findAnnotatedMethodMetadata(final Class<?> beanClass) {
final List<AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement> elements = new LinkedList<AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement>();
//垂直向上查找当前类以及父类的所有方法,保证@Reference方法的继承性
ReflectionUtils.doWithMethods(beanClass, new ReflectionUtils.MethodCallback() {
@Override
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
//获取被桥接的方法(原始方法)
Method bridgedMethod = findBridgedMethod(method);
if (!isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
}
//扫描被@DubboReference(apache/dubbo 2.7.7),@Reference(apache/dubbo 2.7.7 before),@Reference(alibaba)这三个版本的dubbo引用注解标注的method
for (Class<? extends Annotation> annotationType : getAnnotationTypes()) {
AnnotationAttributes attributes = getAnnotationAttributes(bridgedMethod, annotationType, getEnvironment(), true, true);
if (attributes != null && method.equals(ClassUtils.getMostSpecificMethod(method, beanClass))) {
if (Modifier.isStatic(method.getModifiers())) {
if (logger.isWarnEnabled()) {
logger.warn("@" + annotationType.getName() + " annotation is not supported on static methods: " + method);
}
return;
}
if (method.getParameterTypes().length == 0) {
if (logger.isWarnEnabled()) {
logger.warn("@" + annotationType.getName() + " annotation should only be used on methods with parameters: " +
method);
}
}
//后边通过method实现依赖注入的时候,会根据PropertyDescriptor获取该method要依赖注入的属性类型从而创建dubbo服务代理。
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, beanClass);
elements.add(new AnnotatedMethodElement(method, pd, attributes));
}
}
}
});
return elements;
}
private class AnnotatedMethodElement extends InjectionMetadata.InjectedElement {
private final Method method;
private final AnnotationAttributes attributes;
private volatile Object object;
protected AnnotatedMethodElement(Method method, PropertyDescriptor pd, AnnotationAttributes attributes) {
super(method, pd);
this.method = method;
this.attributes = attributes;
}
}
在com.alibaba.spring.beans.factory.annotation.AbstractAnnotationBeanPostProcessor#postProcessMergedBeanDefinition这个回调方法中,其实最主要做的事情就是收集当前加载bean中需要依赖注入的点包括需要依赖注入的Field和Method。将收集起来的依赖注入点封装成AnnotatedMethodElement ,缓存在injectionMetadataCache中。
private class AnnotatedInjectionMetadata extends InjectionMetadata {
private final Collection<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement> fieldElements;
private final Collection<AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement> methodElements;
public AnnotatedInjectionMetadata(Class<?> targetClass, Collection<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement> fieldElements,
Collection<AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement> methodElements) {
super(targetClass, combine(fieldElements, methodElements));
this.fieldElements = fieldElements;
this.methodElements = methodElements;
}
public Collection<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement> getFieldElements() {
return fieldElements;
}
public Collection<AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement> getMethodElements() {
return methodElements;
}
}
最后调用org.springframework.beans.factory.annotation.InjectionMetadata#checkConfigMembers方法将搜集到的需要依赖注入的Field和Method的原信息注册到当前加载Bean的beanDefinition中的externallyManagedConfigMembers集合中,等待后续注入。
public void checkConfigMembers(RootBeanDefinition beanDefinition) {
Set<InjectedElement> checkedElements = new LinkedHashSet<InjectedElement>(this.injectedElements.size());
for (InjectedElement element : this.injectedElements) {
Member member = element.getMember();
if (!beanDefinition.isExternallyManagedConfigMember(member)) {
beanDefinition.registerExternallyManagedConfigMember(member);
checkedElements.add(element);
if (logger.isDebugEnabled()) {
logger.debug("Registered injected element on class [" + this.targetClass.getName() + "]: " + element);
}
}
}
//将需要依赖注入的元信息存放在checkedElements ,后续依赖注入的时候会用到
this.checkedElements = checkedElements;
}
InstantiationAwareBeanPostProcessorAdapter
这个接口提供了在spring bean的实例化和初始化前后的一些回调方法,下面我们来总体梳理下这几个回调方法对应的生命周期。
- 在spring创建bean的时候会调用org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition来收集当前加载bean中需要依赖注入的Field和Mehthod信息并合并到当前加载bean的beanDefinition中。
- 在bean的实例化前会触发org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter#postProcessBeforeInstantiation。
- 调用当前加载bean的构造方法实例化bean。
- 在bean实例化后会触发org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter#postProcessAfterInstantiation
- 在bean的实例化后,初始化前会触发调用本文要介绍的第二个接口com.alibaba.spring.beans.factory.annotation.AbstractAnnotationBeanPostProcessor#postProcessPropertyValues在这个回调方法中主要是对bean属性的更改。在这里dubbo会通过之前缓存在injectionMetadataCache中的需要依赖注入的Field和Method元信息 完成RegisterBean的创建和注册,并且注入到相应的filed和method中 完成@DubboReference依赖注入 dubbo引用。
- 在bean的初始化前会触发其父接口BeanPostProcessor中的postProcessBeforeInitialization方法。org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter#postProcessBeforeInitialization。
@PostConstruct标注的方法就是在这个回调方法中执行的。
org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor#postProcessBeforeInitialization
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
try {
//调用@PostConstruct方法
metadata.invokeInitMethods(bean, beanName);
}
catch (InvocationTargetException ex) {
throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Failed to invoke init method", ex);
}
return bean;
}
- 调用bean中的初始化方法。比如initializingBean 中的afterPropertiesSet方法以及在xml元素<bean>中指定的Init-method。
- 在bean初始化后会触发其父接口BeanPostProcessor中的postProcessAfterInitialization方法org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization。
我们看到InstantiationAwareBeanPostProcessorAdapter这个接口提供的回调方法主要发生在Bean的实例化这个阶段,而其父接口BeanPostProcessor提供的回调方法主要发生在初始化这个阶段。
下面我们来详细看下这个方法postProcessPropertyValues,在这里就是完成dubbo服务引用依赖注入的地方。
postProcessPropertyValues
@Override
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
//根据cachekey从injectionMetadataCache中查找在postProcessMergedBeanDefinition方法中缓存的依赖注入信息。
InjectionMetadata metadata = findInjectionMetadata(beanName, bean.getClass(), pvs);
try {
//依赖注入dubbo服务引用
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;
}
遍历InjectionMetadata 中checkedElements(存放的是需要依赖注入的Field和Method信息),依次依赖注入。
public void inject(Object target, String beanName, PropertyValues pvs) throws Throwable {
Collection<InjectedElement> elementsToIterate =
(this.checkedElements != null ? this.checkedElements : this.injectedElements);
if (!elementsToIterate.isEmpty()) {
boolean debug = logger.isDebugEnabled();
for (InjectedElement element : elementsToIterate) {
if (debug) {
logger.debug("Processing injected element of bean '" + beanName + "': " + element);
}
element.inject(target, beanName, pvs);
}
}
}
这里的InjectedElement 是AnnotatedFieldElement和AnnotatedMethodElement,分别实现通过Field和Method的方式实现依赖注入。
Field字段依赖注入
我们再来回到AnnotatedFieldElement类
- 通过getInjectedObject方法获取需要依赖注入的bean,这里是dubbo服务代理。
- 将刚获取的dubbo服务代理通过反射设置到被@DubboReference注解标注的Field中。
/**
* {@link Annotation Annotated} {@link Field} {@link InjectionMetadata.InjectedElement}
*/
public class AnnotatedFieldElement extends InjectionMetadata.InjectedElement {
private final Field field;
private final AnnotationAttributes attributes;
private volatile Object bean;
protected AnnotatedFieldElement(Field field, AnnotationAttributes attributes) {
super(field, null);
this.field = field;
this.attributes = attributes;
}
@Override
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
Class<?> injectedType = field.getType();
//获取需要依赖注入的bean这里是dubbo服务引用代理
Object injectedObject = getInjectedObject(attributes, bean, beanName, injectedType, this);
ReflectionUtils.makeAccessible(field);
//将dubbo服务代理设置到标注@DubboReference注解的字段上。实现依赖注入
field.set(bean, injectedObject);
}
}
Method依赖注入
我们回到AnnotatedMethodElement类中
- 获取需要依赖注入的bean这里是dubbo服务引用代理
- 将dubbo服务引用代理通过被@DubboReference标注的method反射调用设置到当前加载bean的属性中,完成方法的依赖注入
/**
* {@link Annotation Annotated} {@link Method} {@link InjectionMetadata.InjectedElement}
*/
private class AnnotatedMethodElement extends InjectionMetadata.InjectedElement {
private final Method method;
private final AnnotationAttributes attributes;
private volatile Object object;
protected AnnotatedMethodElement(Method method, PropertyDescriptor pd, AnnotationAttributes attributes) {
super(method, pd);
this.method = method;
this.attributes = attributes;
}
@Override
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
//通过PropertyDescriptor获取method设置的属性类型
Class<?> injectedType = pd.getPropertyType();
//获取需要依赖注入的bean这里是dubbo服务引用代理
Object injectedObject = getInjectedObject(attributes, bean, beanName, injectedType, this);
ReflectionUtils.makeAccessible(method);
//将需要依赖注入的bean通过被@DubboReference标注的method反射调用设置到当前加载bean的属性中,完成方法的依赖注入
method.invoke(bean, injectedObject);
}
}
创建dubbo服务代理
接下来该介绍AbstractAnnotationBeanPostProcessor 类的最后一些内容。
- injectedObjectsCache 用来缓存依赖注入的dubbo服务代理。
- 抽像方法buildInjectedObjectCacheKey用来构建dubbo服务代理的缓存Key。
- 抽象方法doGetInjectedBean用来创建dubbo服务代理。
- 构造器中传入自定义的依赖注入注解。
public abstract class AbstractAnnotationBeanPostProcessor extends
InstantiationAwareBeanPostProcessorAdapter implements MergedBeanDefinitionPostProcessor, PriorityOrdered,
BeanFactoryAware, BeanClassLoaderAware, EnvironmentAware, DisposableBean {
private final static int CACHE_SIZE = Integer.getInteger("", 32);
//缓存需要依赖注入的dubbo服务代理
private final ConcurrentMap<String, Object> injectedObjectsCache = new ConcurrentHashMap<String, Object>(CACHE_SIZE);
//构建缓存dubbo服务代理的cacheKey 子类实现
protected abstract String buildInjectedObjectCacheKey(AnnotationAttributes attributes, Object bean, String beanName,
Class<?> injectedType,
InjectionMetadata.InjectedElement injectedElement);
//创建dubbo服务代理
protected abstract Object doGetInjectedBean(AnnotationAttributes attributes, Object bean, String beanName, Class<?> injectedType,
InjectionMetadata.InjectedElement injectedElement) throws Exception;
//用来指定自定义的依赖注入注解这里是@DubboReference(apache/dubbo 2.7.7),@Reference(apache/dubbo 2.7.7 before),@Reference(alibaba)
public AbstractAnnotationBeanPostProcessor(Class<? extends Annotation>... annotationTypes) {
Assert.notEmpty(annotationTypes, "The argument of annotations' types must not empty");
this.annotationTypes = annotationTypes;
}
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) {
injectedObject = doGetInjectedBean(attributes, bean, beanName, injectedType, injectedElement);
// Customized inject-object if necessary
injectedObjectsCache.putIfAbsent(cacheKey, injectedObject);
}
return injectedObject;
}
}
到这里dubbo依赖注入dubbo服务代理的重要抽像类AbstractAnnotationBeanPostProcessor就介绍完了,我们看到这个抽象类实现的非常灵活可以支持多种自定义的依赖注解 实现依赖注入。子类只需要实现buildInjectedObjectCacheKey,doGetInjectedBean两个抽像方法。在doGetInjectedBean方法中创建需要依赖注入的bean。就完成了自定义依赖注入功能。
我们接下来再看下ReferenceAnnotationBeanPostProcessor是如何实现这两个抽象方法的。
public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBeanPostProcessor implements
ApplicationContextAware, ApplicationListener<ServiceBeanExportedEvent> {
/**
* The bean name of {@link ReferenceAnnotationBeanPostProcessor}
*/
public static final String BEAN_NAME = "referenceAnnotationBeanPostProcessor";
/**
* Cache size
*/
private static final int CACHE_SIZE = Integer.getInteger(BEAN_NAME + ".cache.size", 32);
//缓存referenceBean key为referenceBeanNmae
private final ConcurrentMap<String, ReferenceBean<?>> referenceBeanCache =
new ConcurrentHashMap<>(CACHE_SIZE);
//缓存需要依赖注入的field和需要注入的referenceBean之间的映射
private final ConcurrentMap<InjectionMetadata.InjectedElement, ReferenceBean<?>> injectedFieldReferenceBeanCache =
new ConcurrentHashMap<>(CACHE_SIZE);
//缓存需要依赖注入的method和需要注入的referenceBean之间的映射
private final ConcurrentMap<InjectionMetadata.InjectedElement, ReferenceBean<?>> injectedMethodReferenceBeanCache =
new ConcurrentHashMap<>(CACHE_SIZE);
//缓存referencedBeanName(本地服务暴露的beanName) 和 用于创建本地serviceBean代理的 invocationHandler之间的映射
private final ConcurrentMap<String, ReferencedBeanInvocationHandler> referencedBeanInvocationHandlersCache =
new ConcurrentHashMap<>();
private ApplicationContext applicationContext;
//构造器用来指定自定义的依赖注入注解
public ReferenceAnnotationBeanPostProcessor() {
super(DubboReference.class, Reference.class, com.alibaba.dubbo.config.annotation.Reference.class);
}
doGetInjectedBean
这块代码逻辑稍微有点绕,为了方便大家理解,我还是以这个例子讲解:
@Component("demoServiceComponent")
public class DemoServiceComponent implements DemoService {
@Reference
private DemoService demoService;
@Override
public String sayHello(String name) {
return demoService.sayHello(name);
}
@Override
public CompletableFuture<String> sayHelloAsync(String name) {
return null;
}
}
这个方法主要的逻辑是:
- 根据injectedType这里是DemoService.class和标注的@Reference注解属性,根据ServiceBeanName的命名规则构建出serviceBeanName,根据这个serviceBeanName在spring context中查找是否存在这样的serviceBean,如果引用的是本地服务那么这个serviceBean必然存在。本例中referencedBeanName:ServiceBean:org.apache.dubbo.demo.DemoService
- 根据ReferenceBeanName的构建规则,构建出referenceBeanName。如果引用的不是本地服务那么就用这个referenceBeanName注册referenceBean,如果引用的是本地服务则用这个referenceBeanName作为serviceBeanName的别名,依赖注入的其实就是这个本地服务实例的代理。本例中referenceBeanName :@Reference org.apache.dubbo.demo.DemoService。
- 根据注解@Reference中的配置属性,构建ReferenceBean。
- 判断这个引用的dubbo服务是否为本地服务,判断标准:
- 该dubbo服务的serviceBean是否存在于本地的spring context中。
- 该dubbo服务暴露的协议不能是inJvm
- 注册ReferenceBean,这里分为两种情况:
- 远程服务情况下则将referenceBean注册到spring中。
- 本地服务情况下则将referenceBeanName作为本地服务serviceBean的别名,在服务引用视角中用referenceBeanName获取到的其实是本地服务的serviceBean。
- 缓存依赖注入点(Filed,Method)与要依赖注入的referenceBean之间的关系。
- 创建远程服务引用代理,返回给父类,将该代理注入到相应的Field和Method中。这里同样要分为两种情况:
- 本地服务情况下,会创建返回本地服务的ServiceBean代理。
- 远程服务情况下,调用referenceBean.get()方法创建返回远程服务代理。
@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}
*/
//根据injectedType就是需要注入的DubboService,构造出serviceBeanName,后续会用这个serviceBeanName到spring中去查找
//是否存在serviceBean来判断这个DubboService是不是本地暴露的服务,如果是本地暴露的服务就直接调用。
String referencedBeanName = buildReferencedBeanName(attributes, injectedType);
/**
* The name of bean that is declared by {@link Reference @Reference} annotation injection
*/
//如果引用地不是本地暴露的服务,那么这个就是referenceBean,远程dubboService的代理引用。
//构造referenceBeanName
String referenceBeanName = getReferenceBeanName(attributes, injectedType);
//根据dubbo引用注解@reference的信息,将注解中配置的信息 构造referenceBean(这里就是dubbo注解驱动外部化配置的地方)
ReferenceBean referenceBean = buildReferenceBeanIfAbsent(referenceBeanName, attributes, injectedType);
//判断引用的dubbo服务是否为本地暴露的服务,如果是本地服务后边直接 反射调用服务实例的具体方法
boolean localServiceBean = isLocalServiceBean(referencedBeanName, referenceBean, attributes);
//注册ReferenceBean到spring中(远程暴露情况)
//用ReferenceBeanName关联本地的serviceBean,真正调用的是本地service方法(本地暴露情况)
registerReferenceBean(referencedBeanName, referenceBean, attributes, localServiceBean, injectedType);
//缓存依赖注入的referenceBean 和 依赖注入的点(field,method)之间的关联
cacheInjectedReferenceBean(referenceBean, injectedElement);
//创建返回 远程服务引用代理(这里有本地服务和远程服务的区别)
return getOrCreateProxy(referencedBeanName, referenceBean, localServiceBean, injectedType);
}
以上就是创建需要依赖注入的referenceBean的主要流程,下面我们来详细介绍下这里涉及到的一些核心重要方法。
buildReferenceBeanIfAbsent构建referenceBean
这个方法主要负责创建referenceBean并缓存在referenceBeanCache 中。
这段代码的逻辑不复杂,我们主要看下如何通过ReferenceBeanBuilder 去构建ReferencenBean的过程。
//缓存referenceBean key为referenceBeanNmae
private final ConcurrentMap<String, ReferenceBean<?>> referenceBeanCache =
new ConcurrentHashMap<>(CACHE_SIZE);
private ReferenceBean buildReferenceBeanIfAbsent(String referenceBeanName, AnnotationAttributes attributes,
Class<?> referencedType)
throws Exception {
ReferenceBean<?> referenceBean = referenceBeanCache.get(referenceBeanName);
if (referenceBean == null) {
ReferenceBeanBuilder beanBuilder = ReferenceBeanBuilder
.create(attributes, applicationContext)
.interfaceClass(referencedType);
referenceBean = beanBuilder.build();
referenceBeanCache.put(referenceBeanName, referenceBean);
} else if (!referencedType.isAssignableFrom(referenceBean.getInterfaceClass())) {
throw new IllegalArgumentException("reference bean name " + referenceBeanName + " has been duplicated, but interfaceClass " +
referenceBean.getInterfaceClass().getName() + " cannot be assigned to " + referencedType.getName());
}
return referenceBean;
}
ReferenceBeanBuilder
class ReferenceBeanBuilder extends AnnotatedInterfaceConfigBeanBuilder<ReferenceBean>
ReferenceBeanBuilder 继承了抽像父类AnnotatedInterfaceConfigBeanBuilder,我们主要来看build方法。
- 在ReferenceBeanBuilder#doBuild()方法中new一个空的ReferenceBean。
- 根据@DubboReference中的配置的属性,来填充ReferenceBean。注解中没有配置的属性占不填充,后续会在referenceBean#init()方法中自动装配其他属性。
public abstract class AnnotatedInterfaceConfigBeanBuilder<C extends AbstractInterfaceConfig> {
public final C build() throws Exception {
checkDependencies();
//new出一个 ReferenceBean
C configBean = doBuild();
//根据@reference注解信息构造ReferenceBean
//(注解里配置的属性就装配,没有标注的就不装配,自动装配在后续的ReferenceBean.init()中实现)
configureBean(configBean);
if (logger.isInfoEnabled()) {
logger.info("The configBean[type:" + configBean.getClass().getSimpleName() + "] has been built.");
}
return configBean;
}
}
填充ReferenceBean
// Ignore those fields
static final String[] IGNORE_FIELD_NAMES = of("application", "module", "consumer", "monitor", "registry");
protected void configureBean(C configBean) throws Exception {
//根据注解中的attributes(@reference) 装配cofigBean(ReferenceBean)
preConfigureBean(attributes, configBean);
//获取注解中配置的String[] registry
configureRegistryConfigs(configBean);
//获取注解配置的 String monitor()
configureMonitorConfig(configBean);
//获取注解配置的String application()
configureApplicationConfig(configBean);
//获取注解配置的String module()
configureModuleConfig(configBean);
//配置bean的interface consumer method
//最后调用bean的afterPropertiesSet()初始化bean
postConfigureBean(attributes, configBean);
}
- 根据@DubboReference注解中配置的属性来装配ReferenceBean
这里主要对一些基础配置信息比如:filter,listener,parameters等进行装配,会忽略IGNORE_FIELD_NAMES 集合中的配置信息,因为这些配置信息都是configBean类型的后续会专门针对这些configBean进行装配。- 创建referenceBean的属性绑定器dataBinder
- 注册属性解析器CustomEditor(将注解中的属性类型转化为referenceBean中的属性类 型)
- 解析注解属性配置中的占位符
- 将注解中配置的属性映射到referenceBean中。
这里我们看到@DubboReference注解中的配置属性是支持占位符操作的
@Override
protected void preConfigureBean(AnnotationAttributes attributes, ReferenceBean referenceBean) {
Assert.notNull(interfaceClass, "The interface class must set first!");
DataBinder dataBinder = new DataBinder(referenceBean);
// Register CustomEditors for special fields
//定义属性解析器 比如注解配置中的parameters配置类型是String[]----{key1, value1, key2, value2}
//在ReferenceBean中parameters的类型是map类型,所以在构造ReferenceBean的过程中要将String[] 转化为 Map
dataBinder.registerCustomEditor(String.class, "filter", new StringTrimmerEditor(true));
dataBinder.registerCustomEditor(String.class, "listener", new StringTrimmerEditor(true));
dataBinder.registerCustomEditor(Map.class, "parameters", new PropertyEditorSupport() {
@Override
public void setAsText(String text) throws java.lang.IllegalArgumentException {
// Trim all whitespace
String content = StringUtils.trimAllWhitespace(text);
if (!StringUtils.hasText(content)) { // No content , ignore directly
return;
}
// replace "=" to ","
content = StringUtils.replace(content, "=", ",");
// replace ":" to ","
content = StringUtils.replace(content, ":", ",");
// String[] to Map
Map<String, String> parameters = CollectionUtils.toStringMap(commaDelimitedListToStringArray(content));
setValue(parameters);
}
});
// Bind annotation attributes
//定义好属性解析器后,将注解中配置的属性 映射到 ReferenceBean中
//注解属性的配置支持占位符 @Reference(attribute=${SystemPropertyKey})
dataBinder.bind(new AnnotationPropertyValuesAdapter(attributes, applicationContext.getEnvironment(), IGNORE_FIELD_NAMES));
}
- 根据注解中的配置属性registry装配RegistryConfigs
@DubboReference中的registry类型
String[] registry() default {};
org.apache.dubbo.config.AbstractInterfaceConfig#registries(ReferenceBean中的registries类型)
protected List<RegistryConfig> registries;
- 获取@DubboReference中的registry属性。里边配置的是registryConfigBean的id。
- 通过registryConfigBeanIds 在spring 中查找RegistryConfigBean
- 将外部化配置中配置的RegistryConfigBeans设置到ReferenceBean中。
private void configureRegistryConfigs(C configBean) {
//获取注解中配置的String[] registry
String[] registryConfigBeanIds = resolveRegistryConfigBeanNames(attributes);
//根据注解中配置的 registryConfigBeanId 查找RegistryConfig Bean
List<RegistryConfig> registryConfigs = getBeans(applicationContext, registryConfigBeanIds, RegistryConfig.class);
configBean.setRegistries(registryConfigs);
}
- 根据注解中的配置属性monitor装配MonitorConfig
private void configureMonitorConfig(C configBean) {
String monitorBeanName = resolveMonitorConfigBeanName(attributes);
MonitorConfig monitorConfig = getOptionalBean(applicationContext, monitorBeanName, MonitorConfig.class);
configBean.setMonitor(monitorConfig);
}
- 根据注解中的配置属性application装配ApplicationConfig
private void configureApplicationConfig(C configBean) {
String applicationConfigBeanName = resolveApplicationConfigBeanName(attributes);
ApplicationConfig applicationConfig =
getOptionalBean(applicationContext, applicationConfigBeanName, ApplicationConfig.class);
configBean.setApplication(applicationConfig);
}
5.根据注解中的配置属性module装配ModuleConfig
private void configureModuleConfig(C configBean) {
String moduleConfigBeanName = resolveModuleConfigBeanName(attributes);
ModuleConfig moduleConfig =
getOptionalBean(applicationContext, moduleConfigBeanName, ModuleConfig.class);
configBean.setModule(moduleConfig);
}
- 配置referenceBean的interface consumer method属性,调用referenceBean的afterPropertiesSet()方法初始化referenceBean。
@Override
protected void postConfigureBean(AnnotationAttributes attributes, ReferenceBean bean) throws Exception {
bean.setApplicationContext(applicationContext);
//设置服务引用接口
configureInterface(attributes, bean);
configureConsumerConfig(attributes, bean);
//将注解中设置的Method[] methods() 转化为MethodConfigs 设置到ReferenceBean中
configureMethodConfig(attributes, bean);
//初始化ReferenceBean相关的dubbo config beans 并且不会创建服务引用代理proxy reference(后面会创建)
bean.afterPropertiesSet();
}
- 设置referenceBean的Interface
- 如果该服务引用为泛化调用,那么@DubboReference注解中必须配置interfaceName,将注解配置中的InterfaceName作为referenceBean的interface。
- 如果不是泛化引用,则调用resolveServiceInterfaceClass方法垂直向上查找将第一个实现的接口作为referenceBean的interface。
private void configureInterface(AnnotationAttributes attributes, ReferenceBean referenceBean) {
Boolean generic = getAttribute(attributes, "generic");
if (generic != null && generic) {
// it's a generic reference
// 当泛化调用的时候 @reference注解中的String interfaceName()必须设置
String interfaceClassName = getAttribute(attributes, "interfaceName");
Assert.hasText(interfaceClassName,
"@Reference interfaceName() must be present when reference a generic service!");
referenceBean.setInterface(interfaceClassName);
return;
}
//查找被@reference标注的dubbo service类 向上一层一层查找他的第一个实现接口。作为dubbo service的接口
Class<?> serviceInterfaceClass = resolveServiceInterfaceClass(attributes, interfaceClass);
Assert.isTrue(serviceInterfaceClass.isInterface(),
"The class of field or method that was annotated @Reference is not an interface!");
referenceBean.setInterface(serviceInterfaceClass);
}
- 根据注解中的配置属性consumer装配ConsumerConfig
private void configureConsumerConfig(AnnotationAttributes attributes, ReferenceBean<?> referenceBean) {
String consumerBeanName = getAttribute(attributes, "consumer");
ConsumerConfig consumerConfig = getOptionalBean(applicationContext, consumerBeanName, ConsumerConfig.class);
referenceBean.setConsumer(consumerConfig);
}
- 解析注解中的methods属性,将其转换为MethodConfig,设置到referenceBean中。
例如:
@Service(version = AnnotationConstants.VERSION, methods = {@Method(name = "sayGoodbye", timeout = 250, retries = 0)})
public class AnnotationHelloServiceImpl implements HelloService
将注解中标注的@Method[]转化为 List<MethodConfig>
void configureMethodConfig(AnnotationAttributes attributes, ReferenceBean<?> referenceBean) {
Method[] methods = (Method[]) attributes.get("methods");
List<MethodConfig> methodConfigs = MethodConfig.constructMethodConfig(methods);
if (!methodConfigs.isEmpty()) {
referenceBean.setMethods(methodConfigs);
}
}
- 初始化ReferenceBean相关的dubbo config beans
这里并不会创建服务引用代理proxy
//依赖注入@reference引用时 调用该方法
@Override
@SuppressWarnings({"unchecked"})
public void afterPropertiesSet() throws Exception {
// Initializes Dubbo's Config Beans before @Reference bean autowiring
prepareDubboConfigBeans();
// lazy init by default.
//默认延迟初始化
if (init == null) {
init = false;
}
// eager init if necessary.
if (shouldInit()) {
//创建服务引用代理proxy reference
getObject();
}
}
/**
* Initializes there Dubbo's Config Beans before @Reference bean autowiring
*/
private void prepareDubboConfigBeans() {
//返回指定类型和子类型的所有bean,若该bean factory 是一个继承类型的beanFactory,
// 这个方法也会获取ancestorfactory中定义的指定类型的bean。
//间接使这些ConfigBeans初始化
beansOfTypeIncludingAncestors(applicationContext, ApplicationConfig.class);
beansOfTypeIncludingAncestors(applicationContext, ModuleConfig.class);
beansOfTypeIncludingAncestors(applicationContext, RegistryConfig.class);
beansOfTypeIncludingAncestors(applicationContext, ProtocolConfig.class);
beansOfTypeIncludingAncestors(applicationContext, MonitorConfig.class);
beansOfTypeIncludingAncestors(applicationContext, ProviderConfig.class);
beansOfTypeIncludingAncestors(applicationContext, ConsumerConfig.class);
beansOfTypeIncludingAncestors(applicationContext, ConfigCenterBean.class);
beansOfTypeIncludingAncestors(applicationContext, MetadataReportConfig.class);
beansOfTypeIncludingAncestors(applicationContext, MetricsConfig.class);
beansOfTypeIncludingAncestors(applicationContext, SslConfig.class);
}
创建远程服务引用代理
private Object getOrCreateProxy(String referencedBeanName, ReferenceBean referenceBean, boolean localServiceBean,
Class<?> serviceInterfaceType) {
if (localServiceBean) { // If the local @Service Bean exists, build a proxy of Service
//调用本地暴露的服务时 直接反射调用service类的实现 serviceImpl(代理里直接调用实现类的方法 不走那些invoker链)
return newProxyInstance(getClassLoader(), new Class[]{serviceInterfaceType},
newReferencedBeanInvocationHandler(referencedBeanName));
} else {
//暴露协议为inJvm的的时候需要立马暴露,调用服务虽然也是本地,但是需要走filter链
//https://dubbo.apache.org/zh/docs/v2.7/user/examples/local-call/#%E9%85%8D%E7%BD%AE
exportServiceBeanIfNecessary(referencedBeanName); // If the referenced ServiceBean exits, export it immediately
//创建远程服务代理
return referenceBean.get();
}
}
- 如果引用的是远程服务,则通过referenceBean.get()方法获取远程服务代理。
当引用的远程服务暴露的协议为inJvm时,立马暴露该服务。这种情况下虽然调用走的也是本地,但是需要经过filter链。
private void exportServiceBeanIfNecessary(String referencedBeanName) {
if (existsServiceBean(referencedBeanName)) {
ServiceBean serviceBean = getServiceBean(referencedBeanName);
if (!serviceBean.isExported()) {
serviceBean.export();
}
}
}
- 如果引用的是本地服务情况下,利用jdk proxy创建injectedType类型的代理。在代理中反射调用本地服务方法。
在调用本地服务的情况下是需要走那些filter链的。
我们来看下创建proxy时用到的invocationHandler
//缓存referencedBeanName(本地服务暴露的serviceBeanName) 和 用于创建本地serviceBean代理的 invocationHandler之间的映射
private final ConcurrentMap<String, ReferencedBeanInvocationHandler> referencedBeanInvocationHandlersCache =
new ConcurrentHashMap<>();
private class ReferencedBeanInvocationHandler implements InvocationHandler {
private final String referencedBeanName;
private Object bean;
private ReferencedBeanInvocationHandler(String referencedBeanName) {
this.referencedBeanName = referencedBeanName;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
try {
if (bean == null) {
init();
}
//反射调用本地服务实例方法
result = method.invoke(bean, args);
} catch (InvocationTargetException e) {
// re-throws the actual Exception.
throw e.getTargetException();
}
return result;
}
//通过本地暴露的serviceBean的ref属性获取 本地服务实例bean
private void init() {
ServiceBean serviceBean = applicationContext.getBean(referencedBeanName, ServiceBean.class);
this.bean = serviceBean.getRef();
}
}
到这里注解驱动dubbo服务 的内容,就讲完了。这篇文章写的有点长,大家消化的怎么样?欢迎给我反馈。