1.问题起因
目前写SPringBoot引入多数据源路由
遇到了需要控制加载顺序的问题
@Configuration
@MapperScan(basePackages = "com.gaotianyue.dao")
public class DataSourceConfig {
第一步
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource1() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource2() {
return = DataSourceBuilder.create().build();
}
}
@Configuration
public class RoutingDataSourceConfig {
第二步
@Bean(name="rwDataSource")
public ReadWriteRoutingDataSource rwDataSource(@Qualifier("dataSource1") DataSource dataSource1, @Qualifier("dataSource2") DataSource dataSource2) {
ReadWriteRoutingDataSource ds = new ReadWriteRoutingDataSource();
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(Master.class, dataSource1);
targetDataSources.put(Slave.class, dataSource2);
ds.setTargetDataSources(targetDataSources);
ds.setDefaultTargetDataSource(dataSource1);//默认数据源
return ds;
}
第三部报错
@Bean
public SqlSessionFactory ds1SqlSessionFactory(@Qualifier("rwDataSource") ReadWriteRoutingDataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mappers/*.xml"));
return bean.getObject();
}
}
两段程序本来在一个类中
引用情况如下:
|----<>SqlSessionFactoryBean
|
|<>-- -- DataSource
| /|\
| |
|<>-- -- RWRoutingDataSoure
|
|<>---DataSource
启动后发现出现循环引用错误
单独保留DataSource RWRoutingDataSoure的配置没有问题,一旦SqlSessionFactoryBean从参数注入RWRoutingDataSoure 就会爆出
Error creating bean with name 'rwDataSource': Requested bean is currently in creation: Is there an unresolvable circular reference?
首先想到控制加载顺序的问题。首先想到@AutoConfigureAfter({DataSourceConfig.class})这一注解,但是加上这一注解是无效的。即使有不少人使用了@AutoConfigureAfter({DataSourceConfig.class}) + @Configuration的配置,其实并未起到作用。阅资料还需要配置spring.factories文件,
META-INF/spring.factories
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.gaotianyue.autoconfigure.DataSourceConfig,\
com.gaotianyue.autoconfigure.RoutingDataSourceConfig
修改App.java使配置类的路径不能被SpringBootApplication扫描。
@SpringBootApplication(scanBasePackages={"com.gaotianyue.manager"})
public class App {
public static void main(String[] args) throws Exception {
AppStarter.initManagers(SpringApplication.run(App.class, args));
}
}
但是 依然“不能够控制加载顺序!”
目前循环依赖的起因初步判断跟
org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer有关
@PostConstruct
public void init() {
if (!this.properties.isInitialize()) {
logger.debug("Initialization disabled (not running DDL scripts)");
return;
}
if (this.applicationContext.getBeanNamesForType(DataSource.class, false,
false).length > 0) {
this.dataSource = this.applicationContext.getBean(DataSource.class);
}
if (this.dataSource == null) {
logger.debug("No DataSource found so not initializing");
return;
}
runSchemaScripts();
}
和Object org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerPostProcessor.postProcessAfterInitialization(Object bean, String beanName) throws BeansException
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof DataSource) {
// force initialization of this bean as soon as we see a DataSource
this.beanFactory.getBean(DataSourceInitializer.class);
}
return bean;
}
有关,正在努力测试中
解决方案1
/**
* 读写分离数据源配置
*/
@Configuration
//@EnableTransactionManagement //事务
@MapperScan(basePackages = "com.vipkid.coursera.manager.dao")//scan
public class DataSourceConfig {
public DataSourceConfig(){
LogCore.BASE.info("myabtis multiple datasources configure start !");
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource1() {
DataSource ds = (DataSource) DataSourceBuilder.create().build();
LogCore.BASE.info("dataSource1.hashCode={}", Util.objString(ds));
return ds;
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource2() {
DataSource ds = (DataSource) DataSourceBuilder.create().build();
LogCore.BASE.info("dataSource2.hashCode={}", Util.objString(ds));
return ds;
}
/**装配路由数据源*/
@Bean(name="rwDataSource")
@Autowired
public DataSourceBeanWrapper<ReadWriteDataSourceRouter> rwDataSource(@Qualifier("dataSource1") DataSource dataSource1, @Qualifier("dataSource2") DataSource dataSource2) {
ReadWriteDataSourceRouter ds = new ReadWriteDataSourceRouter();
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(Master.class, dataSource1);
targetDataSources.put(Slave.class, dataSource2);
ds.setTargetDataSources(targetDataSources);
ds.setDefaultTargetDataSource(dataSource1);//默认数据源
DataSourceBeanWrapper<ReadWriteDataSourceRouter> t = new DataSourceBeanWrapper<>(ds);
return t;
}
@Bean
public SqlSessionFactory ds1SqlSessionFactory(DataSourceBeanWrapper<ReadWriteDataSourceRouter> dataSource1) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource1.getDataSource());
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mappers/**/*.xml"));
return bean.getObject();
}
@Bean(name = "ds1TransactionManager")
public DataSourceTransactionManager ds1TransactionManager(@Qualifier("rwDataSource") DataSourceBeanWrapper<ReadWriteDataSourceRouter> dataSource1) {
return new DataSourceTransactionManager(dataSource1.getDataSource());
}
@PostConstruct
void after(){
LogCore.BASE.info("myabtis multiple datasources configure after !");
}
}
public final class DataSourceBeanWrapper<T extends DataSource> implements InitializingBean{
private T dataSource;
public DataSourceBeanWrapper(T realDataSource) {
super();
this.dataSource = realDataSource;
}
public T getDataSource() {
return dataSource;
}
public void setDataSource(T dataSource) {
this.dataSource = dataSource;
}
@Override
public void afterPropertiesSet() throws Exception{
if (this.dataSource == null) {
throw new IllegalArgumentException("Property 'dataSources' is required");
}
if(this.dataSource instanceof InitializingBean){
((InitializingBean)dataSource).afterPropertiesSet();
}
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("DataSourceBeanWrapper =");
builder.append(getClass().getName() + "@" + Integer.toHexString(hashCode()));
builder.append("; dataSource=");
builder.append(dataSource);
return builder.toString();
}
}
解决方案2
Configuration
//@EnableTransactionManagement //事务
@MapperScan(basePackages = "com.vipkid.coursera.manager.dao")//scan
public class DataSourceConfig {
public DataSourceConfig(){
LogCore.BASE.info("myabtis multiple datasources configure start !");
}
@Bean(name="dataSource1")
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource1() {
DataSource ds = DataSourceBuilder.create().build();
LogCore.BASE.info("dataSource1.hashCode={}", Util.objString(ds));
return ds;
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource2() {
DataSource ds = DataSourceBuilder.create().build();
LogCore.BASE.info("dataSource2.hashCode={}", Util.objString(ds));
return ds;
}
/**
* 装配路由数据源
* @see {@link DataSourceBeanWrapper}
*/
@Bean
@DependsOn({ "dataSource1", "dataSource2"})
public DataSource rwDataSource() {
ReadWriteDataSourceRouter ds = new ReadWriteDataSourceRouter();
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(Master.class, dataSource1());
targetDataSources.put(Slave.class, dataSource2());
ds.setTargetDataSources(targetDataSources);
ds.setDefaultTargetDataSource(dataSource1());//默认数据源
LogCore.BASE.info("ReadWriteDataSourceRouter init ={}", ds);
return ds;
}
@Bean
public SqlSessionFactory ds1SqlSessionFactory(DataSource rwDataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(rwDataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mappers/**/*.xml"));
return bean.getObject();
}
@Bean(name = "ds1TransactionManager")
public DataSourceTransactionManager ds1TransactionManager(@Qualifier("rwDataSource") DataSource rwDataSource) {
return new DataSourceTransactionManager(rwDataSource);
}
@PostConstruct
void after(){
LogCore.BASE.info("myabtis multiple datasources configure after !");
}
}