Springboot项目使用AbstractRoutingDataSource的问题

问题描述

我的项目使用springboot + springDataJpa + shiro。

因为我的服务器数据库使用master和salve方法,所以我需要调用代码以连接到这两个数据库,所以我设计使用AbstractRoutingDataSource + aop方法。现在我有一个问题,我认为可能是四郎造成的。

我知道连接切换是通过 AbstractRoutingDataSource getconnection()方法执行的,并且我无法手动控制此方法。现在的问题是,我的getconnection()在接口请求中最多执行两次。让我发布我的代码并描述它:

        @Order(0)
        @Aspect
        @Component
        public class RoutingAopAspect {
        
            @Around("@annotation(targetDataSource)")
            public Object routingWithDataSource(ProceedingJoinPoint joinPoint,TargetDataSource targetDataSource) throws Throwable {
                try {
                    DynamicRoutingDataSourceContext.setRoutingDataSource(targetDataSource.value());
                    return joinPoint.proceed();
                } finally {
                    DynamicRoutingDataSourceContext.removeRoutingDataSource();
                }
            }
        }
    
    public class DynamicRoutingDataSourceContext {
    
        public static final String MASTER = "master";
    
        public static final String SLAVE = "slave";
    
        private static final ThreadLocal<Object> threadLocalDataSource = new ThreadLocal<>();
    
    
        public static void setRoutingDataSource(Object dataSource) {
            if (dataSource == null) {
                throw new NullPointerException();
            }
            threadLocalDataSource.set(dataSource);
            // System.err.println(Thread.currentThread().getName()+" set RoutingDataSource : " + dataSource);
        }
    
            public static Object getRoutingDataSource() {
                Object dataSourceType = threadLocalDataSource.get();
                if (dataSourceType == null) {
                    threadLocalDataSource.set(DynamicRoutingDataSourceContext.MASTER);
                    return getRoutingDataSource();
                }
                // System.err.println(Thread.currentThread().getName()+" get RoutingDataSource : " + dataSourceType);
                return dataSourceType;
            }
        
            public static void removeRoutingDataSource() {
                threadLocalDataSource.remove();
                // System.err.println(Thread.currentThread().getName()+" remove RoutingDataSource");
            }
        }
    
        @EnableTransactionManagement
        @Configuration
        public class DataSourceConfig {
        
            @Value("${datasource.master.url}")
            private String masterUrl;
        
            @Value("${datasource.master.username}")
            private String masterUsername;
        
            @Value("${datasource.master.password}")
            private String masterPassword;
        
            @Value("${dataSource.driverClass}")
            private String masterDriverClassName;
        
            @Value("${datasource.slave.url}")
            private String slaveUrl;
        
            @Value("${datasource.slave.username}")
            private String slaveUsername;
        
            @Value("${datasource.slave.password}")
            private String slavePassword;
        
            @Value("${dataSource.driverClass}")
            private String slaveDriverClassName;
        
            @Bean(name = "masterDataSource")
            public DataSource masterDataSource(){
                DruidDataSource datasource = new DruidDataSource();
                datasource.setUrl(masterUrl);
                datasource.setUsername(masterUsername);
                datasource.setPassword(masterPassword);
                datasource.setDriverClassName(masterDriverClassName);
                return datasource;
            }
        
            @Bean(name = "slaveDataSource")
            public DataSource slaveDataSource(){
                DruidDataSource datasource = new DruidDataSource();
                datasource.setUrl(slaveUrl);
                datasource.setUsername(slaveUsername);
                datasource.setPassword(slavePassword);
                datasource.setDriverClassName(slaveDriverClassName);
                return datasource;
            }
        
            @Primary
            @Bean
            public DynamicRoutingDataSource dynamicDataSource(@Qualifier(value = "masterDataSource") DataSource masterDataSource,@Qualifier(value = "slaveDataSource") DataSource slaveDataSource) {
                Map<Object,Object> targetDataSources = new HashMap<>(2);
                targetDataSources.put(DynamicRoutingDataSourceContext.MASTER,masterDataSource);
                targetDataSources.put(DynamicRoutingDataSourceContext.SLAVE,slaveDataSource);
                DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource();
                dynamicRoutingDataSource.setTargetDataSources(targetDataSources);
                dynamicRoutingDataSource.setDefaultTargetDataSource(masterDataSource);
                dynamicRoutingDataSource.afterPropertiesSet();
                return dynamicRoutingDataSource;
            }
        }


