讓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,之前面臨的問題也能得到有效的解決.因為時間問題,我沒有試驗這個思路,希望以後有機會填坑.