How to modify the default value of the startup class annotation of Springboot Application

Business background

Microservices are split, and the original core business is extracted from the common core dependency project. On the basis of the existing startup class, many core default annotation configurations are encapsulated to avoid the application of separate annotations (such as Fegin\ComponentScan\ServletComponentScan, etc.). The core service is unavailable. It is necessary to add the core configuration information to the annotation before the execution of part of the BeanDefinitionRegistrar when the SpringBoot Application starts, so as to dynamically modify the annotation effect.

SpringBoot SpringApplicationRunListener

To solve the problem of injecting before the initial loading of the Spring context, you can use the new class SpringApplicationRunListener of Springboot. The execution process is as follows
SpringApplicationRunListener call process
: SpringApplicationRunListener method:
started (Execute immediately after SpringBoot scans 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);

Business realization

Get the execution method before the SpringBootApplication Context context, directly press the code, you may need to modify multiple annotation values, and need to be logically decoupled, using the template method mode, the timing diagram is as follows
Business sequence diagram
:
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. Provide two usage modes
1) Modify basePackages by default through separate annotations

@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. Custom modify attributes
@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;
    }
}

At this point, the start of the public annotation modification is complete

Guess you like

Origin blog.csdn.net/lxn1023143182/article/details/112555377