public class DynamicRoutingDataSourceContext {

    public static final String MASTER = "master";

    public static final String SLAVE = "slave";

    private static final ThreadLocal<Object> threadLocalDataSource = new ThreadLocal<>();


    public static void setRoutingDataSource(Object dataSource) {
        if (dataSource == null) {
            throw new NullPointerException();
        }
        threadLocalDataSource.set(dataSource);
        // System.err.println(Thread.currentThread().getName()+" set RoutingDataSource : " + dataSource);
    }

    public static Object getRoutingDataSource() {
        Object dataSourceType = threadLocalDataSource.get();
        if (dataSourceType == null) {
            threadLocalDataSource.set(DynamicRoutingDataSourceContext.MASTER);
            return getRoutingDataSource();
        }
        // System.err.println(Thread.currentThread().getName()+" get RoutingDataSource : " + dataSourceType);
        return dataSourceType;
    }

    public static void removeRoutingDataSource() {
        threadLocalDataSource.remove();
        // System.err.println(Thread.currentThread().getName()+" remove RoutingDataSource");
    }
}

这是AbstractRoutingDataSource的相关基本配置。

我定义了一个方面来获取方法 @TargetDataSource 的参数。此参数是当前需要执行的数据源。我认为我的配置没有问题。

然后,我将在服务方法上使用@TargetDataSource,并使用shiro,shiro的doGetAuthorizationInfo()方法和doGetAuthenticationInfo()在服务之前执行,并且这两种方法都需要调用userservice。 那么现在的问题是,在调用doGetAuthorizationInfo()和doGetAuthenticationInfo()方法之后,它们将自动执行AbstractRoutingDataSource的getconnection()方法来切换数据源,然后执行到我自己的服务,而不执行getconnection( ) 方法。 ,这就是我所说的getconnection()在接口请求中最多执行两次。

    @Slf4j
    @Component
    public class ShiroRealm extends AuthorizingRealm {
    
        @Autowired
        @Lazy
        private UserService userService;
        @Autowired
        CacheUtil cacheUtil;
    
        @Override
        public boolean supports(AuthenticationToken token) {
            return token instanceof JwtToken;
        }
    
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
            String username = JwtUtil.getClaim(principals.toString(),"username");
            User user = userService.getUserByUsername(username);
            SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
            simpleAuthorizationInfo.addRole(user.getRole());
            return simpleAuthorizationInfo;
        }

        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) {
            String token = (String) auth.getCredentials();
            String username = JwtUtil.getClaim(token,"username");
            if (username == null) {
                throw new AuthenticationException("token invalid");
            }
    
            User user = userService.getUserByUsername(username);
            if (user == null) {
                throw new AuthenticationException("User didn't existed!");
            }
            if (JwtUtil.verify(token,username,user.getpassword(),TokenType.ACCESS_TOKEN) &&
                    cacheUtil.hasKey(CacheKey.ACCESS_TOKEN_KEY + token)
            ) {
                return new SimpleAuthenticationInfo(token,token,"userRealm");
            }
            throw new AuthenticationException("Token expired or incorrect");
        }
    }


@Service
public class PageServiceImpl implements PageService {

    @Autowired
    PageRepository pageRepository;

    @Override
    @TargetDataSource("slave")
    @Transactional(rollbackFor = Exception.class)
    public List<Page> adminFindAll() {
        List<Page> Pagelist = pageRepository.findAll();
        if (Pagelist.isEmpty()) {
            throw new CustomNotFoundException("page list not found");
        }
        return Pagelist;
    }
}

我不清楚我的描述是否清楚。如果不清楚,请提出问题。我希望得到您的帮助,非常感谢!

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)