MyBatis-Plus 动态数据源需要引入jar包

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
    <version>3.3.1</version>
</dependency>

使用@DS()注解,括号内的值是我们配置的数据源名称,通常使用的时候是配置到mapper层的类上;如果没有该注解则使用的是默认数据库;
@DS 的优先级也是就近原则,如果类上已有@DS且他的某个方法也有@DS那么该方法使用的数据库为方法上声明的数据库;
有些时候不同库中有相同的表(对应一个实体类),需要在运行时动态选择数据源。我们可以在使用@DS注解的时候,使用#通配符,将我们的数据库以参数的方式来启用;#参数名称可以动态的使用我们的参数变量或者header和session中的变量;

‘#’参数的实现依赖于com.baomidou.dynamic.datasource.processorDsProcessor接口处理,实现方式有三个

  1. DsHeaderProcessor: 从请求的header中获取ds数据源名称。
  2. DsSessionProcessor: 从session中获取数据源d名称
  3. DsSpelExpressionProcessor: 通过spel表达式获取ds数据源名称,

这3种方式采用的是责任链方式连续获取的。依次执行。


DsHeaderProcessor的拦截格式为@DS("#header 变量名")
@DS("#header dataName")
List<Entity> List(@Param("query") String query

DsSessionProcessor的拦截格式为@DS("#session 变量名")
@DS("#session dataName")
List<Entity> List(@Param("query") String query

@DS("#dataName")
List<Entity> List(@Param("query") String query, String dataName);

其实现原理为DynamicDataSourceAutoConfiguration配置类Advisor

  @Bean
  @ConditionalOnMissingBean
  public DynamicDataSourceAnnotationAdvisor dynamicDatasourceAnnotationAdvisor(DsProcessor dsProcessor) {
    DynamicDataSourceAnnotationInterceptor interceptor = new DynamicDataSourceAnnotationInterceptor();
    interceptor.setDsProcessor(dsProcessor);
    DynamicDataSourceAnnotationAdvisor advisor = new DynamicDataSourceAnnotationAdvisor(interceptor);
    advisor.setOrder(properties.getOrder());
    return advisor;
  }

DynamicDataSourceAnnotationAdvisor的切点为DS注解

  private Pointcut buildPointcut() {
    Pointcut cpc = new AnnotationMatchingPointcut(DS.class, true);
    Pointcut mpc = AnnotationMatchingPointcut.forMethodAnnotation(DS.class);
    return new ComposablePointcut(cpc).union(mpc);
  }

拦截器为DynamicDataSourceAnnotationInterceptor,拦截器会处理以#开头的注解值,并交由
DsProcessor来处理

  private static final String DYNAMIC_PREFIX = "#";

  private String determineDatasource(MethodInvocation invocation) throws Throwable {
    Method method = invocation.getMethod();
    DS ds = method.isAnnotationPresent(DS.class)
        ? method.getAnnotation(DS.class)
        : AnnotationUtils.findAnnotation(RESOLVER.targetClass(invocation), DS.class);
    String key = ds.value();
    return (!key.isEmpty() && key.startsWith(DYNAMIC_PREFIX)) ? dsProcessor
        .determineDatasource(invocation, key) : key;
  }

DsHeaderProcessor处理逻辑为从request的header中取变量值:

  private static final String HEADER_PREFIX = "#header";

  @Override
  public boolean matches(String key) {
    return key.startsWith(HEADER_PREFIX);
  }

  @Override
  public String doDetermineDatasource(MethodInvocation invocation, String key) {
    HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
        .getRequestAttributes()).getRequest();
//这一行可以看出HEADER_PREFIX 和数据源之间需要空一位,可以使任意字符
    return request.getHeader(key.substring(8));
  }

DsSessionProcessor 从seesion中去取变量的值:

  private static final String SESSION_PREFIX = "#session";

  @Override
  public boolean matches(String key) {
    return key.startsWith(SESSION_PREFIX);
  }

  @Override
  public String doDetermineDatasource(MethodInvocation invocation, String key) {
    HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
        .getRequestAttributes()).getRequest();
//这一行可以看出SESSION_PREFIX 和数据源之间需要空一位,可以使任意字符
    return request.getSession().getAttribute(key.substring(9)).toString();
  }

DsSpelExpressionProcessor:


  /**
   * 参数发现器
   */
  private static final ParameterNameDiscoverer NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
  /**
   * Express语法解析器
   */
  private static final ExpressionParser PARSER = new SpelExpressionParser();

  @Override
  public boolean matches(String key) {
    return true;
  }

  @Override
  public String doDetermineDatasource(MethodInvocation invocation, String key) {
    Method method = invocation.getMethod();
    Object[] arguments = invocation.getArguments();
    EvaluationContext context = new MethodBasedEvaluationContext(null, method, arguments,
        NAME_DISCOVERER);
    final Object value = PARSER.parseExpression(key).getValue(context);
    return value == null ? null : value.toString();
  }

具体的实现原理可以参考 Spring 动态数据源,源码入口为:`
com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration


tao不是哭脸
119 声望3 粉丝