问题
对于一些需要在多方部署,代码大部分相同,但是只有部分逻辑或者业务不同的系统(比如一些管理系统需要根据当地法规或者标书中的要求去做一些改动适配),在以往实践中,基本会有两种管理方式
1.创建多个项目,其中会有冗余代码
2.在单个项目中,编写多个核心逻辑,部署时,在配置文件选择走哪个逻辑
对于这两种方案来说,都会产生冗余的代码,可扩展性和管理性都会受限,牵一发可能会动全身
思路
目前基于spring提供的可扩展接口来说,有一种实现方式可以比较好的来规避这些问题
核心的思路是只有一个框架服务,而核心代码从中
拆出来当做jar包,在部署时当做plugin加载到JVM中,并且jar包中的代码可以和spring的IoC容器整合
为什么会要整合IoC容器?整合后在框架部分只需要使用@Resource注解注入由IoC管理的jar包内的bean即可使用可插拔的服务,这样可以方便开发人员去编写代码,也可以在无需修改框架代码的情况下去变更接口的实现
具体实现
spring在启动过程中提供了多种可扩展的点,我们这次要利用的是BeanDefinitionRegistryPostProcessor
、ImportBeanDefinitionRegistrar
以及ClassPathBeanDefinitionScanner
spring在启动时,会先扫描classpath
下面的所有符合条件的class,并且生成这些class的描述(beanDefinition
),这些beanDefinition
被统一管理在一个ConfigurableListableBeanFactory
的工厂里,在制作完所有beanDefinition
后,会逐一去实例化并且注入相关的属性
这次瞄准的就是这个过程,在实例化前,将我们扩展jar包内的bean扫描进ConfigurableListableBeanFactory
工厂里
启动时,spring会提前实例化一些类,比如处理beanDefinition
的这些类,所以我们需要将自己的BeanDefinitionRegistryPostProcessor
加入到spring容器中,可以在启动类中加入@Configuration
和@Import
来告诉spring去加载我们的BeanDefinitionRegistryPostProcessor
然后需要定义一个类扫描机制,因为spring默认的扫描机制只会扫描classpath
下面的类,而我们的jar包并不属于classpath
,所以我们需要继承ClassPathBeanDefinitionScanner
来重写doScan
方法,提取出ClassLoader
并将jar包的路径填入,让ClassLoader
加载这个jar包下的class,等于加载到了classpath
下面
在定义我们自己的BeanDefinitionRegistryPostProcessor
时需要告诉spring要将哪些类加入到spring IoC容器中
至此基本可以完成功能