spring-boot结合AOP实现数据源动态配置的方法

本篇内容主要讲解“spring-boot结合AOP实现数据源动态配置的方法”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“spring-boot结合AOP实现数据源动态配置的方法”吧!

网站建设哪家好,找创新互联建站!专注于网页设计、网站建设、微信开发、微信小程序开发、集团企业网站建设等服务项目。为回馈新老客户创新互联还提供了金秀免费建站欢迎大家使用!

Spring-Boot+AOP方式实现多数据源切换

设计总体思路:Spring-Boot+AOP方式实现多数据源切换,继承AbstractRoutingDataSource实现数据源动态的获取,在service层使用注解指定数据源。

一、多数据源配置

在application.properties中,我们的配置是这样的

#主数据源
druid.master.url=jdbc:MySQL://url/masterdb?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull
druid.master.username=xxx
druid.master.password=123
druid.master.driver-class-name=com.mysql.jdbc.Driver
druid.master.max-wait=5000
druid.master.max-active=100
druid.master.test-on-borrow=true
druid.master.validation-query=SELECT 1

#从数据源
druid.slave.url=jdbc:mysql://url/slavedb?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull
druid.slave.username=xxx
druid.slave.password=123
druid.slave.driver-class-name=com.mysql.jdbc.Driver
druid.slave.max-wait=5000
druid.slave.max-active=100
druid.slave.test-on-borrow=true
druid.slave.validation-query=SELECT 1

读取配置

<!-- master数据源 -->
<bean primary="true" id="masterdb" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
    <!-- 基本属性 url、user、password -->
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="${druid.master.url}"/>
    <property name="username" value="${druid.master.username}"/>
    <property name="password" value="${druid.master.password}"/>
    <!-- 配置初始化最大 -->
    <property name="maxActive" value="${druid.master.max-active}"/>
    <!-- 配置获取连接等待超时的时间 -->
    <property name="maxWait" value="${druid.master.max-wait}"/>
    <property name="validationQuery" value="${druid.master.validation-query}"/>
    <property name="testOnBorrow" value="${druid.master.test-on-borrow}"/>

</bean>

<!-- slave数据源 -->
<bean primary="true" id="slavedb" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
    <!-- 基本属性 url、user、password -->
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="${druid.slave.url}"/>
    <property name="username" value="${druid.slave.username}"/>
    <property name="password" value="${druid.slave.password}"/>

    <!-- 配置初始化大小、最小、最大 -->
    <property name="maxActive" value="${druid.slave.max-active}"/>
    <!-- 配置获取连接等待超时的时间 -->
    <property name="maxWait" value="${druid.slave.max-wait}"/>
    <property name="validationQuery" value="${druid.slave.validation-query}"/>
    <property name="testOnBorrow" value="${druid.slave.test-on-borrow}"/>
</bean>

<!-- 动态数据源,根据service接口上的注解来决定取哪个数据源 -->
<bean id="dataSource" class="datasource.DynamicDataSource">
    <property name="targetDataSources">
        <map key-type="java.lang.String">
            <entry key="slave" value-ref="slavedb"/>
            <entry key="master" value-ref="masterdb"/>
        </map>
    </property>
    <property name="defaultTargetDataSource" ref="masterdb"/>
</bean>

<!-- Spring JdbcTemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource" />
</bean>

<!-- Spring事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
</bean>

<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
    <property name="transactionManager" ref="transactionManager"/>
</bean>

<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" order="2" />

<!-- depositdbSqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="mapperLocations" value="classpath*:mapper-xxdb/*Mapper*.xml" />
</bean>

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="xxdb.mapper"/>
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
二、动态数据源

spring为我们提供了AbstractRoutingDataSource,即带路由的数据源。继承后我们需要实现它的determineCurrentLookupKey(),该方法用于自定义实际数据源名称的路由选择方法,由于我们将信息保存到了ThreadLocal中,所以只需要从中拿出来即可。

public class DynamicDataSource extends AbstractRoutingDataSource {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Override
    protected Object determineCurrentLookupKey() {
        String dataSource = JdbcContextHolder.getDataSource();
        logger.info("数据源为{}",dataSource);
        return dataSource;
    }

}
三. 数据源动态切换类

动态数据源切换是基于AOP的,所以我们需要声明一个AOP切面,并在切面前做数据源切换,切面完成后移除数据源名称。

@Aspect
@Order(1)   //设置AOP执行顺序(需要在事务之前,否则事务只发生在默认库中)
@Component
public class DataSourceAspect {

    private Logger logger = LoggerFactory.getLogger(this.getClass());
    //切点
    @Pointcut("execution(* com.xxx.service.*.*(..))")
    public void aspect() { }

    @Before("aspect()")
    private void before(JoinPoint point) {
        Object target = point.getTarget();
        String method = point.getSignature().getName();
        Class<?> classz = target.getClass();// 获取目标类
        Class<?>[] parameterTypes = ((MethodSignature) point.getSignature())
                .getMethod().getParameterTypes();
        try {
            Method m = classz.getMethod(method, parameterTypes);
            if (m != null && m.isAnnotationPresent(MyDataSource.class)) {
                MyDataSource data = m.getAnnotation(MyDataSource.class);
                logger.info("method :{},datasource:{}",m.getName() ,data.value().getName());
                JdbcContextHolder.putDataSource(data.value().getName());// 数据源放到当前线程中
            }
        } catch (Exception e) {
            logger.error("get datasource error ",e);
            //默认选择master
            JdbcContextHolder.putDataSource(DataSourceType.Master.getName());// 数据源放到当前线程中
        }

    }

    @AfterReturning("aspect()")
    public void after(JoinPoint point) {
        JdbcContextHolder.clearDataSource();
    }
}
四、数据源管理类
public class JdbcContextHolder {

    private final static ThreadLocal<String> local = new ThreadLocal<>();

    public static void putDataSource(String name) {
        local.set(name);
    }

    public static String getDataSource() {
        return local.get();
    }

    public static void clearDataSource() {
        local.remove();
    }
}
五、数据源注解和枚举

我们切换数据源时,一般都是在调用具体接口的方法前实现,所以我们定义一个方法注解,当AOP检测到方法上有该注解时,根据注解中value对应的名称进行切换。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyDataSource {

    DataSourceType value();

}

public enum  DataSourceType {
    // 主表
    Master("master"),
    // 从表
    Slave("slave");

    private String name;

    private DataSourceType(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
六、切点注解

由于我们的动态数据源配置了默认库,所以如果方法是操作默认库的可以不需要注解,如果要操作非默认数据源,我们需要在方法上添加@MyDataSource("数据源名称")注解,这样就可以利用AOP实现动态切换了

@Component
public class xxxServiceImpl {
    @Resource
    private XxxMapperExt xxxMapperExt;

    @MyDataSource(value= DataSourceType.Slave)
    public List<Object> getAll(){
        return xxxMapperExt.getAll();
    }

}

原文链接:

http://tech.dianwoda.com/2018/03/28/spring-boot-aopfang-shi-shi-xian-duo-shu-ju-yuan-qie-huan/

到此,相信大家对“spring-boot结合AOP实现数据源动态配置的方法”有了更深的了解,不妨来实际操作一番吧!这里是创新互联网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

网站栏目:spring-boot结合AOP实现数据源动态配置的方法
文章分享:https://www.cdcxhl.com/article26/gssicg.html

成都网站建设公司_创新互联,为您提供网站维护网站排名面包屑导航营销型网站建设网站制作品牌网站建设

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联

手机网站建设