问题描述
我的项目使用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 (将#修改为@)