SpringBoot多数据源AOP动态切换数据源

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/shy415502155/article/details/89088732

1. 创建两个数据库master和slave

2. 在application.yml添加如下配置

spring:
  profiles: dev
  datasource:
    master:
      url: jdbc:mysql://localhost:3306/master?useUnicode=true&characterEncoding=GBK
      username: root
      password: 123456
      driver-class-name: com.mysql.jdbc.Driver
      type: com.alibaba.druid.pool.DruidDataSource
    slave:
      url: jdbc:mysql://localhost:3306/slave?useUnicode=true&characterEncoding=GBK
      username: root
      password: 123456
      driver-class-name: com.mysql.jdbc.Driver
      type: com.alibaba.druid.pool.DruidDataSource

3. 创建切换数据源注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/***
 * 
 * @Title: springstarter
 * @author shy
 * @Description 自定义注解
 * @data 2019年3月28日 下午3:52:49
 *
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface DS {
	 String value();
}

4.多数据源配置类

import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
/***
 * 
 * @Title: springstarter
 * @author shy
 * @Description 多数据源配置类
 * @data 2019年3月28日 下午3:56:13
 *
 */
@Configuration
public class DataSourceConfig {
	/***
	 * @Description: master数据源
	 * @param @return
	 * @return DataSource  
	 * @throws @throws
	 */
	@Bean(name = "master")
	@ConfigurationProperties(prefix = "spring.datasource.master") // application.properteis中对应属性的前缀
	public DataSource dataSourceMaster() {
		return DataSourceBuilder.create().build();
	}
	/***
	 * @Description: slave数据源
	 * @param @return
	 * @return DataSource  
	 * @throws @throws
	 */
	@Bean(name = "slave")
	@ConfigurationProperties(prefix = "spring.datasource.slave")
	public DataSource dataSourceSlave() {
		return DataSourceBuilder.create().build();
	}

	/***
	 * 
	 * @Description: 通过AOP在不同数据源之间动态切换
	 * @param @return
	 * @return DataSource
	 * @throws @throws
	 */
	@Primary
	@Bean(name = "dynamicDataSource")
	public DataSource dynamicDataSource() {
		DynamicDataSource dynamicDataSource = new DynamicDataSource();
		// 默认数据源
		dynamicDataSource.setDefaultTargetDataSource(dataSourceMaster());
		// 配置多数据源
		Map<Object, Object> dsMap = new HashMap<Object, Object>();
		dsMap.put("master", dataSourceMaster());
		dsMap.put("slave", dataSourceSlave());
		dynamicDataSource.setTargetDataSources(dsMap);
		return dynamicDataSource;
	}

	/**
	 * 配置@Transactional注解事物
	 * 
	 * @return
	 */
	@Bean
	public PlatformTransactionManager transactionManager() {
		return new DataSourceTransactionManager(dynamicDataSource());
	}

}

5. 动态切换数据源

/***
 * 
 * @Title: springstarter
 * @author shy
 * @Description 动态切换数据源;
 * @data 2019年3月28日 下午3:47:51
 *
 */
@Slf4j
public class DataSourceContextHolder {
	/***
	 * 默认数据源
	 */
    public static final String DEFAULT_DS = "master";
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
 
    /**
	 * @return the contextholder
	 */
	public static ThreadLocal<String> getContextholder() {
		return contextHolder;
	}
    
    // 设置数据源名
    public static void setDB(String dbType) {
        log.info("切换到{"+dbType+"}数据源");
        contextHolder.set(dbType);
    }
 
    // 获取数据源名
    public static String getDB() {
        return (contextHolder.get());
    }
 
    // 清除数据源名
    public static void clearDB() {
        contextHolder.remove();
    }
}

6. 获取当前数据源

public class DynamicDataSource extends AbstractRoutingDataSource {
	@Override
	protected Object determineCurrentLookupKey() {
		return DataSourceContextHolder.getDB();
	}
}

7. AOP的方式实现数据源动态切换

import java.lang.reflect.Method;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.reflect.MethodSignature;
/***
 * @Title: springstarter
 * @author shy
 * @Description AOP的方式实现数据源动态切换
 * @data 2019年3月28日 下午3:59:04
 *
 */
@Aspect
@Order(-10)//保证该AOP在@Transactional之前执行
@Component
@Slf4j
public class DynamicDataSourceAspect {

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

8. @DS使用

@DS不能在Service和dao层接口的方法上使用,而是在Service实现类中使用

@DS("slave")
@Override
public List<Map<String, Object>> getLoggerInfoList(String targetName, Integer action, Page page) {
	Integer startSize = page.getStartSize(page.getPageNum(), page.getPageSize());
	List<Map<String, Object>> loggerInfoList = loggerMapper.getLoggerInfoList(targetName, action, startSize, page.getPageSize());
	return loggerInfoList;
}
@DS("master")
@Override
public List<User> getUserListByPage(Page page)  throws Exception {
	Integer pageSatrtSize = page.getStartSize(page.getPageNum(), page.getPageSize());
	Integer pageSize = page.getPageSize();
	String order = "user_id";
	UserExample example = new UserExample();
	//Criteria criteria = example.createCriteria();
	int count = userMapper.countByExample(example);
	page.setTotalSize(count);
	List<User> userList = userMapper.getUserListByPage(pageSatrtSize, pageSize, order);
	String sexStr = "";
	if (userList != null && userList.size() > 0) {
		for (User user : userList) {
			Date birthday = user.getBirthday();
			String birthdayStr = CommonUtils.dateFormat(birthday, Constant.YYMMDD);
			user.setBirthdayStr(birthdayStr);
			Integer sex = user.getSex() == null ? Constant.SECRECY_SEX : user.getSex();
			if (sex.equals(Constant.SECRECY_SEX)) {
				sexStr = Constant.SECRECY;
			} else if (sex.equals(Constant.MAN_SEX)) {
				sexStr = Constant.MAN;
			} else if (sex.equals(Constant.WOMAN_SEX)) {
				sexStr = Constant.WOMAN;
			}
			user.setSexStr(sexStr);
		}
	}
	return userList;
}

9. 结果

slave数据源:

master数据源:

猜你喜欢

转载自blog.csdn.net/shy415502155/article/details/89088732
今日推荐