dubbo系列之@Reference注解解析原理(五)

621 阅读5分钟

欢迎关注公众号【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】致力于主流中间件的源码分析, 可以直接与我联系