如何修改Springboot Application的启动类注解默认值

业务背景

微服务拆分,原有的核心业务抽出公共的核心依赖工程,在现有启动类的基础上,封装了很多核心默认的注解配置,避免应用单独使用注解(如Fegin\ComponentScan\ServletComponentScan等)导致核心服务不可用,需要在SpringBoot Application启动时,部分BeanDefinitionRegistrar执行之前把核心的配置信息添加到注解中,做到动态修改注解的效果。

SpringBoot SpringApplicationRunListener

解决在Spring上下文初始化加载之前进行注入,可以采用Springboot新增的类SpringApplicationRunListener,执行过程如下
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;
    }
}
  1. 自定义修改属性
@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;
    }
}

至此,启动公共注解修改完成

猜你喜欢

转载自blog.csdn.net/lxn1023143182/article/details/112555377