目的:项目功能实现分标准实现和个性实现,当启用个性实现时,则需要个性化bean处理业务。
参考自某大神的思路,也可以封装为自定义的starter,供其他模块服务
1.准备
代码结构:
pom文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.me</groupId>
<artifactId>personnalize</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>personnalize</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.22</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
配置文件application.yml:
#项目名称,default表示标准, ICBC , BOC 标识个性项目
personalise:
projectName: BOC
2. 代码实现
/**
* ClassName: PersonaliseComponentScan
*
* @Author: ME
* Description:个性化bean注解
*/
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(CustomComponentRegistrar.class)
public @interface PersonaliseComponentScan {
/**
* 扫描包路径,为空表示默认扫描个性化路径
*/
String basePackage() default "";
}
/**
* ClassName: CustomComponentRegistrar
*
* @Author: ME
* Description:自定义Spring bean注册器
*/
@Slf4j
public class CustomComponentRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {
private static final String PERSONALIZE_PROJECT_PACKAGE = ".project.";
private static final String PROJECT_NAME_PROPERTIES = "personalise.projectName";
private static final String PROJECT_STANDARD = "default";
private Environment environment;
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 读取注解 SpringBootApplication 的包内容
Class<?> introspectedClass = ((StandardAnnotationMetadata) importingClassMetadata).getIntrospectedClass();
String originBasePackageName = introspectedClass.getPackage().getName();
AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(PersonaliseComponentScan.class.getName()));
String projectName = environment.getProperty(PROJECT_NAME_PROPERTIES);
String annotationBasePackage = annoAttrs.getString("basePackage");
if (!StringUtils.isEmpty(annotationBasePackage) && !annotationBasePackage.equals(originBasePackageName)) {
String applicationName = introspectedClass.getName();
log.warn("@PersonaliseComponentScan 的 basePackage 配置 与 {} 所在包路径不一致", applicationName);
}
if (StringUtils.isEmpty(annotationBasePackage)) {
annotationBasePackage = originBasePackageName;
}
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) registry;
this.replacePersonaliseBean(projectName, annotationBasePackage, beanFactory);
}
private void replacePersonaliseBean(String projectName, String annotationBasePackage, DefaultListableBeanFactory beanFactory) {
boolean useStandard = PROJECT_STANDARD.equals(projectName);
for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanDefinitionName);
// 个性化Bean替换
if (beanDefinition instanceof ScannedGenericBeanDefinition && !useStandard) {
String beanClassName = beanDefinition.getBeanClassName();
//处理包名
String replace = Objects.requireNonNull(beanClassName).replace(annotationBasePackage, annotationBasePackage + PERSONALIZE_PROJECT_PACKAGE + projectName);
//处理类名
String className =projectName+replace.substring(replace.lastIndexOf(".")+1);
replace = replace.substring(0,replace.lastIndexOf(".")+1)+className;
try {
Class<?> aClass = Class.forName(replace);
if (aClass != null) {
beanDefinition.setBeanClassName(replace);
((ScannedGenericBeanDefinition) beanDefinition).setBeanClass(aClass);
}
} catch (ClassNotFoundException e) {
// do nothing
}
}
}
}
}
/**
* ClassName: PrintService
*
* @Author: ME
* Description: 标准bean
*/
@Slf4j
@Service
public class PrintService {
public void print(){
log.info("use default bean");
}
}
/**
* ClassName: PrintService
*
* @Author: ME
* Description:个性化的bean
*/
@Slf4j
@Service
public class ICBCPrintService extends PrintService {
public void print(){
log.info("use icbc bean");
}
}
/**
* ClassName: PrintService
*
* @Author: ME
* Description: 个性化的bean
*/
@Slf4j
@Service
public class BOCPrintService extends PrintService {
public void print(){
log.info("use boc bean");
}
}
# 程序入口,@ComponentScan配置默认不扫描个性化bean;
# @PersonaliseComponentScan配置是否启用个性化扫描;
# 个性化扫描路径为:PersonnalizeApplication包名
+project
+application.yml配置名
, 如: com.me.personalize.project.BOC
# 扫描到的个性化bean将会替换标准bean;
@SpringBootApplication
@ComponentScan(
excludeFilters = @ComponentScan.Filter(
type = FilterType.REGEX,
pattern = "com.me.personnalize.project.*"
)
)
@PersonaliseComponentScan
public class PersonnalizeApplication {
public static void main(String[] args) {
SpringApplication.run(PersonnalizeApplication.class, args);
}
}
测试类:
@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonnalizeApplicationTests {
// 自动装配一个bean
@Resource
PrintService printService;
@Test
public void contextLoads() {
printService.print();
}
}
3.效果
3.1 配置标准启动时
personalise:
projectName: default
3.2 配置BOC个性化启动
personalise:
projectName: BOC