1. 程式人生 > >讓Spring Boot專案啟動時可以根據自定義配置決定初始化哪些Bean

讓Spring Boot專案啟動時可以根據自定義配置決定初始化哪些Bean

讓Spring Boot專案啟動時可以根據自定義配置決定初始化哪些Bean

問題描述

目前我工作環境下,後端主要的框架是Spring Boot,目前面臨的問題也是在Spring Boot中出現的.
具體情況是這樣的,期望是搭建一個公用的框架,適用於多種業務場景的,整合好如Redis,日誌管理,定時任務管理等一系列配置即用的框架,但是在整合好Activiti框架後我發現有的專案並不需要使用Activiti框架,但是由於我使用的Maven依賴如下:

<dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-spring-boot-starter-basic</artifactId>
    <version>${activiti.version}</version>
</dependency>
<dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-spring-boot-starter-actuator</artifactId>
    <version>${activiti.version}</version>
</dependency>

可以看到是使用了starter系列的依賴,所以Spring Boot會在啟動時預設初始化Activiti相關的JAVA Bean,這個時候會出現以下問題:

  • 初始化這些我用不到的bean可能需要一些配置檔案等資源,而這個框架因為這個專案沒有使用,所以沒有從而導致的啟動報錯[在我目前的場景下是沒有Activiti的流程圖檔案]
  • 即便初始化這些JAVA Bean沒有問題,但是某些依賴可能會改變我資料庫的表結構[在我這個場景下是會在我的資料庫中新增25個以ACT_開頭的表]
  • 當部署的伺服器資源比較緊張的時候,這些多餘的JAVA Bean會額外佔用本來就不多的記憶體資源
    由於我自己對Activiti的方法還做了一些封裝,相當於提供了一個Service層的介面來使得程式碼編寫更加簡單,這部分程式碼我並不希望從腳手架中刪除掉,比較部分專案還是需要使用的,所以我現在需要實現的就是在Spring Boot專案啟動的時候,讓它根據我在application.yml檔案中的配置來確定需要初始化哪些JAVA Bean.

實現思路

這裡我有三個思路:

  • 思路一:直接移除相關依賴
  • 思路二:增加一個自定義的註解,當滿足我的配置的時候我再初始化我的Bean否則不初始化這些Bean
  • 思路三:把我對Activiti的包裝部分集合成一個專案,然後提供一個類似於activiti-spring-boot-starter的start型別的包給到我的具體專案中去

思路一 [不符合要求]

之前也提到了,由於我自己對Activiti的方法還做了一些封裝,如果移除了Activiti的Maven依賴,會直接導致我封裝的程式碼報錯,所以這種方式並不能滿足我的要求,不再贅述

思路二[滿足要求]

這裡我參照了簡書作者@數齊 的名為SpringBoot基礎教程(十八)——自定義條件註解的博文,成功實現了根據我的配置載入Bean的功能,核心程式碼如下:

  • 註解
import com.hykj.activiti.annotation.impl.ActivitiConditionImpl;
import org.springframework.context.annotation.Conditional;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Documented
@Conditional(ActivitiConditionImpl.class)
public @interface ActivitiCondition {

}
  • 註解的實現
import com.google.common.base.Strings;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;

/**
 * 是否需要activiti的註解<br/>
 * 如果需要activiti的話,那麼在application加入sys.need-activiti=true <br/>
 * 其他情況不再初始化activiti的相關bean
 *
 * @author weizj
 */
public class ActivitiConditionImpl implements Condition {

    /** 啟動的配置值 */
    private static String ENABLE = "true";
    /** 配置的屬性名 */
    private static String CONFIG_PROPERTY_NAME = "sys.need-activiti";


    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {

        String propertyValue = conditionContext.getEnvironment().getProperty(CONFIG_PROPERTY_NAME);

        return !Strings.isNullOrEmpty(propertyValue) && propertyValue.equalsIgnoreCase(ENABLE);

    }
}
  • 註解的應用
import com.hykj.activiti.annotation.ActivitiCondition;
import org.activiti.engine.ProcessEngineConfiguration;
import org.activiti.engine.impl.history.HistoryLevel;
import org.activiti.spring.SpringProcessEngineConfiguration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;

/**
 * Activiti流程引擎的配置
 */
@Configuration
@ActivitiCondition
public class ActivitiConfig {

    @Autowired
    public ActivitiConfig(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    private DataSource dataSource;

    @Bean
    public PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean
    public SpringProcessEngineConfiguration springProcessEngineConfiguration() {
        System.out.println("=======================");
        SpringProcessEngineConfiguration configuration = new SpringProcessEngineConfiguration();
        //配置項內容設定
        configuration
                //設定資料庫的型別
                .setDatabaseType("mysql")
                //使用springboot自帶的資料來源
                .setDataSource(dataSource)
                //設定欄位更新型別
                .setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE)
                //
                .setJobExecutorActivate(true)
                //設定歷史記錄級別
                .setHistoryLevel(HistoryLevel.FULL)
                //設定標籤字型
                .setLabelFontName("宋體")
                //設定註解字型
                .setAnnotationFontName("宋體")
                //設定圖形字型
                .setActivityFontName("宋體")
        ;

        configuration.setTransactionManager(transactionManager());
        return configuration;
    }
}
  • 配置檔案
sys:
  need-activiti: 11

到這裡為止,如果配置檔案中sys.need-activiti的值為true的時候Spring Boot啟動的時候才會載入我配置的ActivitiConfig類中的Bean,但是這並不能讓Spring Boot在啟動的時候不初始化Activiti相關的如:RuntimeService/IdentityService/TaskService/RepositoryService/HistoryService等由activiti-spring-boot-starter-basic依賴自動裝配的Bean.
正當我以為這條路走不通的時候我看到了@SpringBootApplication註解中包含exclude屬性,我之前用它排除了org.activiti.spring.boot.SecurityAutoConfiguration.class類來避免Activiti的安全認證和Spring Secrity以及Apache Shrio之間的衝突問題.在當前的情況下,我已經在專案啟動的時候完成了我自己類的去除,只要再去掉由於activiti-spring-boot-starter-basic包存在所初始化的類大概就可以了,於是我的@SpringBootApplication從這樣:
@SpringBootApplication(exclude = { SecurityAutoConfiguration.class, org.activiti.spring.boot.SecurityAutoConfiguration.class})
變成了這樣:

@SpringBootApplication(exclude = {
        SecurityAutoConfiguration.class,
        //不論是否使用activiti都要關閉這個類
        org.activiti.spring.boot.SecurityAutoConfiguration.class,

        //不使用activiti關閉的類開始
        org.activiti.spring.boot.EndpointAutoConfiguration.class,
        org.activiti.spring.boot.JpaProcessEngineAutoConfiguration.class,
        org.activiti.spring.boot.DataSourceProcessEngineAutoConfiguration.class,
        org.activiti.spring.boot.RestApiAutoConfiguration.class
        //不使用activiti關閉的類結束
})

這時啟動我的專案,發現數據庫中並沒有生成ACT_開頭的表,也就意味著我已經完全去除了activiti-spring-boot-starter-basic所帶來的JAVA Bean.
至此問題圓滿解決.

思路三[未試驗]

目前來說思路二是把和Activiti相關的初始化,封裝的方法單獨抽成一個JAR依賴,在需要它的時候引入這個依賴,這樣Spring Boot在啟動的時候是覺得不會裝配多餘的JAVA Bean,之前面臨的問題也能得到有效的解決.因為時間問題,我沒有試驗這個思路,希望以後有機會填坑.