SpringBoot2.1.5+Druid实现多数据源AOP动态切换

应用场景:
项目需要同时连接多个不同的数据库A, B,并且它们都需要读写操作
项目技术选项:
SpringBoot_ssm
数据库:MySQL 、SQL server

看网上有很多类似笔记,但有些繁琐,这里简单说明,记个笔记。
以此记录:

依赖

这里仅仅展示部份关键依赖

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.0.0</version>
        </dependency>
        
        <!-- 导入Mysql数据库链接jar包 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.30</version>
        </dependency>
        
        <!--SQL server数据库jar-->
        <dependency>
            <groupId>com.microsoft.sqlserver</groupId>
            <artifactId>sqljdbc4</artifactId>
            <version>4.0</version>
        </dependency>
        
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>

数据库配置

#====================数据源MySQL配置===================#
spring.datasource.mysql.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.mysql.url=jdbc\:mysql\://127.0.0.1\:3306/数据库名?useUnicode\=true&characterEncoding\=UTF-8
spring.datasource.mysql.username=账号
spring.datasource.mysql.password=密码
#====================SQL Server=======================#
spring.datasource.sqlserver.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver
spring.datasource.sqlserver.url=jdbc:sqlserver://127.0.0.1:1433;DatabaseName=xinsong
spring.datasource.sqlserver.username=sa
spring.datasource.sqlserver.password=root
#====================druid连接池=======================#
# 使用druid连接池
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.minIdle= 1
spring.datasource.maxActive=20

# 说明:这里省略其他诸多配置。。。。。

在SpringBoot中配置数据源(这里不采用spring boot自带的DataSourceAutoConfiguration)

1.创建DataSourceConfig类,主要用于配置所需要用到的数据库

@Configuration
public class DataSourceConfig {

     //MySQL作为主数据源(默认数据源)
	@Bean(name="mysql")
	@Primary//  注意:这里需要该注解声明是默认数据源
	@ConfigurationProperties(prefix="spring.datasource.mysql")
	public DataSource mysqlDateSource(){
		return DruidDataSourceBuilder.create().build();
	}
	@Bean(name="sqlserver")
	@ConfigurationProperties(prefix="spring.datasource.sqlserver")
	public DataSource sqlServerDateSource(){
		return DruidDataSourceBuilder.create().build();
	}
	@Bean(name = "dynamicDataSource")
	public DataSource dynamicDataSource() {
		DynamicDataSource dynamicDataSource = new DynamicDataSource();
		// 默认数据源
		dynamicDataSource.setDefaultTargetDataSource(mysqlDateSource());
		// 配置多数据源
		Map<Object, Object> dataBaseMap = new HashMap<>(16);
		dataBaseMap.put("sqlserver", sqlServerDateSource());
		dataBaseMap.put("mysql", mysqlDateSource());
		dynamicDataSource.setTargetDataSources(dataBaseMap);
		return dynamicDataSource;
	}

	@Bean(name="sqlServerSqlSessionFactory")
	public SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicDataSource")DataSource dataSource) throws Exception {
		SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
		bean.setDataSource(dataSource);
		return bean.getObject();
	}
	@Bean(name="sqlServerTransactionManager")//事务管理@Transactional(TransactionManager="mysqlTransactionManager")
	public PlatformTransactionManager platformTransactionManager(@Qualifier("dynamicDataSource")DataSource dataSource) {
		return new DataSourceTransactionManager(dataSource);
	}
	@Bean(name="sqlServerSqlSessionTemplate")
	public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlServerSqlSessionFactory")SqlSessionFactory sqlSessionFactory) throws Exception {
		return new SqlSessionTemplate(sqlSessionFactory);
	}

2.duird配置

@Configuration
public class DruidConfig {

