前言
最近在给一个项目整合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是否是单例