欢迎关注公众号【sharedCode】致力于主流中间件的源码分析, 可以直接与我联系
前言
上文中我们讲解了@Service注解的解析原理,了解到Dubbo默认支持两种方式进行解析,一种是通过springboot 自动配置来做的,另外一种是通过DubboComponentScan
注解来解析的,本文继续也是以DubboComponentScan
的方式来讲解的。
源码入口
com.alibaba.dubbo.config.spring.context.annotation.DubboComponentScanRegistrar
Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 获取扫描包路径
Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
// 注册@service解析的类
registerServiceAnnotationBeanPostProcessor(packagesToScan, registry);
// 注册解析@Reference注解的bean
registerReferenceAnnotationBeanPostProcessor(registry);
}
registerReferenceAnnotationBeanPostProcessor
private void registerReferenceAnnotationBeanPostProcessor(BeanDefinitionRegistry registry) {
// Register @Reference Annotation Bean Processor
BeanRegistrar.registerInfrastructureBean(registry,
ReferenceAnnotationBeanPostProcessor.BEAN_NAME, ReferenceAnnotationBeanPostProcessor.class);
}
调用了BeanRegistrar工具类来注册Reference解析器的BeanDefinition, registerInfrastructureBean方法的主要作用就是将ReferenceAnnotationBeanPostProcessor这个类注册到BeanDefinition
public class BeanRegistrar {
/**
* Register Infrastructure Bean
*
* @param beanDefinitionRegistry {@link BeanDefinitionRegistry}
* @param beanType the type of bean
* @param beanName the name of bean
*/
public static void registerInfrastructureBean(BeanDefinitionRegistry beanDefinitionRegistry,
String beanName,
Class<?> beanType) {
if (!beanDefinitionRegistry.containsBeanDefinition(beanName)) {
RootBeanDefinition beanDefinition = new RootBeanDefinition(beanType);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
// 注册
beanDefinitionRegistry.registerBeanDefinition(beanName, beanDefinition);
}
}
}
ReferenceAnnotationBeanPostProcessor
本文的重点在于ReferenceAnnotationBeanPostProcessor类,该类继承了InstantiationAwareBeanPostProcessor
,用来解析@Reference注解并完成依赖注入。
public class ReferenceAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter
implements MergedBeanDefinitionPostProcessor, PriorityOrdered, ApplicationContextAware, BeanClassLoaderAware,
DisposableBean {
// 省略注解
}
**InstantiationAwareBeanPostProcessor **:实例化Bean后置处理器(继承BeanPostProcessor)
1.postProcessBeforeInstantiation :在实例化目标对象之前执行,可以自定义实例化逻辑,如返回一个代理对象等。
2.postProcessAfterInitialization : Bean实例化完毕后执行的后处理操作,所有初始化逻辑、装配逻辑之前执行,如果返回false将阻止其他的InstantiationAwareBeanPostProcessor的postProcessAfterInstantiation的执行。
3.postProcessPropertyValues :完成其他定制的一些依赖注入和依赖检查等,如AutowiredAnnotationBeanPostProcessor执行@Autowired注解注入,CommonAnnotationBeanPostProcessor执行@Resource等注解的注入,PersistenceAnnotationBeanPostProcessor执行@ PersistenceContext等JPA注解的注入,RequiredAnnotationBeanPostProcessor执行@ Required注解的检查等等。
dubbo也是采用了和@Autowired注入一样的原理,通过继承InstantiationAwareBeanPostProcessor
重写postProcessPropertyValues 方法来达到解析@Reference并实现依赖注入。
@Override
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
//这个是注入元数据,包含了目标Bean的Class对象,和注入元素(InjectionElement)集合
InjectionMetadata metadata = findReferenceMetadata(beanName, bean.getClass(), pvs);
try {
// 通过反射来给bean设置值了
metadata.inject(bean, beanName, pvs);
} catch (BeanCreationException ex) {
throw ex;
} catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of @Reference dependencies failed", ex);
}
return pvs;
}
通过findReferenceMetadata找到@Reference,并解析得到元数据对象,最终实现依赖注入,@Autowired注解也是这个干的,二者的实现原理是一模一样。
private InjectionMetadata findReferenceMetadata(String beanName, Class<?> clazz, PropertyValues pvs) {
// 通过类名作为缓存的key
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
// 从缓存中的injectionMetadataCache根据类名获取元数据
ReferenceInjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
// 判断metadata 是否为空, class对象不等于ReferenceInjectionMetadata , 则需要进行刷新
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 {
// 构建InjectionMetadata元数据
metadata = buildReferenceMetadata(clazz);
this.injectionMetadataCache.put(cacheKey, metadata);
} catch (NoClassDefFoundError err) {
throw new IllegalStateException("Failed to introspect bean class [" + clazz.getName() +
"] for reference metadata: could not find class that it depends on", err);
}
}
}
}
return metadata;
}
buildReferenceMetadata
private ReferenceInjectionMetadata buildReferenceMetadata(final Class<?> beanClass) {
// 获取属性上的@Reference注解
Collection<ReferenceFieldElement> fieldElements = findFieldReferenceMetadata(beanClass);
// 获取方法上的@Reference注解
Collection<ReferenceMethodElement> methodElements = findMethodReferenceMetadata(beanClass);
return new ReferenceInjectionMetadata(beanClass, fieldElements, methodElements);
}
获取属性上的@Reference注解findFieldReferenceMetadata
private List<ReferenceFieldElement> findFieldReferenceMetadata(final Class<?> beanClass) {
final List<ReferenceFieldElement> elements = new LinkedList<ReferenceFieldElement>();
// 通过反射的工具类,获取当前beanClass的所有Filed
ReflectionUtils.doWithFields(beanClass, new ReflectionUtils.FieldCallback() {
@Override
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
// 获取Reference注解
Reference reference = getAnnotation(field, Reference.class);
// 注解不为空
if (reference != null) {
if (Modifier.isStatic(field.getModifiers())) {
if (logger.isWarnEnabled()) {
logger.warn("@Reference annotation is not supported on static fields: " + field);
}
return;
}
// 构建ReferenceFieldElement
elements.add(new ReferenceFieldElement(field, reference));
}
}
});
return elements;
}
上面的代码就很简单了,通过ReflectionUtils工具类,反射获取当前beanClass 的所有Filed , 之后获取每个filed上的@Reference注解,如果获取不为空,则继续下一步。最终构建ReferenceFieldElement对象,将对应的filed和Reference注解放进去。
元数据收集好了,接下来就是调用metadata.inject(bean, beanName, pvs);这个方法了。
public void inject(Object target, String beanName, PropertyValues pvs) throws Throwable {
// 获取InjectedElement
Collection<InjectionMetadata.InjectedElement> elementsToIterate = this.checkedElements != null ? this.checkedElements : this.injectedElements;
if (!((Collection)elementsToIterate).isEmpty()) {
boolean debug = logger.isDebugEnabled();
// 进行循环,也就是循环设值,因为有多个字段嘛。
InjectionMetadata.InjectedElement element;
for(Iterator var6 = ((Collection)elementsToIterate).iterator(); var6.hasNext(); element.inject(target, beanName, pvs)) {
element = (InjectionMetadata.InjectedElement)var6.next();
if (debug) {
logger.debug("Processing injected element of bean '" + beanName + "': " + element);
}
}
}
}
上面的代码,其实只有一行,那就是element.inject(target, beanName, pvs) 这一行,因为一个InjectedElement对象就表示一个字段对象,这个对象中将字段信息和注解信息绑定在了一起,调用inject方法就是为了给这个filed进行赋值。
###ReferenceFieldElement
private class ReferenceFieldElement extends InjectionMetadata.InjectedElement {
// 字段对象
private final Field field;
// Reference注解对象
private final Reference reference;
// 服务引用对象
private volatile ReferenceBean<?> referenceBean;
protected ReferenceFieldElement(Field field, Reference reference) {
super(field, null);
this.field = field;
this.reference = reference;
}
@Override
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
// 获取字段的类型
Class<?> referenceClass = field.getType();
// 构建ReferenceBean
referenceBean = buildReferenceBean(reference, referenceClass);
// 字段为私有,需要设置这个属性field.setAccessible(true) 才能进行设值
ReflectionUtils.makeAccessible(field);
// 给这个对象bean的这个filed设置值,值为:referenceBean.getObject()
field.set(bean, referenceBean.getObject());
}
}
通过buildReferenceBean方法创建服务引用对象
private ReferenceBean<?> buildReferenceBean(Reference reference, Class<?> referenceClass) throws Exception {
// 获取服务引用对象的缓存key
String referenceBeanCacheKey = generateReferenceBeanCacheKey(reference, referenceClass);
// 从缓存map中获取服务引用对象
ReferenceBean<?> referenceBean = referenceBeansCache.get(referenceBeanCacheKey);
if (referenceBean == null) {
// 如果引用对象为空,则需要当场创建一个
ReferenceBeanBuilder beanBuilder = ReferenceBeanBuilder
.create(reference, classLoader, applicationContext)
.interfaceClass(referenceClass);
referenceBean = beanBuilder.build();
//并且放入到缓存map中。
referenceBeansCache.putIfAbsent(referenceBeanCacheKey, referenceBean);
}
return referenceBean;
}
private String generateReferenceBeanCacheKey(Reference reference, Class<?> beanClass) {
// 获取接口名称
String interfaceName = resolveInterfaceName(reference, beanClass);
// 通过引用的URl+接口名+接口版本号+接口分组,用来做缓存key
String key = reference.url() + "/" + interfaceName +
"/" + reference.version() +
"/" + reference.group();
Environment environment = applicationContext.getEnvironment();
key = environment.resolvePlaceholders(key);
return key;
}
消费者每引用的一种服务,都会创建一个ReferenceBean, 如果多个地方使用@Reference引用同一个服务,需要看他们的的缓存key是否一样,如果都是一样的,那么就只会创建一个ReferenceBean,如果有些配置不一样,比如版本号不一致,则会创建创建不同的ReferenceBean
对象,这也是他版本号能够起到的作用把。至此,@Reference
注解已经解析完毕,并且服务引用的对象也已经创建了。
源码讲到这里,相信不少人心中有个疑问,虽然现在已经把@Reference注解解析出来了,并且ReferenceBean
创建了,但是为啥ReferenceBean
对象就能够调用到远程服务呢? 上一章讲的,ServiceBean
虽然创建了,但是这个服务是如何暴露到zookeeper
上去的呢? 又是如何下线的呢?
带着这些问题,笔者要继续读源码了,接下来源码分析,会慢慢的转向具体的功能。
欢迎关注公众号【sharedCode】致力于主流中间件的源码分析, 可以直接与我联系
来源:oschina
链接:https://my.oschina.net/u/4845780/blog/4742651