autobahn-java-master,禁用自动配置
Spring Boot大量使用自動配置和默認配置,極大地減少了代碼,通常只需要加上幾個注解,并按照默認規則設定一下必要的配置即可。例如,配置JDBC,默認情況下,只需要配置一個spring.datasource:
spring:
datasource:
url: jdbc:hsqldb:file:testdb
username: sa
password:
dirver-class-name: org.hsqldb.jdbc.JDBCDriver
Spring Boot就會自動創建出DataSource、JdbcTemplate、DataSourceTransactionManager,非常方便。
但是,有時候,我們又必須要禁用某些自動配置。例如,系統有主從兩個數據庫,而Spring Boot的自動配置只能配一個,怎么辦?
這個時候,針對DataSource相關的自動配置,就必須關掉。我們需要用exclude指定需要關掉的自動配置:
@SpringBootApplication
// 啟動自動配置,但排除指定的自動配置:
@EnableAutoConfiguration(exclude = DataSourceAutoConfiguration.class)
public class Application {
...
}
現在,Spring Boot不再給我們自動創建DataSource、JdbcTemplate和DataSourceTransactionManager了,要實現主從數據庫支持,怎么辦?
讓我們一步一步開始編寫支持主從數據庫的功能。首先,我們需要把主從數據庫配置寫到application.yml中,仍然按照Spring Boot默認的格式寫,但datasource改為datasource-master和datasource-slave:
spring:
datasource-master:
url: jdbc:hsqldb:file:testdb
username: sa
password:
dirver-class-name: org.hsqldb.jdbc.JDBCDriver
datasource-slave:
url: jdbc:hsqldb:file:testdb
username: sa
password:
dirver-class-name: org.hsqldb.jdbc.JDBCDriver
注意到兩個數據庫實際上是同一個庫。如果使用MySQL,可以創建一個只讀用戶,作為datasource-slave的用戶來模擬一個從庫。
下一步,我們分別創建兩個HikariCP的DataSource:
public class MasterDataSourceConfiguration {
@Bean("masterDataSourceProperties")
@ConfigurationProperties("spring.datasource-master")
DataSourceProperties dataSourceProperties() {
return new DataSourceProperties();
}
@Bean("masterDataSource")
DataSource dataSource(@Autowired @Qualifier("masterDataSourceProperties") DataSourceProperties props) {
return props.initializeDataSourceBuilder().build();
}
}
public class SlaveDataSourceConfiguration {
@Bean("slaveDataSourceProperties")
@ConfigurationProperties("spring.datasource-slave")
DataSourceProperties dataSourceProperties() {
return new DataSourceProperties();
}
@Bean("slaveDataSource")
DataSource dataSource(@Autowired @Qualifier("slaveDataSourceProperties") DataSourceProperties props) {
return props.initializeDataSourceBuilder().build();
}
}
注意到上述class并未添加@Configuration和@Component,要使之生效,可以使用@Import導入:
@SpringBootApplication
@EnableAutoConfiguration(exclude = DataSourceAutoConfiguration.class)
@Import({ MasterDataSourceConfiguration.class, SlaveDataSourceConfiguration.class})
public class Application {
...
}
此外,上述兩個DataSource的Bean名稱分別為masterDataSource和slaveDataSource,我們還需要一個最終的@Primary標注的DataSource,它采用Spring提供的AbstractRoutingDataSource,代碼實現如下:
class RoutingDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
// 從ThreadLocal中取出key:
return RoutingDataSourceContext.getDataSourceRoutingKey();
}
}
RoutingDataSource本身并不是真正的DataSource,它通過Map關聯一組DataSource,下面的代碼創建了包含兩個DataSource的RoutingDataSource,關聯的key分別為masterDataSource和slaveDataSource:
public class RoutingDataSourceConfiguration {
@Primary
@Bean
DataSource dataSource(
@Autowired @Qualifier("masterDataSource") DataSource masterDataSource,
@Autowired @Qualifier("slaveDataSource") DataSource slaveDataSource) {
var ds = new RoutingDataSource();
// 關聯兩個DataSource:
ds.setTargetDataSources(Map.of(
"masterDataSource", masterDataSource,
"slaveDataSource", slaveDataSource));
// 默認使用masterDataSource:
ds.setDefaultTargetDataSource(masterDataSource);
return ds;
}
@Bean
JdbcTemplate jdbcTemplate(@Autowired DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
@Bean
DataSourceTransactionManager dataSourceTransactionManager(@Autowired DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
仍然需要自己創建JdbcTemplate和PlatformTransactionManager,注入的是標記為@Primary的RoutingDataSource。
這樣,我們通過如下的代碼就可以切換RoutingDataSource底層使用的真正的DataSource:
RoutingDataSourceContext.setDataSourceRoutingKey("slaveDataSource");
jdbcTemplate.query(...);
只不過寫代碼切換DataSource即麻煩又容易出錯,更好的方式是通過注解配合AOP實現自動切換,這樣,客戶端代碼實現如下:
@Controller
public class UserController {
@RoutingWithSlave //
@GetMapping("/profile")
public ModelAndView profile(HttpSession session) {
...
}
}
實現上述功能需要編寫一個@RoutingWithSlave注解,一個AOP織入和一個ThreadLocal來保存key。由于代碼比較簡單,這里我們不再詳述。
如果我們想要確認是否真的切換了DataSource,可以覆寫determineTargetDataSource()方法并打印出DataSource的名稱:
class RoutingDataSource extends AbstractRoutingDataSource {
...
@Override
protected DataSource determineTargetDataSource() {
DataSource ds = super.determineTargetDataSource();
logger.info("determin target datasource: {}", ds);
return ds;
}
}
訪問不同的URL,可以在日志中看到兩個DataSource,分別是HikariPool-1和hikariPool-2:
2020-06-14 17:55:21.676 INFO 91561 --- [nio-8080-exec-7] c.i.learnjava.config.RoutingDataSource : determin target datasource: HikariDataSource (HikariPool-1)
2020-06-14 17:57:08.992 INFO 91561 --- [io-8080-exec-10] c.i.learnjava.config.RoutingDataSource : determin target datasource: HikariDataSource (HikariPool-2)
我們用一個圖來表示創建的DataSource以及相關Bean的關系:
┌────────────────────┐ ┌──────────────────┐
│@Primary │
│RoutingDataSource │ └──────────────────┘
│ ┌────────────────┐ │ ┌──────────────────┐
│ │MasterDataSource│ │
│ └────────────────┘ │ │TransactionManager│
│ ┌────────────────┐ │ └──────────────────┘
│ │SlaveDataSource │ │
│ └────────────────┘ │
└────────────────────┘
注意到DataSourceTransactionManager和JdbcTemplate引用的都是RoutingDataSource,所以,這種設計的一個限制就是:在一個請求中,一旦切換了內部數據源,在同一個事務中,不能再切到另一個,否則,DataSourceTransactionManager和JdbcTemplate操作的就不是同一個數據庫連接。
練習
小結
可以通過@EnableAutoConfiguration(exclude = {...})指定禁用的自動配置;
可以通過@Import({...})導入自定義配置。
總結
以上是生活随笔為你收集整理的autobahn-java-master,禁用自动配置的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 本地缓存之王——Caffeine 组件最
- 下一篇: 三、64位win7系统解决itunes“