业务背景
微服务拆分,原有的核心业务抽出公共的核心依赖工程,在现有启动类的基础上,封装了很多核心默认的注解配置,避免应用单独使用注解(如Fegin\ComponentScan\ServletComponentScan等)导致核心服务不可用,需要在SpringBoot Application启动时,部分BeanDefinitionRegistrar执行之前把核心的配置信息添加到注解中,做到动态修改注解的效果。
SpringBoot SpringApplicationRunListener
解决在Spring上下文初始化加载之前进行注入,可以采用Springboot新增的类SpringApplicationRunListener,执行过程如下
SpringApplicationRunListener方法:
started(SpringBoot扫描spring.factorids后立刻执行;
SpringApplication run代码如下
。
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
业务实现
拿到了SpringBootApplication Context上下文之前的执行方法,直接撸代码,可能需要修改多个注解值,需要在逻辑上解耦,采用模板方法模式,时序图如下
实现代码:
1、DemoApplicationRunListener.class
public class CombApplicationRunListener implements SpringApplicationRunListener {
private SpringApplication application;
private String[] args;
public CombApplicationRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
}
@Override
public void starting() {
IApplicationAnnotationFilter.ANNOTATION_FILTERS.forEach(filter -> {
filter.run(application, args);
});
}
2、IApplicationAnnotationFilter.class
public interface IApplicationAnnotationFilter {
/**
* 通过package url扫描查找耗时3秒左右,故使用手工注册方式进行填充,后续考虑优化
**/
List<IApplicationAnnotationFilter> ANNOTATION_FILTERS = new ArrayList<IApplicationAnnotationFilter>() {
{
add(new MapperScanAnnotationFilter());
add(new ServletScanAnnotationFilter());
}
};
boolean run(SpringApplication application, String[] args);
}
3、AbstractAnnotationExecutor.class
public abstract class AbstractAnnotationExecutor implements IApplicationAnnotationFilter {
private Logger logger = LoggerFactory.getLogger(AbstractAnnotationExecutor.class);
@Override
public boolean run(SpringApplication application, String[] args) {
if (!this.getClass().isAnnotationPresent(Registar.class)) {
throw new FbRuntimeException("默认核心类未添加Register注解,无法识别需要修改的注解信息!");
}
Registar registar = this.getClass().getAnnotation(Registar.class);
if (!application.getMainApplicationClass().isAnnotationPresent(registar.value())) {
return false;
}
Annotation annotation = application.getMainApplicationClass().getAnnotation(registar.value());
InvocationHandler handler = Proxy.getInvocationHandler(annotation);
try {
Field field = handler.getClass().getDeclaredField("memberValues");
field.setAccessible(true);
Map memberValues = (Map) field.get(handler);
boolean result = editAnnotationValue(memberValues);
if (registar.defaultPackages() != null && !"".equals(registar.defaultPackages())) {
result = fillDefaultPackages(memberValues, registar.defaultPackages(), registar.value().getSimpleName());
}
return result;
} catch (Exception e) {
logger.error(String.format("获取注解%s属性异常", annotation.getClass().getName()), e);
}
return false;
}
protected boolean fillDefaultPackages(Map memberValues, String corePackageName, String className) {
Object basePackagesObj = memberValues.get("basePackages");
if (basePackagesObj != null) {
//此处不要使用直接获取的Arrays.asList进行添加操作,因为Arrays默认的返回list属于内部类,内部类不支持add和romve
List<String> basePackages = new ArrayList(Arrays.asList((String[]) basePackagesObj));
if (!basePackages.contains(corePackageName)) {
basePackages.add(corePackageName);
//不能直接将list直接put到memberValues中,list默认都是Object类型,会导致取出无法强转String
String[] defaultPackages = new String[basePackages.size()];
basePackages.toArray(defaultPackages);
memberValues.put("basePackages", defaultPackages);
}
logger.info(String.format("[系统已经默认在%s.class增加默认核心包扫描->%s]", className, basePackages));
}
return true;
}
/**
* @param memberValues 入参,修改属性值的map容器
* @return 修改是否成功.
*/
protected abstract boolean editAnnotationValue(Map memberValues);
}
4、 提供两种使用模式
1)通过单独注解默认修改basePackages
@Registar(value = ServletComponentScan.class, defaultPackages = "com.god.demo.framework.common.filter")
public class ServletScanAnnotationFilter extends AbstractAnnotationExecutor {
@Override
protected boolean editAnnotationValue(Map memberValues) {
//如果只是修改basePackages(要同名),使用了默认实现过程
return true;
}
}
- 自定义修改属性
@Registar(MapperScan.class)
public class MapperScanAnnotationFilter extends AbstractAnnotationExecutor {
@Override
protected boolean editAnnotationValue(Map memberValues) {
fillDefaultPackages(memberValues, "com.god.demo.framework.common.mapper", "MapperScan");
Object sqlSessionFactoryRefObj = memberValues.get("sqlSessionFactoryRef");
if (sqlSessionFactoryRefObj == null || "".equals(sqlSessionFactoryRefObj)) {
memberValues.put("sqlSessionFactoryRef", SQL_FACTORY);
log.info("[系统已经默认在MapperScan.class增加默认sqlSessionFactory]" + SQL_FACTORY);
}
return true;
}
}
至此,启动公共注解修改完成