上一篇文章围绕 @EnableDubbo 进行了深入的分析,本篇文章将重点看@Service和@Reference 原理。
与上面两个注解相关联两个Bean类分别为:
ServiceAnnotationBeanPostProcessorReferenceAnnotationBeanPostProcessor
ServiceAnnotationBeanPostProcessor
上一篇文章中,ServiceAnnotationBeanPostProcessor 是在 DubboComponentScan 中,在 DubboComponentScanRegistrar 中,被注册到Spring 容器中。
public class ServiceAnnotationBeanPostProcessor implements BeanDefinitionRegistryPostProcessor, EnvironmentAware,
ResourceLoaderAware, BeanClassLoaderAware {
...
}
ServiceAnnotationBeanPostProcessor 最核心的角色是 一个 BeanDefinitionRegistryPostProcessor,这样能够在refresh中对BeanFactoryPostProcessor处理时,对 Spring 容器中bean定义进行删改。
里面主要方法为 postProcessBeanDefinitionRegistry:
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// 注册 DubboBootstrapApplicationListener
registerBeans(registry, DubboBootstrapApplicationListener.class);
// 获取扫描路径
Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);
if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
// 具体扫描
registerServiceBeans(resolvedPackagesToScan, registry);
} else {
if (logger.isWarnEnabled()) {
logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
}
}
}
上面方法则中,普通获取扫描包路径,最终调用 registerServiceBeans 进行扫描:
private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
// 构建一个 DubboClassPathBeanDefinitionScanner,父类为 Spring 的 ClassPathBeanDefinitionScanner
DubboClassPathBeanDefinitionScanner scanner =
new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
// 获取一个beanGenerator
BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);
scanner.setBeanNameGenerator(beanNameGenerator);
// 添加过滤类,即只扫描@Service注解的类。
scanner.addIncludeFilter(new AnnotationTypeFilter(Service.class));
// 添加对旧版本支持
scanner.addIncludeFilter(new AnnotationTypeFilter(com.alibaba.dubbo.config.annotation.Service.class));
for (String packageToScan : packagesToScan) {
// 扫描,并且会将@Service 注解的类都注册进Spring
scanner.scan(packageToScan);
// 获取所有的 @Service 的 BeanDefinitionHolders
Set<BeanDefinitionHolder> beanDefinitionHolders =
findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);
if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
// 将上一步中获取到的所有 @Service 注解bean,都注册到ServiceBean类型到 Spirng中
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 + "]");
}
}
}
}
上面方法中,主要有以下几步操作:
- 构建一个
DubboClassPathBeanDefinitionScanner,父类为Spring的ClassPathBeanDefinitionScanner,这是Spring提供出来一个通用扫描器,其内置的@Conponent扫描,以及Mybatis中扫描,也是以这个类为主。 - 将 所有扫描出 的
@ServiceBean类,通过registerServiceBean注册进成为Spring的Bean。
registerServiceBean :
private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry,
DubboClassPathBeanDefinitionScanner scanner) {
// 获取到BeanClass
Class<?> beanClass = resolveClass(beanDefinitionHolder);
Annotation service = findServiceAnnotation(beanClass);
// 获取 @Service 注解的参数
AnnotationAttributes serviceAnnotationAttributes = getAnnotationAttributes(service, false, false);
// 获取接口名
Class<?> interfaceClass = resolveServiceInterfaceClass(serviceAnnotationAttributes, beanClass);
// 获取ServiceBean名字
String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();
// 构造一个 serviceBean
AbstractBeanDefinition serviceBeanDefinition =
buildServiceBeanDefinition(service, serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName);
// ServiceBean Bean name
String beanName = generateServiceBeanName(serviceAnnotationAttributes, interfaceClass);
if (scanner.checkCandidate(beanName, serviceBeanDefinition)) {
// 如果没有注册过,那么久进行注册
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?");
}
}
}
上面注册方法中,包括以下三个逻辑:
- 获取
@Service参数 - 获取
ServiceBean名字 - 构造一个
ServiceBean类型BeanDefinition - 将
ServiceBean注册进Spring容器
整个过程最终要的就是 ServiceBean 构造过程,即 buildServiceBeanDefinition 方法:
private AbstractBeanDefinition buildServiceBeanDefinition(Annotation serviceAnnotation,
AnnotationAttributes serviceAnnotationAttributes,
Class<?> interfaceClass,
String annotatedServiceBeanName) {
BeanDefinitionBuilder builder = rootBeanDefinition(ServiceBean.class);
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
String[] ignoreAttributeNames = of("provider", "monitor", "application", "module", "registry", "protocol",
"interface", "interfaceName", "parameters");
propertyValues.addPropertyValues(new AnnotationPropertyValuesAdapter(serviceAnnotation, environment, 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");
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();
}
围绕两个问题说明:
@Service注解 的bean,最终注入到Spring中,是以ServiceBean方式存在。- 注册进Spring时候,需要将对应属性填充到
BeanDefinition中,大概有以下:
- 包括基本的组件
"provider", "monitor", "application", "module", "registry", "protocol","interface", "interfaceName", "parameters" - ref 属性,默认为实现类名字
- interface属性,暴露接口对应的接口类
- parameters 属性
- methods 属性
- provider 属性,即 ProviderConfig,即确定使用哪一份ProviderConfig
- monitor 属性,同上,可以指定哪一份配置
- application 属性
- module 属性
- registry(registries) 属性
- protocol(protocols) 属性
最终,这就是一个 @Service 在Spring 中初始化过程,但是似乎还少了些什么,读过博主以前博客同学应该知道,Dubbo的接口暴露过程远不止这些,最重要的还要执行export方法呀。
其实这个问题的答案,在上一篇文章最后已经给出,在Spring容器完全初始化完之后,由 DubboBootstrap 中 exportServices 将所有ServiceBean 执行其 export 方法。
还有一个问题,如果使用 @Service 暴露后,在本地,可以使用 @Autowired 之类的注入注解 引用到吗?
答案是可以,因为Dubbo会往Spring中注入两种类型bean:
- 对应接口 实现类的bean(扫描时注入)
ServiceBean类型bean
ReferenceAnnotationBeanPostProcessor
ReferenceAnnotationBeanPostProcessor 是为了处理 @Reference 注解,运行原理和 AutowiredAnnotationBeanPostProcessor (处理@Autowired)很相似。
public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBeanPostProcessor implements
ApplicationContextAware, ApplicationListener {
...
}
对应的父类为:
public abstract class AbstractAnnotationBeanPostProcessor extends
InstantiationAwareBeanPostProcessorAdapter implements MergedBeanDefinitionPostProcessor, PriorityOrdered,
BeanFactoryAware, BeanClassLoaderAware, EnvironmentAware, DisposableBean {
}
ReferenceAnnotationBeanPostProcessor 关键的接口超类为: InstantiationAwareBeanPostProcessorAdapter 和 MergedBeanDefinitionPostProcessor。
从调用关系来看,MergedBeanDefinitionPostProcessor 在实例化前被调用(具体原因看博主前面文章哦),postProcessMergedBeanDefinition 优先调用,先看其方法:
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
if (beanType != null) {
// 获取所有的@Reference注解字段并缓存
InjectionMetadata metadata = findInjectionMetadata(beanName, beanType, null);
// 校验
metadata.checkConfigMembers(beanDefinition);
}
}
上面方法中,重要的是 findInjectionMetadata 方法,主要目的:
- 扫描所有非static 的字段和方法,并进行缓存。
这一步和@Autowired的AutowiredAnnotationBeanPostProcessor处理逻辑一样,可以参考博主以前文章:
https://blog.csdn.net/anLA_/article/details/104507941
当在每个Bean实例化前,找完其所有的@Reference 注解过的字段和方法后,就进入到 InstantiationAwareBeanPostProcessorAdapter 的作用范围了。
InstantiationAwareBeanPostProcessorAdapter 的 postProcessPropertyValues 会在实例化时被调用:
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
InjectionMetadata metadata = findInjectionMetadata(beanName, bean.getClass(), pvs);
try {
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;
}
上面就是实例化之后,具体的初始化动作中执行,还是那个方法 findInjectionMetadata 获取到 InjectionMetadata, 而后进行注入。
对 获取到的 InjectMetadata 进行注入:
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
Collection<InjectedElement> checkedElements = this.checkedElements;
Collection<InjectedElement> elementsToIterate =
(checkedElements != null ? checkedElements : this.injectedElements);
if (!elementsToIterate.isEmpty()) {
for (InjectedElement element : elementsToIterate) {
if (logger.isTraceEnabled()) {
logger.trace("Processing injected element of bean '" + beanName + "': " + element);
}
element.inject(target, beanName, pvs);
}
}
}
此处的 InjectedElement 包括 两种类型 AnnotatedMethodElement 和 AnnotatedFieldElement。这两种都是在 findInjectionMetadata 已经获取到的数据。
以 AnnotatedFieldElement 为例:
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();
Object injectedObject = getInjectedObject(attributes, bean, beanName, injectedType, this);
ReflectionUtils.makeAccessible(field);
field.set(bean, injectedObject);
}
}
对应 inject 方法逻辑,首先是获取对应类型,而后调用具体子类的 getInjectedObject 获取bean实例,最后反射调用set方法注入值。
在 AbstactAnnotationBeanPostProcessor 的 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) {
// 调用子类创建
injectedObject = doGetInjectedBean(attributes, bean, beanName, injectedType, injectedElement);
// Customized inject-object if necessary
injectedObjectsCache.putIfAbsent(cacheKey, injectedObject);
}
return injectedObject;
}
最终还是回到了 ReferenceAnnotationBeanPostProcessor,看它的 doGetInjectedBean 方法:
protected Object doGetInjectedBean(AnnotationAttributes attributes, Object bean, String beanName, Class<?> injectedType,
InjectionMetadata.InjectedElement injectedElement) throws Exception {
// 创建一个ServiceBean对应名字,例如 ServiceBean:com.anla.rpc.configcenter.provider.service.HelloService:1.0.0
String referencedBeanName = buildReferencedBeanName(attributes, injectedType);
// 生成一个@Reference bean对应名字,如果有id属性,则直接用id属性替代。 @Reference(check=false,version=1.0.0) com.anla.rpc.configcenter.provider.service.HelloService
String referenceBeanName = getReferenceBeanName(attributes, injectedType);
// 以 injectedElement 为蓝本,创建一个 @ReferenceBean实例
ReferenceBean referenceBean = buildReferenceBeanIfAbsent(referenceBeanName, attributes, injectedType);
// 注册到Spring中
registerReferenceBean(referencedBeanName, referenceBean, attributes, injectedType);
// 缓存一份bean实例
cacheInjectedReferenceBean(referenceBean, injectedElement);
// 返回
return getOrCreateProxy(referencedBeanName, referenceBeanName, referenceBean, injectedType);
}
上面方法有以下几点关键逻辑:
buildReferenceBeanIfAbsent方法里面其实也做了挺多复杂逻辑,包括检查依赖,检查Spring 各层级组件等:
ReferenceBeanBuilder的 build方法
public final C build() throws Exception {
checkDependencies();
C configBean = doBuild();
configureBean(configBean);
if (logger.isInfoEnabled()) {
logger.info("The configBean[type:" + configBean.getClass().getSimpleName() + "] has been built.");
}
return configBean;
}
- 构建出对应的
@Service方法的bean名字,用于判断是否@Reference的对象就在本地,如果就在本地,则注册一个@Reference对象的别名。这一点在registerReferenceBean有体现:
private void registerReferenceBean(String referencedBeanName, ReferenceBean referenceBean,
AnnotationAttributes attributes,
Class<?> interfaceClass) {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
String beanName = getReferenceBeanName(attributes, interfaceClass);
if (existsServiceBean(referencedBeanName)) { // If @Service bean is local one
/**
* Get the @Service's BeanDefinition from {@link BeanFactory}
* Refer to {@link ServiceAnnotationBeanPostProcessor#buildServiceBeanDefinition}
*/
AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition) beanFactory.getBeanDefinition(referencedBeanName);
RuntimeBeanReference runtimeBeanReference = (RuntimeBeanReference) beanDefinition.getPropertyValues().get("ref");
// The name of bean annotated @Service
String serviceBeanName = runtimeBeanReference.getBeanName();
// register Alias rather than a new bean name, in order to reduce duplicated beans
beanFactory.registerAlias(serviceBeanName, beanName);
} else { // Remote @Service Bean
if (!beanFactory.containsBean(beanName)) {
beanFactory.registerSingleton(beanName, referenceBean);
}
}
- 如果不是在本地,则直接注入单例到Spring容器中。
beanFactory.registerSingleton(beanName, referenceBean); - 缓存注入的bean,以及inject对象。
- 在最后一步的
getOrCreateProxy中,仍然会以 是否就是@Reference本地对象为基础:
private Object getOrCreateProxy(String referencedBeanName, String referenceBeanName, ReferenceBean referenceBean, Class<?> serviceInterfaceType) {
if (existsServiceBean(referencedBeanName)) { // If the local @Service Bean exists, build a proxy of ReferenceBean
// 如果存在@Service对象,则返回一个代理对象
return newProxyInstance(getClassLoader(), new Class[]{serviceInterfaceType},
wrapInvocationHandler(referenceBeanName, referenceBean));
} else {
// 默认是立刻获取
return referenceBean.get();
}
referenceBean.get(); 就是dubbo接口的refer过程,这个看博主dubbo系列文章即可清楚。
ReferenceBean 本身是一个 FactoryBean 类型,其bean实例可以动态制定,主要在 getObject方法中体现:
public Object getObject() {
return get();
}
整个 @Service 暴露和 @Reference 注入原理过程即已讲清楚,
但是感觉目前最新版本(2.7.7-SNAPSHOT) 在 @Reference 处理上,还有点小缺陷,已经有了处理,打算提交Issue和PR反馈给社区,等博主后面文章具体分析。
觉得博主写的有用,不妨关注博主公众号: 六点A君。
哈哈哈,一起研究Spring:

本文深入解析Dubbo框架中@Service和@Reference注解的工作原理。详细介绍了ServiceAnnotationBeanPostProcessor如何处理@Service注解,将其实例化为ServiceBean,并注册到Spring容器中。同时,阐述了ReferenceAnnotationBeanPostProcessor如何解析@Reference注解,创建和注册ReferenceBean,以及在本地和服务端调用的不同处理方式。
Dubbo 与Spring 集成 - @Reference和@Service 原理&spm=1001.2101.3001.5002&articleId=105455483&d=1&t=3&u=22d4bd66abb14094bf15e7b4f3c85f11)
4320

被折叠的 条评论
为什么被折叠?



