springboot+mybatis配置多数据源并利用aop实现自动切换
1.项目大致结构

2.pom依赖
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
          <scope>runtime</scope>
      </dependency>
      <dependency>
          <groupId>org.mybatis.spring.boot</groupId>
          <artifactId>mybatis-spring-boot-starter</artifactId>
          <version>2.0.1</version>
      </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>
       
       <!--aop依赖 项目中使用了aop,实现数据源的自动切换-->
  	   <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
        </dependency>
3.yml配置文件
server:
  port: 8087
  servlet:
        context-path: / #项目路径
 
spring: 
  datasource: 
    main: 
      username: root
      password: jiajia123
      url: jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
      driver-class-name: com.mysql.jdbc.Driver
    secondary: 
      username: root
      password: jiajia123
      url: jdbc:mysql://localhost:3306/db2?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
      driver-class-name: com.mysql.jdbc.Driver
    
mybatis:
  mapper-locations: classpath:com/oauth/server/mapper/*Mapper.xml
  type-aliases-package: com.oauth.server.entity
  configuration:      
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #sql日志打印
        call-setters-on-nulls: true #解决返回类型为Map的时候如果值为null将不会封装此字段
在启动类中修改@SpringBootApplication这个配置为:
@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})
意思要去掉springboot默认的数据源配置,使用自己定义的数据源配置。
4.DS注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * 
 * @author 硅谷探秘者(jia)
 *
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DS {
    String value() default "main";
}
        这个注解是用在service层方法上的,从配置文件中可以看出,配置了两个数据源,主数据源main和辅数据源secondary,如果service方法想访问main这个数据源,则不需要任何配置它默认会访问main数据源,或在方法上加个@DS("main")注解,如果想访问辅数据源secondary则需要在方法上加上@DS("secondary")注解。
例子:
	//访问主数据源
    public void test() {
		// TODO Auto-generated method stub
		testMapper.test();
	}
    //访问辅数据源
	@DS("secondary")
	public void test2() {
		// TODO Auto-generated method stub
		testMapperDb2.test();
	}
        注意这个注解一定是要在运行是可以访问到的,所以要加上@Retention(RetentionPolicy.RUNTIME)这个元注解。
5.数据源配置
/**
 * @author 硅谷探秘者(jia)
 */
public class DataSourceContextHolder {
    /**
     * 默认数据源
     */
    public static final String DEFAULT_DS = "main";
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
    // 设置数据源名
    public static void setDB(String dbType) {
        contextHolder.set(dbType);
    }
    // 获取数据源名
    public static String getDB() {
        return (contextHolder.get());
    }
    // 清除数据源名
    public static void clearDB() {
        contextHolder.remove();
    }
}
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
 * @author 硅谷探秘者(jia)
 */
public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDB();
    }
}
主要配置
package com.oauth.server.conf;
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;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
 * @author 硅谷探秘者(jia)
 */
@Configuration
public class DataSourceConfig {
	/**
	 * 	主数据源配置
	 * @return
	 */
    @Bean(name = "main")
    @ConfigurationProperties(prefix = "spring.datasource.main")
    public DataSource main() {
        return DataSourceBuilder.create().build();
    }
    /**
     * 	辅数据源配置
     * @return
     */
    @Bean(name = "secondary")
    @ConfigurationProperties(prefix = "spring.datasource.secondary")
    public DataSource secondary() {
        return DataSourceBuilder.create().build();
    }
    /**
     * 动态数据源: 通过AOP在不同数据源之间动态切换
     *
     * @return
     */
    @Primary
    @Bean(name = "dynamicDS1")
    public DataSource dataSource() {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        // 默认数据源
        dynamicDataSource.setDefaultTargetDataSource(main());
        // 配置多数据源
        Map<Object, Object> dsMap = new HashMap<Object, Object>(5);
        dsMap.put("main", main());
        dsMap.put("secondary", secondary());
        dynamicDataSource.setTargetDataSources(dsMap);
        return dynamicDataSource;
    }
    /**
     * 配置@Transactional注解事物
     * @return
     */
    @Bean
    public PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource());
    }
}
aop配置,拦截带@DS注解的方法
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.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
 * @author 硅谷探秘者(jia)
 */
@Aspect
@Component
public class DynamicDataSourceAspect {
    @Before("@annotation(DS)")
    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.DEFAULT_DS;
        try {
            // 得到访问的方法对象
            Method method = className.getMethod(methodName, argClass);
            System.out.println(method.getName());
            System.out.println(method.isAnnotationPresent(DS.class));
            // 判断是否存在@DS注解
            if (method.isAnnotationPresent(DS.class)) {
                DS annotation = method.getAnnotation(DS.class);
                // 取出注解中的数据源名
                dataSource = annotation.value();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(dataSource);
        // 切换数据源
        DataSourceContextHolder.setDB(dataSource);
    }
    @After("@annotation(DS)")
    public void afterSwitchDS(JoinPoint point){
        DataSourceContextHolder.clearDB();
    }
}
这样配置基本完成,使用的时候只需要在方法上添加@DS注解,标明你要访问的数据源即可。
server层例子
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.oauth.server.conf.DS;
import com.oauth.server.mapper.TestMapper;
import com.oauth.server.mapper.TestMapperDb2;
@Service
public class TestService {
	@Autowired
	private TestMapper testMapper;
	@Autowired
	private TestMapperDb2 testMapperDb2;
	public void test() {
		// TODO Auto-generated method stub
		testMapper.test();
	}
	@DS("secondary")
	public void test2() {
		// TODO Auto-generated method stub
		testMapperDb2.test();
	}
}