java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Spring 拦截器多数据源切换

Spring通过拦截器实现多数据源切换的示例代码

作者:南姜先生

本文主要介绍了Spring拦截器实现多数据源动态切换,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

在 Spring 应用中,特别是使用了多数据源的场景下,可以通过**拦截器(Interceptor)**机制来实现动态数据源切换。Spring 提供了多种方式来管理多个数据源,并允许开发者根据业务逻辑的需求灵活地选择不同的数据源进行数据库操作。

下面介绍如何通过拦截器实现多数据源切换的基本思路和具体步骤。

🧠 一句话总结:

✅ 通过自定义 HandlerInterceptor 或 MyBatis 的 Interceptor 拦截请求或 SQL 执行过程,在合适的时机动态设置当前线程的数据源,从而实现基于请求或方法级别的多数据源切换。

🔍 一、基本思路

  1. 配置多个数据源:首先需要在 Spring 配置类中定义多个 DataSource Bean。
  2. 动态数据源路由:创建一个代理数据源,它能够根据某些条件(如当前线程中的某个标识符)选择实际使用的数据源。
  3. 拦截请求或方法调用:使用 Spring MVC 的 HandlerInterceptor 或 MyBatis 的 Interceptor 来拦截请求或方法调用,在执行之前设置当前线程的数据源标识符。
  4. 清除上下文:确保在请求结束后清理线程局部变量,避免影响后续请求。

🛠️ 二、具体实现步骤

1.定义多个数据源

@Configuration
public class DataSourceConfig {

    @Bean(name = "primaryDataSource")
    @Primary
    @ConfigurationProperties(prefix = "spring.datasource.primary")
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "secondaryDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.secondary")
    public DataSource secondaryDataSource() {
        return DataSourceBuilder.create().build();
    }
}

2.创建动态数据源路由

public class DynamicDataSource extends AbstractRoutingDataSource {

    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();

    @Override
    protected Object determineCurrentLookupKey() {
        return contextHolder.get();
    }

    public static void setDataSource(String dataSource) {
        contextHolder.set(dataSource);
    }

    public static String getDataSource() {
        return contextHolder.get();
    }

    public static void clearDataSource() {
        contextHolder.remove();
    }
}

3.注册动态数据源

@Configuration
public class DynamicDataSourceConfig {

    @Autowired
    @Qualifier("primaryDataSource")
    private DataSource primaryDataSource;

    @Autowired
    @Qualifier("secondaryDataSource")
    private DataSource secondaryDataSource;

    @Bean(name = "dynamicDataSource")
    public DataSource dynamicDataSource() {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put("primary", primaryDataSource);
        targetDataSources.put("secondary", secondaryDataSource);

        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        dynamicDataSource.setTargetDataSources(targetDataSources);
        dynamicDataSource.setDefaultTargetDataSource(primaryDataSource); // 设置默认数据源
        return dynamicDataSource;
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);
        return sessionFactory.getObject();
    }
}

4.编写拦截器

a.Spring MVC 拦截器

@Component
public class DataSourceInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 根据请求路径或其他条件设置数据源
        if (request.getRequestURI().startsWith("/api/secondary")) {
            DynamicDataSource.setDataSource("secondary");
        } else {
            DynamicDataSource.setDataSource("primary");
        }
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        DynamicDataSource.clearDataSource(); // 清理线程局部变量
    }
}

b.MyBatis 拦截器

如果你想要基于 MyBatis 的 @Intercepts 注解来实现,则可以这样做:

@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
             @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
public class DataSourceInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        try {
            // 假设有一个方法可以根据某些条件确定要使用的数据源
            String dataSourceKey = determineDataSourceKey(invocation.getMethod().getName());
            DynamicDataSource.setDataSource(dataSourceKey);

            return invocation.proceed();
        } finally {
            DynamicDataSource.clearDataSource();
        }
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
    }
}

5.注册拦截器

a.Spring MVC 拦截器注册

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private DataSourceInterceptor dataSourceInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(dataSourceInterceptor).addPathPatterns("/**");
    }
}

b.MyBatis 拦截器注册

如果你使用的是 MyBatis 拦截器,可以在配置类中添加如下代码:

@Configuration
public class MyBatisConfig {

    @Autowired
    private DataSourceInterceptor myBatisDataSourceInterceptor;

    @Bean
    public SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);
        sessionFactory.setPlugins(new Interceptor[]{myBatisDataSourceInterceptor});
        return sessionFactory.getObject();
    }
}

📦 三、注意事项

到此这篇关于Spring通过拦截器实现多数据源切换的示例代码的文章就介绍到这了,更多相关Spring 拦截器多数据源切换内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文