springboot多数据源相关:使用多个自定义注解完成切换数据源,根据指定条件切换数据源,将多个数据源的结果集合并

使用多个自定义注解完成切换数据源,根据指定条件切换数据源,将多个数据源的结果集合并

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;
    }
}

你可能感兴趣的:(spring,boot,spring,java)