这是我参与8月更文挑战的第8天,活动详情查看:8月更文挑战
首先分享之前的所有文章 , 欢迎点赞收藏转发三连下次一定 >>>> 😜😜😜
文章合集 : 🎁 juejin.cn/post/694164…
Github : 👉 github.com/black-ant
CASE 备份 : 👉 gitee.com/antblack/ca…
一 . 前言
前一篇看了 Server 段 DubboService 的扫描逻辑 , 这一篇看一下 Dubbo Reference 的扫描逻辑 , 其实是类似的 ,所以文章会偏精简
二 . 扫描的过程
2.1 应用案例
public class DemoServiceComponent implements DemoService {
@DubboReference
private DemoService demoService;
@Override
public String sayHello(String name) {
return demoService.sayHello(name);
}
}
2.2 DubboReference 的扫描
此环节来看一下 , Client 端如何创建一个代理类来代理对应的 @DubboReference 接口的
2.2.1 Reference 扫描的起点
// Reference 扫描的起点为 DubboBootstrap , 其中有这样一段代码
public DubboBootstrap start() {
//.........
// 发起 Refer Services 处理
referServices();
//.........
}
private void referServices() {
if (cache == null) {
cache = ReferenceConfigCache.getCache();
}
// 2.2.2 : configManager 中 reference 是之前扫描出来
configManager.getReferences().forEach(rc -> {
// 为其设置容器 , 并且刷新
ReferenceConfig<?> referenceConfig = (ReferenceConfig<?>) rc;
referenceConfig.setBootstrap(this);
if (!referenceConfig.isRefreshed()) {
referenceConfig.refresh();
}
//............... 后面的逻辑先不关注
});
}
CacheManager 参数详情
2.2.2 References 的扫描过程
下面来看一下 References 是如何扫描出来的
Step 1 : Spring 管理时的创建
在这个环节中 ,对应的类进行 Bean 的初始化 , 而 Reference 起点就是 ReferenceAnnotationBeanPostProcessor 中对 postProcessPropertyValues 进行处理
@Override
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
AnnotatedInjectionMetadata metadata = findInjectionMetadata(beanName, bean.getClass(), pvs);
prepareInjection(metadata);
// bean : DemoServiceComponent 也就是前面的 Bean 实体类
// beanName : demoServiceComponent
metadata.inject(bean, beanName, pvs);
return pvs;
}
来看一下 metadata 的结构 : (此处开始要对成员进行处理了)
Step 2 : 成员变量的反射
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
// 获取其中的成员变量 , 即为 @DubboReference 对象
Object injectedObject = getInjectedObject(attributes, bean, beanName, getInjectedType(), this);
// TODO : 此处后续再看
if (member instanceof Field) {
Field field = (Field) member;
ReflectionUtils.makeAccessible(field);
field.set(bean, injectedObject);
} else if (member instanceof Method) {
Method method = (Method) member;
ReflectionUtils.makeAccessible(method);
method.invoke(bean, injectedObject);
}
}
此处获取注入的 Bean
//
protected Object doGetInjectedBean(AnnotationAttributes attributes, Object bean, String beanName, Class<?> injectedType,
AnnotatedInjectElement injectedElement) throws Exception {
// injectedElement.injectedObject -> demoService
return getBeanFactory().getBean((String) injectedElement.injectedObject);
}
Step 3 : reference 的处理主流程
首先得说一下 getBean 的过程 , 其 createBean 时获取的 mbd 为 ReferenceBean
C- ReferenceBean
public void afterPropertiesSet() throws Exception {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
// INTERFACE_CLASS -> interfaceClass ->
// INTERFACE_NAME -> interfaceName
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(getId());
this.interfaceClass = (Class<?>) beanDefinition.getAttribute(ReferenceAttributes.INTERFACE_CLASS);
this.interfaceName = (String) beanDefinition.getAttribute(ReferenceAttributes.INTERFACE_NAME);
// Constants.REFERENCE_PROPS -> referenceProps
if (beanDefinition.hasAttribute(Constants.REFERENCE_PROPS)) {
// @DubboReference annotation at java-config class @Bean method
// @DubboReference annotation at reference field or setter method
// 可以看到 , 支持 JavaConfig 类和 属性以及 Setter 注入
referenceProps = (Map<String, Object>) beanDefinition.getAttribute(Constants.REFERENCE_PROPS);
} else {
// 此处是不同注册类型的处理方式
if (beanDefinition instanceof AnnotatedBeanDefinition) {
// Return ReferenceBean in java-config class @Bean method
ReferenceBeanSupport.convertReferenceProps(referenceProps, interfaceClass);
if (this.interfaceName == null) {
this.interfaceName = (String) referenceProps.get(ReferenceAttributes.INTERFACE);
}
} else {
// xml reference bean
propertyValues = beanDefinition.getPropertyValues();
}
}
// -> BEAN_NAME = "dubboReferenceBeanManager";
ReferenceBeanManager referenceBeanManager = beanFactory.getBean(ReferenceBeanManager.BEAN_NAME, ReferenceBeanManager.class);
referenceBeanManager.addReference(this);
}
Step 4 : reference 的管理流程
// C- ReferenceBeanManager
public void addReference(ReferenceBean referenceBean) throws Exception {
String referenceBeanName = referenceBean.getId();
PropertyResolver propertyResolver = applicationContext.getEnvironment();
String referenceKey = ReferenceBeanSupport.generateReferenceKey(referenceBean, propertyResolver);
ReferenceBean oldReferenceBean = referenceIdMap.get(referenceBeanName);
// 这里有个细节点 ,通过 != 判断 , 但是!= 比较的是地址引用 ,而不是像 equal 比较的对象
// 这里我理解就是对象地址不同 , 意味着创建了重复的 Bean , 也就是说不能创建同样的实例
if (oldReferenceBean != null) {
if (referenceBean != oldReferenceBean) {
String oldReferenceKey = ReferenceBeanSupport.generateReferenceKey(oldReferenceBean, propertyResolver);
throw new IllegalStateException("Found duplicated ReferenceBean with id: " + referenceBeanName +
", old: " + oldReferenceKey + ", new: " + referenceKey);
}
return;
}
// private Map<String, ReferenceBean> referenceIdMap = new ConcurrentHashMap<>();
// 这是一个通过 id/name 指向 ReferenceBean 的对象
referenceIdMap.put(referenceBeanName, referenceBean);
//save cache, map reference key to referenceBeanName
this.registerReferenceKeyAndBeanName(referenceKey, referenceBeanName);
// if add reference after prepareReferenceBeans(), should init it immediately.
if (initialized) {
initReferenceBean(referenceBean);
}
}
有了 ReferenceBean , 后续要创建 ReferenceConfig 对象了 , 该对象后续会成为 invoke 的基础配置对象
private synchronized void initReferenceBean(ReferenceBean referenceBean) throws Exception {
if (referenceBean.getReferenceConfig() != null) {
return;
}
// reference key -> ReferenceBean:org.apache.dubbo.demo.DemoService()
String referenceKey = ReferenceBeanSupport.generateReferenceKey(referenceBean, applicationContext.getEnvironment());
ReferenceConfig referenceConfig = referenceConfigMap.get(referenceKey);
if (referenceConfig == null) {
// 创建 实际 ReferenceConfig 对象 , 通过 ReferenceCreator 进行创建
// 这里获取到了注解中的属性
Map<String, Object> referenceAttributes = ReferenceBeanSupport.getReferenceAttributes(referenceBean);
referenceConfig = ReferenceCreator.create(referenceAttributes, applicationContext)
.defaultInterfaceClass(referenceBean.getObjectType())
.build();
// 如果不是生成的名称,请设置id
if (referenceBean.getId() != null && !referenceBean.getId().contains("#")) {
referenceConfig.setId(referenceBean.getId());
}
// cache referenceConfig
// private Map<String, ReferenceConfig> referenceConfigMap = new ConcurrentHashMap<>();
referenceConfigMap.put(referenceKey, referenceConfig);
// register ReferenceConfig -> Step 5
DubboBootstrap.getInstance().reference(referenceConfig);
}
// associate referenceConfig to referenceBean
referenceBean.setKeyAndReferenceConfig(referenceKey, referenceConfig);
}
注释很完善 , 整个流程也就很清晰了 , 下面来看一下往Cache 缓存的逻辑
Step 5 : Config Manager 管理
public DubboBootstrap reference(ReferenceConfig<?> referenceConfig) {
// 设置容器
referenceConfig.setBootstrap(this);
// 添加当前Config -> <dubbo:reference id="demoService" />
// 这里很有意思 , 使用的还是 xml 格式 ,我猜测应该是和老版本保存统一 , 会不会是利于升级 ?
configManager.addReference(referenceConfig);
return this;
}
// 调用下方
public void addReference(ReferenceConfigBase<?> referenceConfig)
protected <T extends AbstractConfig> T addConfig(AbstractConfig config, boolean unique) {
// 省略判空逻辑
return (T) write(() -> {
Map<String, AbstractConfig> configsMap = configsCache.computeIfAbsent(getTagName(config.getClass()), type -> newMap());
return addIfAbsent(config, configsMap, unique);
});
}
Step 6 : 使用
这里就可以看到 , 最后已经放在了 Cache 中 , 也就是最开始获取的地方 , 这又是怎么处理出来的呢 ?
三 . 细节补充
3.1 ReferenceBean 的创建
这里getBean 的时候 ,反射出来的是 ReferenceBean , 这其实是 Spring IOC 的相关逻辑
TODO : 还有点多 ,开个单章吧 , 看一下 Dubbo 如何把 Spring 用活的!!!
总结
下一篇来看看一个 Reference 对象是怎么转变为 Invoke 对象的