package com.api.config.aspect;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
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.core.annotation.Order;
import org.springframework.stereotype.Component;
import com.api.config.annotation.DataSource;
import com.api.config.annotation.DataSourceMulti;
import com.api.config.dynamic.DynamicDataSourceContextHolder;
import com.api.config.enums.DataSourceType;
/**
* 数据切面,用于代理三种不同的数据集成
* @author
*
*/
@Order(1)
@Aspect
@Component
public class DataSourceAspect {
@Pointcut("@annotation(com.api.config.annotation.DataSource)")
public void dataSourcePointcut() {
}
@Pointcut("@annotation(com.api.config.annotation.DataSourceMulti)")
public void dataSourceMultiPointcut() {
}
@Pointcut("@annotation(com.api.config.annotation.DataSourceChange)")
public void dataSourceChangePointcut() {
}
/**
* 切换数据源的功能
* @Author K75022761
* @Date 下午4:50:54 2023年5月24日
* @param point
*/
@Around("dataSourcePointcut()")
public Object switchDataSource(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature) point.getSignature();
DataSource dataSource = signature.getMethod().getAnnotation(DataSource.class);
if (dataSource != null) {
String dataSourceName = dataSource.value().name();
DynamicDataSourceContextHolder.setDataSourceType(dataSourceName);
}
Object proceed = point.proceed();
DynamicDataSourceContextHolder.clearDataSourceType();
return proceed;
}
/**
* 切换数据源获取所有数据并返回
* @Author
* @Date 下午4:51:32 2023年5月24日
* @param point
* @return
* @throws Throwable
*/
@SuppressWarnings("unchecked")
@Around("dataSourceMultiPointcut()")
public Object executeMulti(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature) point.getSignature();
DataSourceMulti dataSourceMulti = signature.getMethod().getAnnotation(DataSourceMulti.class);
if (dataSourceMulti != null) {
List<Object> result = new ArrayList<>();
Method method = signature.getMethod();
if (method != null) {
Object[] args = point.getArgs();
Annotation[] annotations = method.getAnnotations();
for (Annotation annotation : annotations) {
if (annotation instanceof DataSourceMulti) {
DataSourceMulti dataSource = (DataSourceMulti) annotation;
DataSourceType[] value = dataSource.value();
for (int j = 0; j < value.length; j++) {
DynamicDataSourceContextHolder.setDataSourceType(value[j].name());
result.addAll((List<Object>) method.invoke(point.getTarget(), args));
}
}
}
}
return result;
}
return point.proceed();
}
/**
* 依据SN或者其他条件动态切换数据源
* @Author
* @Date 下午4:51:44 2023年5月24日
* @param point
* @return
* @throws Throwable
*/
@Around("dataSourceChangePointcut()")
public Object dataSourceChange(ProceedingJoinPoint point) throws Throwable {
Object[] args = point.getArgs();
//TODO add 获取source表内容
DataSourceType d = getDataSourceType(args);
DynamicDataSourceContextHolder.setDataSourceType(d.name());
Object proceed = point.proceed();
DynamicDataSourceContextHolder.clearDataSourceType();
return proceed;
}
private DataSourceType getDataSourceType(Object[] args) {
// TODO Auto-generated method stub
return DataSourceType.IFAS;
}
}
若依的数据源切换类
package com.api.config.dynamic;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 数据源切换处理
*
* @author ruoyi
*/
public class DynamicDataSourceContextHolder
{
public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);
/**
* 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
* 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
*/
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
/**
* 设置数据源的变量
*/
public static void setDataSourceType(String dsType)
{
log.info("切换到{}数据源", dsType);
CONTEXT_HOLDER.set(dsType);
}
/**
* 获得数据源的变量
*/
public static String getDataSourceType()
{
return CONTEXT_HOLDER.get();
}
/**
* 清空数据源变量
*/
public static void clearDataSourceType()
{
CONTEXT_HOLDER.remove();
}
}
自定义注解,基本上只是改个类名,总共三个
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface DataSource {
String value() default "";
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface DataSourceChange {
String value() default "";
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface DataSourceMulti {
String value() default "";
}
动态数据源类
package com.api.config.dynamic;
import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.annotation.Order;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import com.api.config.enums.DataSourceType;
//import com.api.config.interceptor.ReplaceSqlInterceptor;//参见上一篇博文
import com.api.utils.SpringUtils;
@Order(1)
@Configuration
@MapperScan(basePackages = {"com.api.dao.*"})
public class DynamicDataSourceConfig {
class DynamicDataSource extends AbstractRoutingDataSource
{
public final Logger log = LoggerFactory.getLogger(DynamicDataSource.class);
public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources)
{
super.setDefaultTargetDataSource(defaultTargetDataSource);
super.setTargetDataSources(targetDataSources);
super.afterPropertiesSet();
}
@Override
protected Object determineCurrentLookupKey()
{
log.info("使用{}数据源", DynamicDataSourceContextHolder.getDataSourceType());
return DynamicDataSourceContextHolder.getDataSourceType();
}
}
@Autowired
ReplaceSqlInterceptor replaceSqlInterceptor;
@Bean(name = "dynamicDataSource")
@Primary
public DynamicDataSource dataSource() {
Map<Object, Object> targetDataSources = new HashMap<>();
DataSource dataSource1 = SpringUtils.getBean("db1DataSource");
targetDataSources.put(DataSourceType.DB1.name(), dataSource1);
DataSource dataSource2 = SpringUtils.getBean("db2DataSource");
targetDataSources.put(DataSourceType.DB2.name(), dataSource2);
return new DynamicDataSource(dataSource1, targetDataSources);
}
@Bean("dynamicSqlSessionFactory")
@Autowired
@Primary
public SqlSessionFactory sqlSessionFactory(DynamicDataSource dynamicDataSource) throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
// PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
// sessionFactory.setMapperLocations(resolver.getResources("classpath*:mapping/*.xml"));
//增加插件,不用的话删掉
//Interceptor[] plugins = new Interceptor[]{replaceSqlInterceptor};
//sessionFactory.setPlugins(plugins);
return sessionFactory.getObject();
}
@Primary
@Bean
public SqlSessionTemplate testSqlSessionTemplate(@Qualifier("dynamicSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
@Bean("dynamicTransactionManager")
@Autowired
@Primary
public DataSourceTransactionManager transactionManager(DynamicDataSource dynamicDataSource) {
return new DataSourceTransactionManager(dynamicDataSource);
}
}
若依工具类
package com.api.utils;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* spring工具类 方便在非spring管理环境中获取bean
*
* @author ruoyi
*/
@Component
public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware
{
/** Spring应用上下文环境 */
private static ConfigurableListableBeanFactory beanFactory;
private static ApplicationContext applicationContext;
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
{
SpringUtils.beanFactory = beanFactory;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
{
SpringUtils.applicationContext = applicationContext;
}
/**
* 获取对象
*
* @param name
* @return Object 一个以所给名字注册的bean的实例
* @throws org.springframework.beans.BeansException
*
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) throws BeansException
{
return (T) beanFactory.getBean(name);
}
/**
* 获取类型为requiredType的对象
*
* @param clz
* @return
* @throws org.springframework.beans.BeansException
*
*/
public static <T> T getBean(Class<T> clz) throws BeansException
{
T result = (T) beanFactory.getBean(clz);
return result;
}
/**
* 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
*
* @param name
* @return boolean
*/
public static boolean containsBean(String name)
{
return beanFactory.containsBean(name);
}
/**
* 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
*
* @param name
* @return boolean
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
*
*/
public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException
{
return beanFactory.isSingleton(name);
}
/**
* @param name
* @return Class 注册对象的类型
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
*
*/
public static Class<?> getType(String name) throws NoSuchBeanDefinitionException
{
return beanFactory.getType(name);
}
/**
* 如果给定的bean名字在bean定义中有别名,则返回这些别名
*
* @param name
* @return
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
*
*/
public static String[] getAliases(String name) throws NoSuchBeanDefinitionException
{
return beanFactory.getAliases(name);
}
/**
* 获取aop代理对象
*
* @param invoker
* @return
*/
@SuppressWarnings("unchecked")
public static <T> T getAopProxy(T invoker)
{
return (T) AopContext.currentProxy();
}
/**
* 获取当前的环境配置,无配置返回null
*
* @return 当前的环境配置
*/
public static String[] getActiveProfiles()
{
return applicationContext.getEnvironment().getActiveProfiles();
}
/**
* 获取当前的环境配置,当有多个环境配置时,只获取第一个
*
* @return 当前的环境配置
*/
public static String getActiveProfile()
{
final String[] activeProfiles = getActiveProfiles();
return activeProfiles.length>0 ? activeProfiles[0] : null;
}
}