由于公司项目需求,研究了一下如何在项目不重启的情况下,修改了数据源配置后能正常使用新的数据源配置。在百度查找解决方案时找到了动态修改数据源和动态刷新配置两种方式,但是都不适合我的实际场景。百度找不到解决方案,自己尝试解析springboot自动加载配置数据源的源码,依然没有找到。最后无意间看DruidDataSource 的源码时发现里面有个restart方法就可以实现,只需要监听到配置有改动过,刷新数据源配置,执行restart方法即可实现不重启项目加载最新的数据源配置,闲话说到这,接下来实际操作。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>3.0.0.M3</version>
</dependency>
<!-- 阿里的druid 依赖log4j -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<!-- 引入mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
<version>5.1.46</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
spring.datasource.username=root
spring.datasource.password=
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
@SpringBootApplication
@EnableTransactionManagement
@ComponentScan("com")
@MapperScan("com.example.demo.datasource.mapper")
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
DruidDataSource master = SpringUtils.getBean("dataSource");
master.setUrl("jdbc:mysql://192.168.64.145:3306/test?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false");
master.setUsername("root");
master.setPassword("123456");
master.setDriverClassName("com.mysql.jdbc.Driver");
master.restart();
SpringUtils:
@Component
public class SpringUtils implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringUtils.applicationContext = applicationContext;
}
public static <T> T getBean(String beanName) {
if(applicationContext.containsBean(beanName)){
return (T) applicationContext.getBean(beanName);
}else{
return null;
}
}
public static <T> Map<String, T> getBeansOfType(Class<T> baseType){
return applicationContext.getBeansOfType(baseType);
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
}
@RestController
public class TestController {
@Resource
private UserMapper userMapper;
@GetMapping("/query")
public String query() {
return userMapper.selectAll().get(0).getName();
}
@GetMapping("/changeSource")
public String changeSource() {
try {
DruidDataSource master = SpringUtils.getBean("master");
master.setUrl("jdbc:mysql://192.168.64.145:3306/test?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false");
master.setUsername("root");
master.setPassword("123456");
master.setDriverClassName("com.mysql.jdbc.Driver");
master.restart();
} catch (SQLException e) {
e.printStackTrace();
}
return "success";
}
}
master.db.driverClassName=com.mysql.jdbc.Driver
master.db.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
master.db.username=root
master.db.password=
@Configuration
public class MasterDataSourceConfig {
@Value("${master.db.url}")
private String url;
@Value("${master.db.username}")
private String username;
@Value("${master.db.password}")
private String password;
@Value("${master.db.driverClassName}")
private String driverClassName;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getDriverClassName() {
return driverClassName;
}
public void setDriverClassName(String driverClassName) {
this.driverClassName = driverClassName;
}
}
@Configuration
public class DataSourceComponent {
@Resource
private MasterDataSourceConfig masterDataSourceConfig;
@Bean(name = "master")
public DataSource masterDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(masterDataSourceConfig.getUrl());
dataSource.setUsername(masterDataSourceConfig.getUsername());
dataSource.setPassword(masterDataSourceConfig.getPassword());
dataSource.setDriverClassName(masterDataSourceConfig.getDriverClassName());
return dataSource;
}
@Primary//不加这个会报错。
@DependsOn({ "master"}) //解决数据库循环依赖问题
@Bean(name = "multiDataSource")
public MultiRouteDataSource exampleRouteDataSource() {
MultiRouteDataSource multiDataSource = new MultiRouteDataSource();
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put("master", masterDataSource());
multiDataSource.setTargetDataSources(targetDataSources);
multiDataSource.setDefaultTargetDataSource(masterDataSource());
return multiDataSource;
}
/**
* 注册ServletRegistrationBean
*
* @return
*/
@Bean
public ServletRegistrationBean druidServlet() {
ServletRegistrationBean reg = new ServletRegistrationBean();
reg.setServlet(new StatViewServlet());
reg.addUrlMappings("/druid/*");
reg.addInitParameter("allow", ""); // 白名单 return reg;
reg.addInitParameter("loginUsername", "root");
reg.addInitParameter("loginPassword", "123456");
reg.addInitParameter("resetEnable", "false");
return reg;
}
/**
* 注册FilterRegistrationBean
*
* @return
*/
@Bean
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(new WebStatFilter());
filterRegistrationBean.addUrlPatterns("/*");
filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
filterRegistrationBean.addInitParameter("profileEnable", "true");
filterRegistrationBean.addInitParameter("principalCookieName", "USER_COOKIE");
filterRegistrationBean.addInitParameter("principalSessionName", "USER_SESSION");
filterRegistrationBean.addInitParameter("DruidWebStatFilter", "/*");
return filterRegistrationBean;
}
// 注意这里的bean名称要和自定义的名称一致,否则无法找到对应的实例
DruidDataSource master = SpringUtils.getBean("master");
master.setUrl("jdbc:mysql://192.168.64.145:3306/test?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false");
master.setUsername("root");
master.setPassword("123456");
master.setDriverClassName("com.mysql.jdbc.Driver");
master.restart();
// 加上exclude = {DataSourceAutoConfiguration.class} 关闭自动加载数据源
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@EnableTransactionManagement
@ComponentScan("com")
@MapperScan("com.example.demo.datasource.mapper")
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
写到最后,不知道有没有遗漏,如哪里写的不通或者不对,请大神们指正并多包涵