从Flowable流程引擎聊一聊如何动态修改Spring中的BeanDefination

前言

最近在给一个项目整合Flowable流程引擎,跑起来很顺利。为了便于维护,将业务用的数据库和Flowable用的数据库分离。根据文档,很容易就能实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Component
public class FlowableConfig implements EngineConfigurationConfigurer<SpringAppEngineConfiguration> {

private final DataSource dataSource;

public FlowableConfig(@Qualifier("flowableDataSource") DataSource dataSource) {
this.dataSource = dataSource;
}

@Override
public void configure(SpringAppEngineConfiguration engineConfiguration) {
engineConfiguration.setDataSource(dataSource);
}

}

但是,当添加了一些扩展模块后,发现扩展模块的数据源并不符合预期,仍然使用了容器中的默认数据源。通过一番阅读源码,找到了问题所在

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Bean
@Qualifier("flowableModeler")
public SqlSessionFactory modelerSqlSessionFactory(DataSource dataSource) {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
String databaseType = initDatabaseType(dataSource);
if (databaseType == null) {
throw new FlowableException("couldn't deduct database type");
}

try {
Properties properties = new Properties();
properties.put("prefix", modelerAppProperties.getDataSourcePrefix());
properties.put("blobType", "BLOB");
properties.put("boolValue", "TRUE");

properties.load(this.getClass().getClassLoader().getResourceAsStream("org/flowable/db/properties/" + databaseType + ".properties"));

sqlSessionFactoryBean.setConfigurationProperties(properties);
sqlSessionFactoryBean
.setMapperLocations(ResourcePatternUtils.getResourcePatternResolver(resourceLoader).getResources("classpath:/META-INF/modeler-mybatis-mappings/*.xml"));
sqlSessionFactoryBean.afterPropertiesSet();
return sqlSessionFactoryBean.getObject();
} catch (Exception e) {
throw new FlowableException("Could not create sqlSessionFactory", e);
}

}

这是官方建模工具的后端源码,可以看到这里直接注入了默认的数据源。那现在的问题来了,第三方库中的源码,无法修改,这该怎么修改行为呢?

方案

好在这是一个Spring容器中的Bean,只要是Bean,必然有某种媒介可以与其交互。

BeanPostProcessor

该接口是Spring提供用来修改已经创建的Bean的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Component
public class FlowableModelerDataSourceConfig implements BeanPostProcessor {

private final ModelerDatabaseConfiguration modelerDatabaseConfiguration;

private final DataSource dataSource;

public FlowableModelerDataSourceConfig(ModelerDatabaseConfiguration modelerDatabaseConfiguration, @Qualifier("flowableDataSource") DataSource dataSource) {
this.modelerDatabaseConfiguration = modelerDatabaseConfiguration;
this.dataSource = dataSource;
}

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals("adminLiquibase")) {
return modelerDatabaseConfiguration.modelerSqlSessionFactory(dataSource);
}

return bean;
}

}

这里注入了原来的配置类,并再次调用了构造方法,传入所需的数据源、替换掉了原来的bean。这里实现了需求,但是在另一处地方就不行了

1
2
3
4
5
6
7
8
9
10
11
@Bean
@Qualifier("flowableModeler")
public Liquibase modelerLiquibase(DataSource dataSource) {
LOGGER.info("Configuring Liquibase");

try {
return LiquibaseUtil.runInFlowableScope(() -> createAndUpdateLiquibase(dataSource));
} catch (Exception e) {
throw new InternalServerErrorException("Error creating liquibase database", e);
}
}

在这里,构造方法注入了默认的数据源,但是却用它进行了表结构的初始化。由于在替换Bean之前构造方法已经被执行了一次,所以表结构被创建到了默认数据源中,所以这种方法是不能完全满足需求的,这就有了更底层的第二种方法

BeanFactoryPostProcessor

在Spring扫描完所有Bean定义后,就会将他们封装成BeanDefinition,在需要时根据BeanDefinition创建对应的Bean。所以只要修改BeanDefinition,就能改变Bean创建的行为。而Spring提供了BeanFactoryPostProcessor接口来实现这个目的,它可以在所有Bean初始化之前,提供修改BeanDefinition的能力。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Component
public class FlowableModelerDataSourceConfig implements BeanFactoryPostProcessor {

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
Stream.of("adminLiquibase", "adminSqlSessionFactory", "modelerSqlSessionFactory", "modelerLiquibase")
.forEach(beanName -> {
final ConstructorArgumentValues values = beanFactory.getBeanDefinition(beanName).getConstructorArgumentValues();
values.clear();
values.addGenericArgumentValue(new RuntimeBeanReference("flowableDataSource"));
});
}

}

在这段代码中,通过获取到adminLiquibase的构造器参数列表,清除了原来的参数,并向其中添加了一个新的参数,将默认数据源Bean替换为了flowableDataSource,实现了Bean初始化行为的修改。
除此之外,通过BeanDefinition能修改各种各样的行为,如通过setScope方法就能控制Bean是否是单例

从Flowable流程引擎聊一聊如何动态修改Spring中的BeanDefination

https://jktu.cc/从Flowable流程引擎聊一聊如何动态修改Spring中的BeanDefination/

作者

udp_bbr

发布于

2023-10-10

更新于

2023-10-11

许可协议