您现在的位置是:首页 > 文章详情

spring boot多数据源、读写分离( AOP动态)

日期:2018-11-29点击:898

项目地址


springboot读写分离


应用场景


    读写分离,多数据源,主从库


具体实现


1、配置文件application.yml

##多数据源 datasource:   #主库   master:     jdbcUrl: jdbc:mysql://localhost:3306/pa_yqs_game?useUnicode=true&characterEncoding=utf-8     username: root     password: root     driver-class-name: com.mysql.jdbc.Driver   #从库   slave:     #并非url而是jdbcUrl(因为这个在获取数据源时一直报错,看了DataSource的属性才知道是jdbcUrl)     jdbcUrl: jdbc:mysql://localhost:3306/data_count?useUnicode=true&characterEncoding=utf-8     username: root     password: root     driver-class-name: com.mysql.jdbc.Driver        ##mybatis mybatis:   mapper-locations: classpath:mapper/*.xml   type-aliases-package: com.example.*.domain.pojo   check-config-location: true   config-location: classpath:mybatis-config.xml


2、Application启动类

/**  * **在最外侧,即包含所有子包   * @ClassName Application  * @Description 入口,启动类  * @author lide  * @date 2018年2月9日 下午2:47:04  */ @SpringBootApplication(exclude = {         DataSourceAutoConfiguration.class }) @EnableTransactionManagement(order = 2) //设置事务执行顺序(需要在切换数据源之后,否则只走默认库) @MapperScan(basePackages = "com.example.*.domain.mapper") public class Application {   public static void main(String[] args) { SpringApplication.run(Application.class, args); } }


 1.  需要设置事务(本质也是AOP)执行的顺序,否则事务的执行顺序高于后续的AOP,会导致动态切换数据源失效


 2. exclude = {DataSourceAutoConfiguration.class}用于禁用掉默认的数据源获取方式,默认会读取配置文件的据源(spring.datasource.*)


 3. MapperScan()指向的是mapper


3、代码实现


3.1 DataSourceType数据源枚举

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; } }


3.2 数据源上下文JdbcContextHolder(ThreadLocal)

public class JdbcContextHolder { private final static ThreadLocal local = new ThreadLocal<>(); public static void putDataSource(String name) { local.set(name); } public static String getDataSource() { return local.get(); } }


3.3 AbstractRoutingDataSource实现类DynamicDataSource(关键)


AbstractRoutingDataSource抽象类知识,实现AOP动态切换的关键

  1. AbstractRoutingDataSource中determineTargetDataSource()方法中获取数据源 

Object lookupKey = determineCurrentLookupKey();

DataSource dataSource = this.resolvedDataSources.get(lookupKey);

根据determineCurrentLookupKey()得到Datasource,并且此方法是抽象方法,应用可以实现

    2.resolvedDataSources的值根据targetDataSources所得

afterPropertiesSet()方法中(在@Bean所在方法执行完成后,会调用此方法):

Map.Entry

    3.然后在xml中使用

    4.利用自定义注解,AOP拦截动态的设置ThreadLocal的值

    5.在DAO层与数据库建立连接时会根据ThreadLocal的key得到数据源

代码:getConnection()

determineTargetDataSource().getConnection();(determineTargetDataSource返回的是DataSource)

public class DynamicDataSource extends AbstractRoutingDataSource {   private Logger logger = LoggerFactory.getLogger(this.getClass()); @Override protected Object determineCurrentLookupKey() { logger.info("数据源为{}",JdbcContextHolder.getDataSource()); return JdbcContextHolder.getDataSource(); } }


3.4 AOP切换数据源 DataSourceAspect

@Aspect @Order(1) //设置AOP执行顺序(需要在事务之前,否则事务只发生在默认库中) @Component public class DataSourceAspect { private Logger logger = LoggerFactory.getLogger(this.getClass()); //切点 @Pointcut("execution(* com.example.*.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);                   JdbcContextHolder.putDataSource(data.value().getName());                  logger.info("===============上下文赋值完成:{}",data.value().getName());             }           } catch (Exception e) {               e.printStackTrace();           }            } }


3.5 自定义注解MyDataSource

@Retention(RetentionPolicy.RUNTIME)   @Target(ElementType.METHOD)  public @interface MyDataSource {   DataSourceType value() default DataSourceType.Master; //默认主表 }


3.6 数据源配置


注意@Primary标注多数据源,否则会产生实现类冲突。


@Primary和@Qualifier这两个注解的意思:


@Primary:  意思是在众多相同的bean中,优先使用用@Primary注解的bean.

@Qualifier : 这个注解则指定某个bean有没有资格进行注入。


@Configuration public class DataSourceConfig {   @Bean(name = "master") @ConfigurationProperties(prefix = "datasource.master")  public DataSource dataSource1() { System.out.println("主配"); return DataSourceBuilder.create().build(); }   @Bean(name = "slave") @ConfigurationProperties(prefix = "datasource.slave")  public DataSource dataSource2() { System.out.println("从配"); return DataSourceBuilder.create().build(); } @Bean(name="dynamicDataSource") @Primary //优先使用,多数据源 public DataSource dataSource() { DynamicDataSource dynamicDataSource = new DynamicDataSource(); DataSource master = dataSource1(); DataSource slave = dataSource2(); //设置默认数据源 dynamicDataSource.setDefaultTargetDataSource(master); //配置多数据源 Map map = new HashMap<>(); map.put(DataSourceType.Master.getName(), master); //key需要跟ThreadLocal中的值对应 map.put(DataSourceType.Slave.getName(), slave); dynamicDataSource.setTargetDataSources(map); return dynamicDataSource; } }


原文链接:https://blog.roncoo.com/article/133953
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章