概述
Spring Dubbo 是我自己写的一个基于spring-boot和dubbo,目的是使用Spring boot的风格来使用dubbo。(即可以了解Spring boot的启动过程又可以学习一下dubbo的框架)
项目介绍:
github: https://github.com/Athlizo/spring-dubbo-parent
码云: https://git.oschina.net/null_584_3382/spring-dubbo-parent
有兴趣的朋友可以一起交流学习。
需求:
Dubbo启动的时候,是可以使用自己的Spring来启动dubbo服务,但是现在是需要把Dubbo启动SpringApplicationContest的逻辑放入到Spring Boot的启动逻辑中去。(主要是针对注解的方式)
常用的接口
按照调用的顺序来介绍
ApplicationContextInitializer
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
void initialize(C applicationContext);
}
Spring Boot启动的时候,会扫描classpath下的META-INF.spring.factories, 其中有一个配置名为org.springframework.context.ApplicationContextInitializer,配置项为一些实现了ApplicationContextInitializer接口的类的全路径名,这些类就是Spring Boot在启动的时候首先会进行实例化。
ApplicationListener
同ApplicationContextInitializer加载方式一样,META-INF.spring.factories中还有另外一个配置项org.springframework.context.ApplicationListener,定义在SpringBoot启动的时候会初始化的ApplicationListener。严格来说ApplicationListener在整个SpringApplicationContext启动的时候都会触发调用逻辑(通过各种不同事件触发)
BeanFactoryPostProcessor
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
从名字可以看出,这个接口的类主要是针对BeanFactory进行一些操作,和ApplicationContextInitializer实例化的方式不一样,这个接口必须在BeanFactory中存在才会在启动中生效,因此,ApplicationContextInitializer中常做的事情就是加入一些BeanFactoryPostProcessor 。
BeanPostProcessor
public interface BeanPostProcessor {
// 先于afterPropertiesSet() 和init-method
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
如果BeanFactoryPostProcessor针对的是BeanFactory,那么BeanPostProcessor针对就是BeanFactory中的所有bean了。分别提供了Bean初始化之前和bean初始化之后的相关处理。和BeanFactoryPostProcessor一样,也需要在启动中注册到BeanFactory中才会生效,一般通过BeanFactoryPostProcessor 加入。
小结
从上面的分析可以看出
- 如果需要在SpringApplication初始化的时候需要做事情,使用ApplicationContextInitializer
- 如果在Spring启动的各个阶段有一些定制化的处理,使用ApplicationListener
- 如果需要对BeanFactory做一些处理,例如添加一些Bean,使用BeanFactoryPostProcessor
- 如果需要对BeanFactory中的bean做一些处理,使用BeanPostProcessor ,(个人理解是该接口主要针对bean批量处理,否则针对特定的bean就使用InitializeBean或者Init-method完成初始化逻辑)
SpringBoot接入Dubbo
注:这里的接入主要是对使用注解的方式。
Dubbo是中处理@Service和@Reference
关键类:com.alibaba.dubbo.config.spring.AnnotationBean 代码逻辑比较简单,这个类实现了BeanFactoryPostProcessor和BeanPostProcessor这2个接口,分别作用:
- 在BeanFactory处理结束以后,扫描Service注解的类,加入到BeanFactory中
- 在Bean初始化之前,对@Reference注解的set方法和属性创建远程调用代理类并注入
- 在Bean初始化之后,对@Service注解的类暴露远程调用服务(生成Exporter)
因此,SpringBoot接入dubbo的关键在于:在完成BeanFactoryPostProcessor调用之前,把AnnotationBean加入到BeanFactory中就可以了
接入方式
这里不直接使用AnnotationBean,而是另外定义一个类,新类的名字为AnnotationBeanProcessor(为了贴代码方便),作用是一样的,只是修改里面的部分处理逻辑。
方法1. 利用ApplicationContextInitializer
代码如下
public class DubboContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
public void initialize(ConfigurableApplicationContext applicationContext) {
AnnotationBeanProcessor annotationBeanProcessor= new AnnotationBeanProcessor(${构造参数});
annotationBeanProcessor.setApplicationContext(applicationContext);
applicationContext.addBeanFactoryPostProcessor(annotationBeanProcessor);
}
}
特点——简单暴力
方法2 利用BeanFactoryPostProcessor
在ApplicationContextInitializer中加入其它的一个BeanFactoryPostProcessor,然后在这个BeanFactoryPostProcessor加入AnnotationBeanProcessor
public class DubboContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
public void initialize(ConfigurableApplicationContext applicationContext) {
DubboBeanDefinitionRegistryPostProcessor dubboBeanDefinitionRegistryPostProcessor = new DubboBeanDefinitionRegistryPostProcessor();
applicationContext.addBeanFactoryPostProcessor(dubboBeanDefinitionRegistryPostProcessor);
}
public class DubboBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(AnnotationBeanProcessor.class);
beanDefinition.getConstructorArgumentValues()
.addGenericArgumentValue(${构造参数});
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition("annotationBeanProcessor", beanDefinition);
}
}
}
特点: 这样做虽然感觉有点绕,但是好处就是可以在其它的一些关键的BeanDefinitionRegistryPostProcessor 后再执行,这样就可以使用xxxAware接口,Spring会自动帮我们注入。可以利用Spring提供的一些便利功能。 虽然利用ApplicationListener也可以做到,但是不推荐
方法3 利用ImportBeanDefinitionRegistrar
@Import注解中加入ImportBeanDefinitionRegistrar的实现类,实现对bean definition 层面的开发。
public class AnnotationBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
private String BEAN_NAME = "annotationBeanProcessor";
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
List<String> basePackages = getPackagesToScan(importingClassMetadata);
if (!registry.containsBeanDefinition(BEAN_NAME)) {
addPostProcessor(registry, basePackages);
}
}
// register annotationBeanProcessor.class
private void addPostProcessor(BeanDefinitionRegistry registry, List<String> basePackages) {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(AnnotationBeanProcessor.class);
beanDefinition.getConstructorArgumentValues()
.addGenericArgumentValue(basePackages);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(BEAN_NAME, beanDefinition);
}
//获取扫描的包路径
private List<String> getPackagesToScan(AnnotationMetadata metadata) {
//EnableDubbo 是一个注解,用于开启扫描dubbo的bean,并且可以自己定义扫描basePackages
AnnotationAttributes attributes = AnnotationAttributes.fromMap(
metadata.getAnnotationAttributes(EnableDubbo.class.getName()));
String[] basePackages = attributes.getStringArray("basePackages");
return Arrays.asList(basePackages);
}
}
其中还使用到了EnableDubbo.class ,其实这个是一个注解,里面定义了basePackages的属性。 特点:1 通过注解使Dubbo是否生效,还可以自己配置basePackages的扫描包路径,而不用写死在代码里。2. 很Spring Boot Style
@Reference和@Service
- @Reference注解的set方法和属性,Dubbo会创建ReferenceBean(代理类)然后注入进进。
- @Service就是在bean初始化,会生成一个ServiceBean,然后exporter(监听远程调用请求)。
而这2个流程的理想处理方式就是在BeanPostProcessor 中,因为上面这2个处理逻辑不是针对某个特殊的bean,而是针对所有的bean,只要有@Reference或者@Service,并且满足basepackage限制就行。
dubbo现有的逻辑是分别在所有bean初始化之前进行@Reference相关流程,而在所有bean初始化之后调用@Service处理流程。(这2个流程甚至在ApplicationContext初始化成功以后再进行,并且这样做还会带来一定的好处)