让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,之前面临的问题也能得到有效的解决.因为时间问题,我没有试验这个思路,希望以后有机会填坑.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270