应该算是非常简单的HSF框架转Dubbo框架方案
为什么会这么说呢
因为你的代码改动是最少的,只增加了两个依赖却可以让你的项目从HSF框架轻松转移到Dubbo框架。
经历
阿里巴巴集团业务广泛,涵盖多个领域。在电商领域,拥有淘宝、天猫等核心平台,提供丰富的商品选择和便捷的购物体验。金融领域,通过蚂蚁集团布局支付、贷款、理财等,支付宝成为主流移动支付工具。云计算与大数据方面,阿里云提供全面的云计算服务,助力企业数字化转型。物流领域,菜鸟网络整合物流资源,提升物流效率。此外,阿里巴巴还积极布局新零售、智慧零售,推动线上线下融合,并致力于国际化战略,拓展全球市场。这些业务共同构成了阿里巴巴庞大的商业生态系统。
普通人平常接触更多的则是购物,秉持着“让天下没有难做的生意”这一核心理念,成功引领了全中国普通小商家迈入全新的销售模式。而其旗下的淘宝平台,更是让全国人民享受到了足不出户便能尽收天下好物的极致便利。
作为一个程序员,我对于阿里云平台有着更为深入的了解。阿里云平台作为阿里巴巴集团旗下的云计算服务提供商,凭借其强大的技术实力和丰富的产品线,为广大企业和开发者提供了稳定、安全、高效的云计算解决方案。无论是弹性计算、存储与CDN、数据库、网络与安全,还是大数据、人工智能等领域,阿里云平台都展现出了其卓越的技术实力和市场竞争力。
2017年下半年我开始接触淘宝团队打造的HSF框架,作为项目的分布式框架,有太多的东西需要学习。不过,好在有同事的帮助下,很快了解了此框架是如何在项目中配置的,慢慢地对其源码也产生了好奇。在好奇心的作用下,利用开发工具可以下载源码的功能试图下载源码,可惜的是下载不下来,经过多次百度查询得知HSF框架属于私有框架。
刚开始接触HSF框架的时候正处于公司从Dubbo转换HSF的结尾期,大概是一开始使用Dubbo框架后来公司接入阿里私有云后就切换到了HSF框架,当我接手的时候已经就是HSF框架了,只不过新项目的时候偶尔还能看到Dubbo的影子(应该是之前的项目模板遗留下来的)。
算是从今年开始,看到公司内部有开始迁移分布式框架的动向,发现改动原始代码的代价有点高,再加上本人喜欢研究源码,本着能不动原来的代码尽量就不动原来的代码的原则,准备将HSF框架无缝对接到Dubbo框架。
动手
由于HSF框架的封闭性,从现有的框架代码上动手就有些难搞了,幸好阿里云官方已经实现了HSF、Dubbo注册中心双注册的实现方案,再加上这块实现方案是公开的,源码可以找到,因此,在改造的路上就借鉴了双注册的部分逻辑。
方向
随着SpringBoot的火爆以及公司内部封装了基于HSF的SpringBoot框架,因此,此次改动的目标是基于内部封装的框架由HSF无缝迁移到Dubbo。
SpringBoot版HSF叫做PandoraBoot,基于注解的形式完成HSF注册和消费,因此,着手点首先要从@HSFProvider和@HSFConsumer两个注解开始。
1. @HSFProvider
入口文件HSFProviderAnnotationRegistrar
,由于实现了ImportBeanDefinitionRegistrar
接口,启动项目会通过registerBeanDefinitions
方法注册bean,具体逻辑如下:
HSFProvider HSFProvider = (HSFProvider)AnnotationUtils.findAnnotation(targetClass, HSFProvider.class);
// 省略部分代码...
HSFProviderBeanDefinitionBuilder builder = (new HSFProviderBeanDefinitionBuilder(targetBeanName, HSFProvider)).clazz(targetClass).properties(HSFProperties);
BeanDefinition beanDefinition = builder.build(registry);
if (beanDefinition != null) {
if (registry.containsBeanDefinition(beanName)) {
throw new BeanDefinitionValidationException("BeanDefinition with the same beanName already existed, please check your config! beanName:" + beanName);
}
registry.registerBeanDefinition(beanName, beanDefinition);
// 省略部分代码...
}
通过HSFProviderBeanDefinitionBuilder
构建BeanDefinition,其build方法通过BeanDefinitionBuilder
注册HSFSpringProviderBean
。
这块改造逻辑借鉴了Dubbo的处理思路,通过@DubboService
注解查找到ServiceAnnotationPostProcessor
bean后置处理类,postProcessBeanFactory
方法处理逻辑如下:
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
// 查找通过@DubboService注解的类并获取属性
Map<String, Object> annotationAttributes = getServiceAnnotationAttributes(beanDefinition);
if (annotationAttributes != null) {
// 注册BeanDefinition
processAnnotatedBeanDefinition(
beanName, (AnnotatedBeanDefinition) beanDefinition, annotationAttributes);
}
private void processAnnotatedBeanDefinition(
String refServiceBeanName,
AnnotatedBeanDefinition refServiceBeanDefinition,
Map<String, Object> attributes) {
Map<String, Object> serviceAnnotationAttributes = new LinkedHashMap<>(attributes);
// 省略部分代码...
// 构建AbstractBeanDefinition,其实就是对BeanDefinition的构建
AbstractBeanDefinition serviceBeanDefinition =
buildServiceBeanDefinition(serviceAnnotationAttributes, serviceInterface, refServiceBeanName);
// set id
serviceBeanDefinition.getPropertyValues().add(Constants.ID, serviceBeanName);
// 注册BeanDefinition
registerServiceBeanDefinition(serviceBeanName, serviceBeanDefinition, serviceInterface);
}
了解到Dubbo的注册过程,实际改造过程中也是通过实现BeanDefinitionRegistryPostProcessor
接口完成BeanDefinition的构建与注册,其逻辑如下:
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
String[] beanNames = beanFactory.getBeanDefinitionNames();
for (String beanName : beanNames) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
// 消费者
if (isHsfConsumerBean(beanDefinition)) {
// 省略部分代码...
}
// 提供者
else if (isHsfProviderBean(beanDefinition)) {
// 注册Dubbo bean
HsfProviderDubboService.registerBeanDefinition(beanName, this.applicationContext, beanDefinition, this.environment, registry);
if (this.registry.getBeanDefinition(beanName) != null) {
// 为了清除hsf注册中心注册实例
this.registry.removeBeanDefinition(beanName);
}
}
// 省略部分代码...
}
}
public static void registerBeanDefinition(String beanName, ApplicationContext applicationContext, BeanDefinition providerBeanDefinition, Environment environment, BeanDefinitionRegistry registry) {
BeanDefinitionHolder beanDefinitionHolder = new BeanDefinitionHolder(providerBeanDefinition, beanName);
// 具体注册逻辑
processScannedBeanDefinition(beanDefinitionHolder, applicationContext.getClassLoader(), environment, registry);
}
private static void processScannedBeanDefinition(BeanDefinitionHolder beanDefinitionHolder, ClassLoader classLoader, Environment environment, BeanDefinitionRegistry registry) {
// The attributes of @Service annotation
Map<String, Object> serviceAnnotationAttributes =
ReferenceBeanSupport.convertPropertyValues(beanDefinitionHolder.getBeanDefinition().getPropertyValues());
Map<String, Object> dubboAttributes = new HashMap<>(serviceAnnotationAttributes.size());
cleanAttribute(serviceAnnotationAttributes, dubboAttributes, "serviceInterface", "interfaceClass");
cleanAttribute(serviceAnnotationAttributes, dubboAttributes, "serviceGroup", "group");
cleanAttribute(serviceAnnotationAttributes, dubboAttributes, "serviceVersion", "version");
BeanDefinition beanDefinition = registry.getBeanDefinition(serviceAnnotationAttributes.get("target").toString());
Class<?> beanClass = resolveClass(beanDefinition, classLoader);
String serviceInterface = resolveInterfaceName(dubboAttributes, beanClass);
String annotatedServiceBeanName = serviceAnnotationAttributes.get("target").toString();
// ServiceBean Bean name
String beanName = generateServiceBeanName(dubboAttributes, serviceInterface, environment);
// 构建BeanDefinition
AbstractBeanDefinition serviceBeanDefinition =
buildServiceBeanDefinition(dubboAttributes, serviceInterface, annotatedServiceBeanName, environment);
// 注册,由于代码逻辑简单就不再展示了
// 核心代码:registry.registerBeanDefinition(serviceBeanName, serviceBeanDefinition)
registerServiceBeanDefinition(beanName, serviceBeanDefinition, serviceInterface, registry);
}
2. 展示
3. @HSFConsumer
入口文件HsfConsumerPostProcessor
,由于实现了BeanFactoryPostProcessor
接口,启动项目会通过postProcessBeanFactory
方法注册bean,消费者逻辑与Dubbo类似,具体逻辑如下:
private void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory, BeanDefinitionRegistry registry) {
final HsfProperties properties = HsfPropertiesUtils.buildHsfProperties(this.environment);
new HashMap();
String[] var5 = beanFactory.getBeanDefinitionNames();
int var6 = var5.length;
for(int var7 = 0; var7 < var6; ++var7) {
String beanName = var5[var7];
BeanDefinition definition = beanFactory.getBeanDefinition(beanName);
String beanClassName = definition.getBeanClassName();
if (beanClassName != null) {
Class<?> clazz = ClassUtils.resolveClassName(definition.getBeanClassName(), this.classLoader);
ReflectionUtils.doWithFields(clazz, new ReflectionUtils.FieldCallback() {
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
// 解析@HSFConsumer属性
HsfConsumerPostProcessor.this.parseElement(field, properties);
}
});
}
}
// 省略部分代码...
while(var12.hasNext()) {
String beanName = (String)var12.next();
if (this.context.containsBean(beanName)) {
throw new IllegalArgumentException("[HSF Starter] Spring context already has a bean named " + beanName + ", please change @HSFConsumer field name.");
}
// 注册bean
registry.registerBeanDefinition(beanName, (BeanDefinition)this.beanDefinitions.get(beanName));
}
}
private void parseElement(Field field, HsfProperties properties) {
// 获取注解属性
HSFConsumer annotation = (HSFConsumer)AnnotationUtils.getAnnotation(field, HSFConsumer.class);
if (annotation != null) {
HsfConsumerBeanDefinitionBuilder beanDefinitionBuilder = new HsfConsumerBeanDefinitionBuilder(field.getType(), annotation);
beanDefinitionBuilder.context(this.context).beanFactory(this.beanFactory).properties(properties);
// 构建BeanDefinition,解析注解属性
BeanDefinition beanDefinition = beanDefinitionBuilder.build();
if (this.checkFieldNameDuplicate4FirstTime(field.getName(), beanDefinition)) {
logger.error("registered HSFConsumerBean with duplicate fieldName:{} in spring context.", field.getName());
}
this.beanDefinitions.put(field.getName(), beanDefinition);
}
}
通过HsfConsumerBeanDefinitionBuilder
解析@HSFConsumer
属性,随后通过build
方法并构建BeanDefinition
。
从上文注册@DubboService
的过程可以发现,postProcessBeanFactory
方法预留了处理Hsf消费者的逻辑,
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
String[] beanNames = beanFactory.getBeanDefinitionNames();
for (String beanName : beanNames) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
// 消费者
if (isHsfConsumerBean(beanDefinition)) {
try {
// 构建BeanDefinition
BeanDefinition consumerDefinition =
HsfConsumerDubboService.registerBeanDefinition(beanName, this.applicationContext, this.referenceBeanManager, beanDefinition);
if (this.registry.getBeanDefinition(beanName) != null) {
// 为了清除hsf注册中心注册实例
this.registry.removeBeanDefinition(beanName);
}
// 构建bean
this.registry.registerBeanDefinition(beanName, consumerDefinition);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
} else if (isHsfProviderBean(beanDefinition)) {
// 服务提供者逻辑...
}
if (isAnnotatedReferenceBean(beanDefinition)) {
}
}
}
public static BeanDefinition registerBeanDefinition(String beanName, ApplicationContext applicationContext,
ReferenceBeanManager referenceBeanManager, BeanDefinition consumerBeanDefinition)
throws ClassNotFoundException {
MutablePropertyValues mutablePropertyValues = consumerBeanDefinition.getPropertyValues();
Map<String, Object> propertyAttributes = ReferenceBeanSupport.convertPropertyValues(mutablePropertyValues);
// check reference key
String referenceKey = ReferenceBeanSupport.generateReferenceKey(propertyAttributes, applicationContext);
Object propertyValue = propertyAttributes.get("id");
String referenceBeanName = beanName;
if (propertyValue != null) {
referenceBeanName = propertyValue.toString();
}
Map<String, Object> attributes = new HashMap<>();
attributes.put(ReferenceAttributes.ID, referenceBeanName);
// 将hsf属性转换为dubbo属性
resolveDubboAttributes(propertyAttributes, attributes);
// Register the reference bean definition to the beanFactory
RootBeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setBeanClassName(ReferenceBean.class.getName());
beanDefinition.getPropertyValues().add(ReferenceAttributes.ID, referenceBeanName);
// set attribute instead of property values
beanDefinition.setAttribute(Constants.REFERENCE_PROPS, attributes);
String interfaceName = attributes.get("interface").toString();
Class interfaceClass = Class.forName(interfaceName);
beanDefinition.setAttribute(ReferenceAttributes.INTERFACE_CLASS, interfaceClass);
beanDefinition.setAttribute(ReferenceAttributes.INTERFACE_NAME, interfaceName);
// create decorated definition for reference bean, Avoid being instantiated when getting the beanType of ReferenceBean
// see org.springframework.beans.factory.support.AbstractBeanFactory#getTypeForFactoryBean()
GenericBeanDefinition targetDefinition = new GenericBeanDefinition();
targetDefinition.setBeanClass(interfaceClass);
beanDefinition.setDecoratedDefinition(new BeanDefinitionHolder(targetDefinition, referenceBeanName + "_decorated"));
// signal object type since Spring 5.2
beanDefinition.setAttribute(Constants.OBJECT_TYPE_ATTRIBUTE, interfaceClass);
referenceBeanManager.registerReferenceKeyAndBeanName(referenceKey, referenceBeanName);
logger.info("Register dubbo reference bean: " + referenceBeanName + " = " + referenceKey);
return beanDefinition;
}
4. 展示
最终效果
结语
这次改动不仅仅只是适配SpringBoot框架,SpringMvc其实也是适配的,上述UserDemoService
服务是通过注解注入的服务,而RoleDemoService
服务是由xml配置提供的服务。
最终源码有时间会公开提供下,或者私信也可以。
- 点赞
- 收藏
- 关注作者
评论(0)