1.配置项(数据库驱动省略)
1.1 bootstrap-local.yml
server :
port : 7700
spring :
datasource :
siyuaneApp :
url : xx
username : x x x x
password : sa
driver-class-name : com.microsoft.sqlserver.jdbc.SQLServerDriver
siyuaneop :
url : x x x x
username : sa
password : x x
driver-class-name : com.microsoft.sqlserver.jdbc.SQLServerDriver
druid :
driver-class-name : com.mysql.jdbc.Driver
url : x x x x
username : x x
password : xx
management :
endpoints :
web :
exposure :
include : '*'
mybatis :
config-location : classpath: mybatis- config.xml
mapper-locations : classpath: /mapper/*.xml
logging :
level :
com.alibaba.nacos.client.naming : WARN
1.2 配置数据源DynamicDataSourceConfig
package com. pipe. system. dynamicdatasource. config ;
import com. alibaba. druid. spring. boot. autoconfigure. DruidDataSourceAutoConfigure ;
import com. pipe. system. dynamicdatasource. DynamicDataSource ;
import com. pipe. system. dynamicdatasource. constant. DataSourceConstants ;
import lombok. extern. slf4j. Slf4j ;
import org. springframework. boot. autoconfigure. EnableAutoConfiguration ;
import org. springframework. boot. autoconfigure. jdbc. DataSourceAutoConfiguration ;
import org. springframework. boot. jdbc. DataSourceBuilder ;
import org. springframework. context. annotation. Bean ;
import org. springframework. context. annotation. Configuration ;
import org. springframework. context. annotation. Primary ;
import org. springframework. jdbc. core. JdbcTemplate ;
import javax. annotation. Resource ;
import javax. sql. DataSource ;
import java. util. HashMap ;
import java. util. Map ;
@Slf4j
@Configuration
@EnableAutoConfiguration ( exclude = { DataSourceAutoConfiguration . class , DruidDataSourceAutoConfigure . class } )
public class DynamicDataSourceConfig {
@Resource
private DataSourcePropertiesConfig dataSourcePropertiesConfig;
private DataSource dataSource ( DataSourceProperties dataSourceProperties) {
return DataSourceBuilder . create ( )
. driverClassName ( dataSourceProperties. getDriverClassName ( ) )
. url ( dataSourceProperties. getUrl ( ) )
. username ( dataSourceProperties. getUsername ( ) )
. password ( dataSourceProperties. getPassword ( ) )
. build ( ) ;
}
@Bean
@Primary
public DataSource dynamicDataSource ( ) {
Map < Object , Object > dataSourceMap = new HashMap < > ( 16 ) ;
log. info ( "dataSourcePropertiesConfig: {}" , dataSourcePropertiesConfig) ;
Map < String , DataSourceProperties > dataSourcePropertiesMap = dataSourcePropertiesConfig. getDataSourceConfig ( ) ;
dataSourcePropertiesMap. forEach ( ( merchant, properties) -> dataSourceMap. put ( merchant, dataSource ( properties) ) ) ;
DynamicDataSource dynamicDataSource = new DynamicDataSource ( ) ;
dynamicDataSource. setTargetDataSources ( dataSourceMap) ;
dynamicDataSource. setDefaultTargetDataSource ( dataSourceMap. get ( DataSourceConstants . DS_KEY_MASTER ) ) ;
log. info ( "动态数据源dynamicDataSource: {}" , dynamicDataSource) ;
return dynamicDataSource;
}
@Primary
@Bean
public JdbcTemplate jdbcTemplate ( ) {
return new JdbcTemplate ( dynamicDataSource ( ) ) ;
}
}
1.3 动态数据源选择上下文DynamicDataSourceContextHolder
package com. pipe. system. dynamicdatasource ;
import com. pipe. system. dynamicdatasource. constant. DataSourceConstants ;
public class DynamicDataSourceContextHolder {
private static final ThreadLocal < String > DATASOURCE_CONTEXT_KEY_HOLDER = new ThreadLocal < > ( ) ;
public static void setContextKey ( String key) {
DATASOURCE_CONTEXT_KEY_HOLDER . set ( key) ;
}
public static String getContextKey ( ) {
String key = DATASOURCE_CONTEXT_KEY_HOLDER . get ( ) ;
return key == null ? DataSourceConstants . DS_KEY_MASTER : key;
}
public static void removeContextKey ( ) {
DATASOURCE_CONTEXT_KEY_HOLDER . remove ( ) ;
}
}
1.4 设置DynamicDataSource
路由策略
package com. pipe. system. dynamicdatasource ;
import org. springframework. jdbc. datasource. lookup. AbstractRoutingDataSource ;
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey ( ) {
return DynamicDataSourceContextHolder . getContextKey ( ) ;
}
}
1.5 动态读取nacos配置中心数据库配置DataSourcePropertiesConfig
package com. pipe. system. dynamicdatasource. config ;
import lombok. Setter ;
import lombok. extern. slf4j. Slf4j ;
import org. springframework. boot. context. properties. ConfigurationProperties ;
import org. springframework. cloud. context. config. annotation. RefreshScope ;
import java. util. * ;
@Slf4j
@RefreshScope
@ConfigurationProperties ( prefix = DataSourcePropertiesConfig . PREFIX )
public class DataSourcePropertiesConfig {
public static final String PREFIX = "spring" ;
@Setter
private Map < String , Map < String , String > > datasource;
public Map < String , DataSourceProperties > getDataSourceConfig ( ) {
Map < String , DataSourceProperties > resultMap = new HashMap < > ( 16 ) ;
datasource. forEach ( ( merchant, setConfig) -> {
DataSourceProperties dataSourceProperties = DataSourceProperties . builder ( )
. driverClassName ( setConfig. get ( "driver-class-name" ) )
. url ( setConfig. get ( "url" ) )
. username ( setConfig. get ( "username" ) )
. password ( setConfig. get ( "password" ) ) . build ( ) ;
log. info ( "nacos配置文件数据库配置 :{}" , dataSourceProperties) ;
resultMap. put ( merchant, dataSourceProperties) ;
} ) ;
log. info ( "多数据源最终map :{}" , resultMap) ;
return resultMap;
}
}
1.6 数据库连接属性DataSourceProperties
@Data
@Builder
public class DataSourceProperties {
private String driverClassName;
private String url;
private String username;
private String password;
}
1.7 constants
public interface DataSourceConstants {
String DS_KEY_MASTER = "druid" ;
}
2.动态数据源注解切面
@Target ( { ElementType . METHOD , ElementType . TYPE } )
@Retention ( RetentionPolicy . RUNTIME )
public @interface DS {
String value ( ) default DataSourceConstants . DS_KEY_MASTER ;
}
package com. pipe. system. dynamicdatasource. aspect ;
import com. pipe. system. dynamicdatasource. DynamicDataSourceContextHolder ;
import com. pipe. system. dynamicdatasource. annotation. DS ;
import lombok. extern. slf4j. Slf4j ;
import org. aspectj. lang. ProceedingJoinPoint ;
import org. aspectj. lang. annotation. Around ;
import org. aspectj. lang. annotation. Aspect ;
import org. aspectj. lang. annotation. Pointcut ;
import org. aspectj. lang. reflect. MethodSignature ;
import org. springframework. stereotype. Component ;
import java. util. Objects ;
@Slf4j
@Component
@Aspect
public class DynamicDataSourceAspect {
@Pointcut ( "@annotation(com.pipe.system.dynamicdatasource.annotation.DS)" )
public void dataSourcePointCut ( ) {
}
@Around ( "dataSourcePointCut()" )
public Object around ( ProceedingJoinPoint joinPoint) throws Throwable {
String dsKey = getDSAnnotation ( joinPoint) . value ( ) ;
DynamicDataSourceContextHolder . setContextKey ( dsKey) ;
try {
return joinPoint. proceed ( ) ;
} finally {
DynamicDataSourceContextHolder . removeContextKey ( ) ;
}
}
private DS getDSAnnotation ( ProceedingJoinPoint joinPoint) {
Class < ? > targetClass = joinPoint. getTarget ( ) . getClass ( ) ;
DS dsAnnotation = targetClass. getAnnotation ( DS . class ) ;
if ( Objects . nonNull ( dsAnnotation) ) {
return dsAnnotation;
} else {
MethodSignature methodSignature = ( MethodSignature ) joinPoint. getSignature ( ) ;
return methodSignature. getMethod ( ) . getAnnotation ( DS . class ) ;
}
}
}
3.使用
@Slf4j
@RestController
@RequestMapping ( "/v1.0/slaver" )
public class SalverContoller {
@Autowired
private JdbcTemplate jdbcTemplate;
@PostMapping ( "/nologin/sendSms" )
@DS ( "siyuaneop" )
public CommonResult sendSms ( ) {
List < Map < String , Object > > list = jdbcTemplate. queryForList ( "select * from table where mobile = 'xxxx'" ) ;
System . out. println ( "list" ) ;
return CommonResult . success ( list) ;
}
}