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
接口解决,实现形式有三个
- DsHeaderProcessor: 从申请的header中获取ds数据源名称。
- DsSessionProcessor: 从session中获取数据源d名称
- DsSpelExpressionProcessor: 通过spel表达式获取ds数据源名称,
这3种形式采纳的是责任链形式间断获取的。顺次执行。
DsHeaderProcessor的拦挡格局为@DS("#header 变量名")@DS("#header dataName")List<Entity> List(@Param("query") String queryDsSessionProcessor的拦挡格局为@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