开发者

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

目录
  • 一句话总结:
  • 一、基本思路
  • ️ 二、具体实现步骤
    • 1.定义KvMiQmNH多个数据源
    • 2.创建动态数据源路由
    • 3.注册动态数据源
    • 4.编写拦截器
      • a.Spring MVC 拦截器
      • b.MyBATis 拦截器
    • 5.注册拦截器
      • a.Spring MVC 拦截器注册
      • b.MyBatis 拦截器注册
  • 三、注意事项

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

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

    一句话总结:

    ✅ 通过自定义 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();
        }
    }
    • determineCurrentLohttp://www.devze.comokupKey() 方法返回当前应该使用的数据源标识符。
    • 使用 ThreadLocal 变量存储每个线程的数据源标识符。

    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.clhttp://www.devze.comass, 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.clearDataSourpythonce();
            }
        }
    
        @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();
        }
    }

    三、注意事项

    • 线程安全:由于我们使用了 ThreadLocal 来存储数据源标识符,因此在每次请求结束时都必须清理该变量,以防止内存泄漏或跨请求污染。
    • 事务管理:如果涉及到事务,需确保事务与数据源绑定正确。通常情况下,Spring 的事务管理器会自动处理这个问题,但如果你手动管理事务,可能需要额外注意。
    • 性能考虑:频繁地切换数据源可能会带来一定的性能开销,尤其是在高并发场景下。因此,应尽量减少不必要的切换频率。
    • 异常处理:确保在发生异常时也能正确地清理上下文,避免潜在的问题。

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

    0

    上一篇:

    下一篇:

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    最新开发

    开发排行榜