Dubbo的2.7.7版本中已经标注了Reference和Service注解为@Deprecated弃用了。改用DubboReference和DubboService这两个注解了
我们可以看到注解的方式启动可以使用EnableDubbo注解,这个注解又’继承’了EnableDubboConfig和DubboComponentScan
两个注解。 在EnableDubboConfig注解中的起作用的类是Import引入的DubboConfigConfigurationRegistrar类。
注解的继承, 是Spring的一种技巧。在注解上添加注解就实现了类似的一个注解继承另外一个注解的能力
可以看到方法的最后一行, registerCommonBeans
方法. 进入其中可以看到
方法内部会放入下面五个类到容器中,如果容器中不存在这些类才会放入:
其中ReferenceAnnotationBeanPostProcessor类就是与DubboReference注解息息相关的类
可以看到构造方法传入了两个注解的信息到父类中, 实际上这两个注解就是待会ReferenceAnnotationBeanPostProcessor需要处理的注解.
这个类继承自AbstractAnnotationBeanPostProcessor类,继续往上追查查看源码
发现它实现InstantiationAwareBeanPostProcessor接口,这个InstantiationAwareBeanPostProcessor
也是一个十分重要的类,这个类是在bean实例化的前后进行一些操作的, dubbo就是使用这个InstantiationAwareBeanPostProcessor接口的com.alibaba.spring.beans.factory.annotation.AbstractAnnotationBeanPostProcessor#postProcessPropertyValues
方法在bean实例化后注入对象到字段的。
实际上Spring的Autowire也是使用的这个方式实现的功能。
InstantiationAwareBeanPostProcessor 的知识可以参考Spring的Autowire注解来功能实现类AutowiredAnnotationBeanPostProcessor类来理解。
要理解Spring的bean的生命周期,区分实例化和初始化的区别。BeanPostProcessor接口是只有初始化前后的调用, InstantiationAwareBeanPostProcessor 接口增加了三个方法,作用分别是"实例化前调用",“实例化后调用” 和 “注入字段值”. 而Dubbo的Reference和Spring的Autowire注解就是使用了第三方法来实现注入字段值的。
在实例化后会调用InstantiationAwareBeanPostProcessor接口的postProcessPropertyValues方法,并最终会调用到ReferenceAnnotationBeanPostProcessor#doGetInjectedBean
方法。 具体的调用栈如下图所示:
我们看下postProcessPropertyValues的实现逻辑:
我们看下实现逻辑中的找到符合要求的字段的实现
findInjectionMetadata方法的源码如下:
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 {
//查找符合要求的逻辑主要在这个方法里面。 上面的一些逻辑都是一些如何从缓存中获取字段信息的逻辑,可以忽略不看
metadata = buildAnnotatedMetadata(clazz);
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方法中查看
private AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata buildAnnotatedMetadata(final Class<?> beanClass) {
//通过下面的方法名就可以知道是找到符合注入要求的字段
Collection<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement> fieldElements = findFieldAnnotationMetadata(beanClass);
//通过下面的方法名就可以知道是找到符合注入要求的方法
Collection<AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement> methodElements = findAnnotatedMethodMetadata(beanClass);
return new AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata(beanClass, fieldElements, methodElements);
}
继续深入到findFieldAnnotationMetadata方法中:
这里如何筛选要注入字段就很清楚了, 就是获取到Bean中有指定注解的字段。而指定的注解就是在构造放入中传入的
注入字段值的方法调用堆栈图就在上面。 从堆栈图中知道,最后会调用ReferenceAnnotationBeanPostProcessor#doGetInjectedBean
方法。
这个方法的作用就是:获取要注入字段的值。 我们看下dubbo的实现:
doGetInjectedBean方法的7步流程如上, 实际上最重要的流程就是第7步
referenceBean.get()
这一行. referenceBean继承了FactoryBean接口,所以这里调用的是FactoryBean的get方法。
主要逻辑都在init方法中, init方法才是Reference注解实现的真正核心部分, 代码量有点多,下面全部贴出
public synchronized void init() {
if (initialized) {
return;
}
if (bootstrap == null) {
bootstrap = DubboBootstrap.getInstance();
bootstrap.init();
}
checkAndUpdateSubConfigs();
checkStubAndLocal(interfaceClass);
ConfigValidationUtils.checkMock(interfaceClass, this);
Map<String, String> map = new HashMap<String, String>();
map.put(SIDE_KEY, CONSUMER_SIDE);
ReferenceConfigBase.appendRuntimeParameters(map);
if (!ProtocolUtils.isGeneric(generic)) {
String revision = Version.getVersion(interfaceClass, version);
if (revision != null && revision.length() > 0) {
map.put(REVISION_KEY, revision);
}
String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
if (methods.length == 0) {
logger.warn("No method found in service interface " + interfaceClass.getName());
map.put(METHODS_KEY, ANY_VALUE);
} else {
map.put(METHODS_KEY, StringUtils.join(new HashSet<String>(Arrays.asList(methods)), COMMA_SEPARATOR));
}
}
map.put(INTERFACE_KEY, interfaceName);
AbstractConfig.appendParameters(map, getMetrics());
AbstractConfig.appendParameters(map, getApplication());
AbstractConfig.appendParameters(map, getModule());
// remove 'default.' prefix for configs from ConsumerConfig
// appendParameters(map, consumer, Constants.DEFAULT_KEY);
AbstractConfig.appendParameters(map, consumer);
AbstractConfig.appendParameters(map, this);
MetadataReportConfig metadataReportConfig = getMetadataReportConfig();
if (metadataReportConfig != null && metadataReportConfig.isValid()) {
map.putIfAbsent(METADATA_KEY, REMOTE_METADATA_STORAGE_TYPE);
}
Map<String, AsyncMethodInfo> attributes = null;
if (CollectionUtils.isNotEmpty(getMethods())) {
attributes = new HashMap<>();
for (MethodConfig methodConfig : getMethods()) {
AbstractConfig.appendParameters(map, methodConfig, methodConfig.getName());
String retryKey = methodConfig.getName() + ".retry";
if (map.containsKey(retryKey)) {
String retryValue = map.remove(retryKey);
if ("false".equals(retryValue)) {
map.put(methodConfig.getName() + ".retries", "0");
}
}
AsyncMethodInfo asyncMethodInfo = AbstractConfig.convertMethodConfig2AsyncInfo(methodConfig);
if (asyncMethodInfo != null) {
// consumerModel.getMethodModel(methodConfig.getName()).addAttribute(ASYNC_KEY, asyncMethodInfo);
attributes.put(methodConfig.getName(), asyncMethodInfo);
}
}
}
String hostToRegistry = ConfigUtils.getSystemProperty(DUBBO_IP_TO_REGISTRY);
if (StringUtils.isEmpty(hostToRegistry)) {
hostToRegistry = NetUtils.getLocalHost();
} else if (isInvalidLocalHost(hostToRegistry)) {
throw new IllegalArgumentException("Specified invalid registry ip from property:" + DUBBO_IP_TO_REGISTRY + ", value:" + hostToRegistry);
}
map.put(REGISTER_IP_KEY, hostToRegistry);
serviceMetadata.getAttachments().putAll(map);
ref = createProxy(map);
serviceMetadata.setTarget(ref);
serviceMetadata.addAttribute(PROXY_CLASS_REF, ref);
ConsumerModel consumerModel = repository.lookupReferredService(serviceMetadata.getServiceKey());
consumerModel.setProxyObject(ref);
consumerModel.init(attributes);
initialized = true;
// dispatch a ReferenceConfigInitializedEvent since 2.7.4
dispatch(new ReferenceConfigInitializedEvent(this, invoker));
}
真正生成代理类的是createProxy方法, 具体如何生成代理对象的是使用的Dubbo的SPI机制导入的协议和实现类来生成的, 具体的详情会在后面的dubbo的SPI机制中详细讲解。 这里就不再说明。
dubbo 使用ReferenceAnnotationBeanPostProcessor类在每个bean示例化之前获取到bean中Reference注解的字段,并且为这个字段生成代理类ReferenceBean. 具体的dubbo远程调用逻辑实际上是在这个代理类中完成的。