    @Bean
    @ConditionalOnMissingBean
    public ServletRegistrationBean druidServlet() {
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
        //白名单
        servletRegistrationBean.addInitParameter("allow", "192.168.6.195");
        //IP黑名单(存在共同时,deny优先于allow) : 如果满足deny的话提示:Sorry, you are not permitted to view this page.
        servletRegistrationBean.addInitParameter("deny", "192.168.6.73");
        //用于登陆的账号密码
        servletRegistrationBean.addInitParameter("loginUsername", "admin");
        servletRegistrationBean.addInitParameter("loginPassword", "admin");
        //是否能重置数据
        servletRegistrationBean.addInitParameter("resetEnable", "true");
        return servletRegistrationBean;
    }

    /**
     * @Date: 2019/5/29 20:51
     * @Description: 注册filter信息,用于拦截
     */
    @Bean
    public FilterRegistrationBean<WebStatFilter> filterRegistrationBean() {
        //创建过滤器
        FilterRegistrationBean<WebStatFilter> filterRegistrationBean = new FilterRegistrationBean<>(new WebStatFilter());
        //设置过滤器过滤路径
        filterRegistrationBean.addUrlPatterns("/*");
        //忽略过滤得形式
        filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
        return filterRegistrationBean;
    }

}

3.实现AOP动态切换,需要先创建一个注解:@DataSource
创建:

import java.lang.annotation.*;
/**
 * @author ttllihao
 * @description: AOP切换数据源注解,默认值mysql,即MySQL是默认主数据源
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
    String value() default "mysql";
}

4.动态数据源上下文.

/**
 * @author ttllihao
 * @description: 动态数据源上下文.
 */
@Component
public class DataSourceContextHolder {
    /**
     * 线程独立
     */
    private static  ThreadLocal<String> contextHolder = new ThreadLocal<String>();

    public static final String DB_DEFAULT__MySQL = "mysql";
    public static final String DB_TYPE_SQLServer = "sqlserver";

    public static  String getDataBaseType() {
        return contextHolder.get();
    }

    public static  void setDataBaseType(String dataBase) {
        contextHolder.set(dataBase);
    }
    public static  void clearDataBaseType() {
        contextHolder.remove();
    }


/**
 * @author ttllihao
 * @description: 获取数据源名
 */
public class DynamicDataSource extends AbstractRoutingDataSource {
    private Logger logger =  LogManager.getLogger("TestController");


    @Override
    protected Object determineCurrentLookupKey() {
        logger.info("当前数据源:{}"+ DataSourceContextHolder.getDataBaseType());
        return DataSourceContextHolder.getDataBaseType();
    }

6.最重要的一步

**
 * @author ttllihao
 * @description: 动态切换数据源类
 */
@Aspect
@Component
@Order(-10)
public class DynamicDataSourceAspect {
    private Logger logger =  LogManager.getLogger("DynamicDataSourceAspect");


        @Before("@annotation(DataSource)")
        public void beforeSwitchDS(JoinPoint point){
            //获得当前访问的class
            Class<?> className = point.getTarget().getClass();
            //获得访问的方法名
            String methodName = point.getSignature().getName();
            //得到方法的参数的类型
            Class[] argClass = ((MethodSignature)point.getSignature()).getParameterTypes();
            String dataSource = DataSourceContextHolder.DB_DEFAULT__MySQL;
            try {
                // 得到访问的方法对象
                Method method = className.getMethod(methodName, argClass);
                // 判断是否存在@DS注解
                if (method.isAnnotationPresent(DataSource.class)) {
                    DataSource annotation = method.getAnnotation(DataSource.class);
                    // 取出注解中的数据源名
                    dataSource = annotation.value();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            // 切换数据源
            DataSourceContextHolder.setDataBaseType(dataSource);
        }

        @After("@annotation(DataSource)")
        public void afterSwitchDS(JoinPoint point){
            DataSourceContextHolder.clearDataBaseType();
        }


    }

使用:在impl层方法上面加入注解,默认(主)数据源可以不写注解,写注解也可以不填value值
在这里插入图片描述

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值