Springbootアプリケーションのスタートアップクラスアノテーションのデフォルト値を変更する方法

事業背景

マイクロサービスは分割され、元のコアビジネスは共通のコア依存関係プロジェクトから抽出されます。既存のスタートアップクラスに基づいて、多くのコアのデフォルトアノテーション構成がカプセル化され、個別のアノテーションの適用を回避します(Fegin \ ComponentScan \ ServletComponentScan、コアサービスは利用できません。アノテーションの効果を動的に変更するには、SpringBootアプリケーションの起動時に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コンテキストコンテキストの前に実行方法を取得し、直接コードを押して、あなたが複数の注釈の値を変更する必要があり、かつ論理的にテンプレートメソッドモードを使用して、デカップリングする必要があり、次のようにタイミング図である
ビジネスシーケンス図

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. 2つの使用モードを提供します
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