1:整体项目结构
2:pom.xml
3:application.yml配置文件
server:
port: 8080
spring:
datasource:
master:
driverClassName: com.mysql.jdbc.Driver
username: root
password: 123456
jdbc-url: jdbc:mysql://127.0.0.1:3306/master?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
type: com.alibaba.druid.pool.DruidDataSource
slave:
driverClassName: com.mysql.jdbc.Driver
username: root
password: 123456
jdbc-url: jdbc:mysql://127.0.0.1:3306/slave1?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
type: com.alibaba.druid.pool.DruidDataSource
mybatis:
mapper-locations: classpath:mapper/*.xml
4:controller层
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private IUserService userService;
// http://127.0.0.1:8080/users/1
@DataSource("master")
@GetMapping("/{id}")
public UserVO selectByPrimaryKey(@PathVariable("id") int id) {
return userService.selectByPrimaryKey(id);
}
// http://127.0.0.1:8080/users
@DataSource("slave1")
@GetMapping("")
public List<UserVO> selectAll() {
return userService.selectAll();
}
}
5:Service层
public interface IUserService {
UserVO selectByPrimaryKey(int id);
List<UserVO> selectAll();
}
@Service
public class UserService implements IUserService {
@Autowired
private UserDao userDao;
@Override
public UserVO selectByPrimaryKey(int id) {
return userDao.selectByPrimaryKey(id);
}
@Override
public List<UserVO> selectAll() {
return userDao.selectAll();
}
}
6:Dao层
@Mapper
public interface UserDao {
UserVO selectByPrimaryKey(int id);
List<UserVO> selectAll();
}
7 xml省略;
8:配置类:
a:注解类
@Target({ ElementType.METHOD, ElementType.TYPE, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
String value() default “master”; // 该值即key值
}
b:切面类
@Aspect
@Component
public class DynamicDataSourceAspect {
private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceAspect.class);
@Before("@annotation(ds)")
public void changeDataSource(JoinPoint point, DataSource ds) throws Throwable {
String dsId = ds.value();
if (DynamicDataSourceContextHolder.dataSourceIds.contains(dsId)) {
DynamicDataSourceContextHolder.setDataSourceRouterKey(dsId);
logger.debug("Use DataSource :{} >", dsId, point.getSignature());
} else {
logger.info("数据源[{}]不存在,使用默认数据源 >{}", dsId, point.getSignature());
DynamicDataSourceContextHolder.setDataSourceRouterKey(dsId);
}
}
@After("@annotation(ds)")
public void restoreDataSource(JoinPoint point, DataSource ds) {
logger.debug("Revert DataSource : " + ds.value() + " > " + point.getSignature());
DynamicDataSourceContextHolder.removeDataSourceRouterKey();
}
}
c:动态数据源类
public class DynamicDataSourceContextHolder {
private static Logger logger = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);
/**
* 存储已经注册的数据源的key
*/
public static List<String> dataSourceIds = new ArrayList<>();
/**
* 线程级别的私有变量
*/
private static final ThreadLocal<String> HOLDER = new ThreadLocal<>();
public static String getDataSourceRouterKey() {
return HOLDER.get();
}
public static void setDataSourceRouterKey(String dataSourceRouterKey) {
logger.info("切换至{}数据源", dataSourceRouterKey);
HOLDER.set(dataSourceRouterKey);
}
/**
* 设置数据源之前一定要先移除
*/
public static void removeDataSourceRouterKey() {
HOLDER.remove();
}
/**
* 判断指定DataSrouce当前是否存在
*
* @param dataSourceId
* @return
*/
public static boolean containsDataSource(String dataSourceId) {
return dataSourceIds.contains(dataSourceId);
}
}
public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
private static Logger logger = LoggerFactory.getLogger(DynamicRoutingDataSource.class);
@Override
protected Object determineCurrentLookupKey() {
String dataSourceName = DynamicDataSourceContextHolder.getDataSourceRouterKey();
logger.info("当前数据源是:{}", dataSourceName);
return DynamicDataSourceContextHolder.getDataSourceRouterKey();
}
}
/**
-
动态数据源注册 实现 ImportBeanDefinitionRegistrar 实现数据源注册 实现 EnvironmentAware
-
用于读取application.yml配置
*/
public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware {private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceRegister.class);
public static DataSource defaultDataSource;
/**
- 存储我们注册的数据源
*/
private Map<String, DataSource> customDataSources = new HashMap<String, DataSource>();
/**
- ImportBeanDefinitionRegistrar接口的实现方法,通过该方法可以按照自己的方式注册bean
- @param annotationMetadata
- @param beanDefinitionRegistry
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata,
BeanDefinitionRegistry beanDefinitionRegistry) {
DynamicDataSourceContextHolder.dataSourceIds.add(“master”);
logger.info(“注册默认数据源成功”);
// 获取其他数据源配置
Set keySet = customDataSources.keySet();
for (String key : keySet) {
DynamicDataSourceContextHolder.dataSourceIds.add(key);
logger.info(“注册数据源{}成功”, key);
}
// bean定义类
GenericBeanDefinition define = new GenericBeanDefinition();
// 设置bean的类型,此处DynamicRoutingDataSource是继承AbstractRoutingDataSource的实现类
define.setBeanClass(DynamicRoutingDataSource.class);
// 需要注入的参数
MutablePropertyValues mpv = define.getPropertyValues();
// 添加默认数据源,避免key不存在的情况没有数据源可用
mpv.add(“defaultTargetDataSource”, defaultDataSource);
// 添加其他数据源
mpv.add(“targetDataSources”, customDataSources);
// 将该bean注册为datasource,不使用springboot自动生成的datasource
beanDefinitionRegistry.registerBeanDefinition(“datasource”, define);
logger.info(“注册数据源成功,一共注册{}个数据源”, customDataSources.keySet().size() + 1);
}
/**
-
EnvironmentAware接口的实现方法,通过aware的方式注入,此处是environment对象
-
@param environment
*/
@Override
public void setEnvironment(Environment environment) {
logger.info(“开始注册数据源”);
try {
RelaxedPropertyResolver re = new RelaxedPropertyResolver(environment, “spring.datasource.master.”);
Map<String, String> map = new HashMap<>();
map.put(“url”, re.getProperty(“jdbc-url”));
map.put(“username”, re.getProperty(“username”));
map.put(“password”, re.getProperty(“password”));
map.put(“type”, re.getProperty(“type”));
map.put(“driverClassName”, re.getProperty(“driver-class-name”));
defaultDataSource = buildDataSource(map);re = new RelaxedPropertyResolver(environment, "spring.datasource.slave."); map.put("url", re.getProperty("jdbc-url")); map.put("username", re.getProperty("username")); map.put("password", re.getProperty("password")); map.put("type", re.getProperty("type")); map.put("driverClassName", re.getProperty("driver-class-name")); DataSource slave1DataSource = buildDataSource(map); customDataSources.put("slave1", slave1DataSource);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private DataSource buildDataSource(Map<String, String> map) throws ClassNotFoundException {
String url = map.get(“url”).toString();
String username = map.get(“username”).toString();
String password = map.get(“password”).toString();
String driverClassName = map.get(“driverClassName”).toString();
String type = map.get(“type”).toString();
DataSourceBuilder sourceBuilder = DataSourceBuilder.create().driverClassName(driverClassName).url(url)
.username(username).password(password).type((Class<? extends DataSource>) Class.forName(type));
return sourceBuilder.build();}
} - 存储我们注册的数据源
至此,启动测试:
数据库中
跟controller中
@DataSource(“master”)
@GetMapping("/{id}")
public UserVO selectByPrimaryKey(@PathVariable(“id”) int id) {
return userService.selectByPrimaryKey(id);
}
的确是请求到了master库;
@DataSource(“slave1”)
@GetMapping("")
public List selectAll() {
return userService.selectAll();
}
的确是请求到了slave1库;