fix @DSTransactional 无法获取类|接口|接口方法的注解属性 (#543)
Co-authored-by: zhangpeng <xinniankuailezp@163.com>
This commit is contained in:
parent
a71a54bd13
commit
d9f2bd000d
@ -85,7 +85,8 @@ public class DynamicDataSourceAopConfiguration {
|
|||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnProperty(prefix = DynamicDataSourceProperties.PREFIX, name = "seata", havingValue = "false", matchIfMissing = true)
|
@ConditionalOnProperty(prefix = DynamicDataSourceProperties.PREFIX, name = "seata", havingValue = "false", matchIfMissing = true)
|
||||||
public Advisor dynamicTransactionAdvisor() {
|
public Advisor dynamicTransactionAdvisor() {
|
||||||
DynamicLocalTransactionInterceptor interceptor = new DynamicLocalTransactionInterceptor();
|
DynamicDatasourceAopProperties aopProperties = properties.getAop();
|
||||||
|
DynamicLocalTransactionInterceptor interceptor = new DynamicLocalTransactionInterceptor(aopProperties.getAllowedPublicOnly());
|
||||||
return new DynamicDataSourceAnnotationAdvisor(interceptor, DSTransactional.class);
|
return new DynamicDataSourceAnnotationAdvisor(interceptor, DSTransactional.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,7 +85,8 @@ public class DynamicDataSourceAopConfiguration {
|
|||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnProperty(prefix = DynamicDataSourceProperties.PREFIX, name = "seata", havingValue = "false", matchIfMissing = true)
|
@ConditionalOnProperty(prefix = DynamicDataSourceProperties.PREFIX, name = "seata", havingValue = "false", matchIfMissing = true)
|
||||||
public Advisor dynamicTransactionAdvisor() {
|
public Advisor dynamicTransactionAdvisor() {
|
||||||
DynamicLocalTransactionInterceptor interceptor = new DynamicLocalTransactionInterceptor();
|
DynamicDatasourceAopProperties aopProperties = properties.getAop();
|
||||||
|
DynamicLocalTransactionInterceptor interceptor = new DynamicLocalTransactionInterceptor(aopProperties.getAllowedPublicOnly());
|
||||||
return new DynamicDataSourceAnnotationAdvisor(interceptor, DSTransactional.class);
|
return new DynamicDataSourceAnnotationAdvisor(interceptor, DSTransactional.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
package com.baomidou.dynamic.datasource.annotation;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base kind all dynamicDatasource annotation attribute.
|
||||||
|
*
|
||||||
|
* @author zp
|
||||||
|
* @since 4.1.3
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class BasicAttribute<T> {
|
||||||
|
/**
|
||||||
|
* dataOperation
|
||||||
|
*/
|
||||||
|
private T dataOperation;
|
||||||
|
}
|
@ -16,6 +16,7 @@
|
|||||||
package com.baomidou.dynamic.datasource.aop;
|
package com.baomidou.dynamic.datasource.aop;
|
||||||
|
|
||||||
import com.baomidou.dynamic.datasource.processor.DsProcessor;
|
import com.baomidou.dynamic.datasource.processor.DsProcessor;
|
||||||
|
import com.baomidou.dynamic.datasource.annotation.DS;
|
||||||
import com.baomidou.dynamic.datasource.support.DataSourceClassResolver;
|
import com.baomidou.dynamic.datasource.support.DataSourceClassResolver;
|
||||||
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
|
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
|
||||||
import org.aopalliance.intercept.MethodInterceptor;
|
import org.aopalliance.intercept.MethodInterceptor;
|
||||||
@ -66,7 +67,7 @@ public class DynamicDataSourceAnnotationInterceptor implements MethodInterceptor
|
|||||||
* @return dsKey
|
* @return dsKey
|
||||||
*/
|
*/
|
||||||
private String determineDatasourceKey(MethodInvocation invocation) {
|
private String determineDatasourceKey(MethodInvocation invocation) {
|
||||||
String key = dataSourceClassResolver.findKey(invocation.getMethod(), invocation.getThis());
|
String key = dataSourceClassResolver.findKey(invocation.getMethod(), invocation.getThis(), DS.class);
|
||||||
return key.startsWith(DYNAMIC_PREFIX) ? dsProcessor.determineDatasource(invocation, key) : key;
|
return key.startsWith(DYNAMIC_PREFIX) ? dsProcessor.determineDatasource(invocation, key) : key;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -16,6 +16,7 @@
|
|||||||
package com.baomidou.dynamic.datasource.aop;
|
package com.baomidou.dynamic.datasource.aop;
|
||||||
|
|
||||||
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
|
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
|
||||||
|
import com.baomidou.dynamic.datasource.support.DataSourceClassResolver;
|
||||||
import com.baomidou.dynamic.datasource.tx.TransactionalExecutor;
|
import com.baomidou.dynamic.datasource.tx.TransactionalExecutor;
|
||||||
import com.baomidou.dynamic.datasource.tx.TransactionalInfo;
|
import com.baomidou.dynamic.datasource.tx.TransactionalInfo;
|
||||||
import com.baomidou.dynamic.datasource.tx.TransactionalTemplate;
|
import com.baomidou.dynamic.datasource.tx.TransactionalTemplate;
|
||||||
@ -32,12 +33,17 @@ import java.lang.reflect.Method;
|
|||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class DynamicLocalTransactionInterceptor implements MethodInterceptor {
|
public class DynamicLocalTransactionInterceptor implements MethodInterceptor {
|
||||||
private final TransactionalTemplate transactionalTemplate = new TransactionalTemplate();
|
private final TransactionalTemplate transactionalTemplate;
|
||||||
|
private final DataSourceClassResolver dataSourceClassResolver;
|
||||||
|
|
||||||
|
public DynamicLocalTransactionInterceptor(Boolean allowedPublicOnly) {
|
||||||
|
transactionalTemplate = new TransactionalTemplate();
|
||||||
|
dataSourceClassResolver = new DataSourceClassResolver(allowedPublicOnly);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object invoke(final MethodInvocation methodInvocation) throws Throwable {
|
public Object invoke(final MethodInvocation methodInvocation) throws Throwable {
|
||||||
Method method = methodInvocation.getMethod();
|
final Method method = methodInvocation.getMethod();
|
||||||
final DSTransactional dsTransactional = method.getAnnotation(DSTransactional.class);
|
|
||||||
|
|
||||||
TransactionalExecutor transactionalExecutor = new TransactionalExecutor() {
|
TransactionalExecutor transactionalExecutor = new TransactionalExecutor() {
|
||||||
@Override
|
@Override
|
||||||
@ -47,11 +53,7 @@ public class DynamicLocalTransactionInterceptor implements MethodInterceptor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TransactionalInfo getTransactionInfo() {
|
public TransactionalInfo getTransactionInfo() {
|
||||||
TransactionalInfo transactionInfo = new TransactionalInfo();
|
return dataSourceClassResolver.findTransactionalInfo(method, methodInvocation.getThis(), DSTransactional.class);
|
||||||
transactionInfo.setPropagation(dsTransactional.propagation());
|
|
||||||
transactionInfo.setNoRollbackFor(dsTransactional.noRollbackFor());
|
|
||||||
transactionInfo.setRollbackFor(dsTransactional.rollbackFor());
|
|
||||||
return transactionInfo;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return transactionalTemplate.execute(transactionalExecutor);
|
return transactionalTemplate.execute(transactionalExecutor);
|
||||||
|
@ -15,15 +15,18 @@
|
|||||||
*/
|
*/
|
||||||
package com.baomidou.dynamic.datasource.support;
|
package com.baomidou.dynamic.datasource.support;
|
||||||
|
|
||||||
|
import com.baomidou.dynamic.datasource.annotation.BasicAttribute;
|
||||||
import com.baomidou.dynamic.datasource.annotation.DS;
|
import com.baomidou.dynamic.datasource.annotation.DS;
|
||||||
|
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
|
||||||
|
import com.baomidou.dynamic.datasource.tx.TransactionalInfo;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.aop.framework.AopProxyUtils;
|
import org.springframework.aop.framework.AopProxyUtils;
|
||||||
import org.springframework.core.BridgeMethodResolver;
|
import org.springframework.core.BridgeMethodResolver;
|
||||||
import org.springframework.core.MethodClassKey;
|
import org.springframework.core.MethodClassKey;
|
||||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||||
import org.springframework.core.annotation.AnnotationAttributes;
|
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.*;
|
import java.lang.reflect.*;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
@ -70,7 +73,20 @@ public class DataSourceClassResolver {
|
|||||||
* 缓存方法对应的数据源
|
* 缓存方法对应的数据源
|
||||||
*/
|
*/
|
||||||
private final Map<Object, String> dsCache = new ConcurrentHashMap<>();
|
private final Map<Object, String> dsCache = new ConcurrentHashMap<>();
|
||||||
|
/**
|
||||||
|
* 缓存事务信息
|
||||||
|
*/
|
||||||
|
private final Map<Object, TransactionalInfo> dsTransactionalCache = new ConcurrentHashMap<>();
|
||||||
private final boolean allowedPublicOnly;
|
private final boolean allowedPublicOnly;
|
||||||
|
/**
|
||||||
|
* 默认事务属性
|
||||||
|
*/
|
||||||
|
private static final TransactionalInfo NULL_TRANSACTION_ATTRIBUTE = new TransactionalInfo() {
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "null";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 加入扩展, 给外部一个修改aop条件的机会
|
* 加入扩展, 给外部一个修改aop条件的机会
|
||||||
@ -88,22 +104,49 @@ public class DataSourceClassResolver {
|
|||||||
* @param targetObject 目标对象
|
* @param targetObject 目标对象
|
||||||
* @return ds
|
* @return ds
|
||||||
*/
|
*/
|
||||||
public String findKey(Method method, Object targetObject) {
|
public String findKey(Method method, Object targetObject, Class<? extends Annotation> annotation) {
|
||||||
if (method.getDeclaringClass() == Object.class) {
|
if (method.getDeclaringClass() == Object.class) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
Object cacheKey = new MethodClassKey(method, targetObject.getClass());
|
Object cacheKey = new MethodClassKey(method, targetObject.getClass());
|
||||||
String ds = this.dsCache.get(cacheKey);
|
String ds = this.dsCache.get(cacheKey);
|
||||||
if (ds == null) {
|
if (ds == null) {
|
||||||
ds = computeDatasource(method, targetObject);
|
BasicAttribute<String> dsOperation = computeDatasource(method, targetObject, annotation);
|
||||||
if (ds == null) {
|
if (dsOperation == null) {
|
||||||
ds = "";
|
ds = "";
|
||||||
|
} else {
|
||||||
|
ds = dsOperation.getDataOperation();
|
||||||
}
|
}
|
||||||
this.dsCache.put(cacheKey, ds);
|
this.dsCache.put(cacheKey, ds);
|
||||||
}
|
}
|
||||||
return ds;
|
return ds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从缓存获取事务属性
|
||||||
|
*
|
||||||
|
* @param method 方法
|
||||||
|
* @param targetObject 目标对象
|
||||||
|
* @return TransactionalInfo
|
||||||
|
*/
|
||||||
|
public TransactionalInfo findTransactionalInfo(Method method, Object targetObject, Class<? extends Annotation> annotation) {
|
||||||
|
if (method.getDeclaringClass() == Object.class) {
|
||||||
|
return NULL_TRANSACTION_ATTRIBUTE;
|
||||||
|
}
|
||||||
|
Object cacheKey = new MethodClassKey(method, targetObject.getClass());
|
||||||
|
TransactionalInfo dsTransactional = this.dsTransactionalCache.get(cacheKey);
|
||||||
|
if (dsTransactional == null) {
|
||||||
|
BasicAttribute<TransactionalInfo> dsTransactionalOperation = computeDatasource(method, targetObject, annotation);
|
||||||
|
if (dsTransactionalOperation == null) {
|
||||||
|
dsTransactional = NULL_TRANSACTION_ATTRIBUTE;
|
||||||
|
} else {
|
||||||
|
dsTransactional = dsTransactionalOperation.getDataOperation();
|
||||||
|
}
|
||||||
|
this.dsTransactionalCache.put(cacheKey, dsTransactional);
|
||||||
|
}
|
||||||
|
return dsTransactional;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查找注解的顺序
|
* 查找注解的顺序
|
||||||
* 1. 当前方法
|
* 1. 当前方法
|
||||||
@ -115,12 +158,12 @@ public class DataSourceClassResolver {
|
|||||||
* @param targetObject 目标对象
|
* @param targetObject 目标对象
|
||||||
* @return ds
|
* @return ds
|
||||||
*/
|
*/
|
||||||
private String computeDatasource(Method method, Object targetObject) {
|
private <T> BasicAttribute<T> computeDatasource(Method method, Object targetObject, Class<? extends Annotation> annotation) {
|
||||||
if (allowedPublicOnly && !Modifier.isPublic(method.getModifiers())) {
|
if (allowedPublicOnly && !Modifier.isPublic(method.getModifiers())) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
//1. 从当前方法接口中获取
|
//1. 从当前方法接口中获取
|
||||||
String dsAttr = findDataSourceAttribute(method);
|
BasicAttribute<T> dsAttr = findDataSourceAttribute(method, annotation);
|
||||||
if (dsAttr != null) {
|
if (dsAttr != null) {
|
||||||
return dsAttr;
|
return dsAttr;
|
||||||
}
|
}
|
||||||
@ -130,19 +173,19 @@ public class DataSourceClassResolver {
|
|||||||
Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass);
|
Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass);
|
||||||
|
|
||||||
specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
|
specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
|
||||||
//2. 从桥接方法查找
|
//2. 从实现类的方法找
|
||||||
dsAttr = findDataSourceAttribute(specificMethod);
|
dsAttr = findDataSourceAttribute(specificMethod, annotation);
|
||||||
if (dsAttr != null) {
|
if (dsAttr != null) {
|
||||||
return dsAttr;
|
return dsAttr;
|
||||||
}
|
}
|
||||||
// 从当前方法声明的类查找
|
// 从当前方法声明的类查找
|
||||||
dsAttr = findDataSourceAttribute(userClass);
|
dsAttr = findDataSourceAttribute(userClass, annotation);
|
||||||
if (dsAttr != null && ClassUtils.isUserLevelMethod(method)) {
|
if (dsAttr != null && ClassUtils.isUserLevelMethod(method)) {
|
||||||
return dsAttr;
|
return dsAttr;
|
||||||
}
|
}
|
||||||
//since 3.4.1 从接口查找,只取第一个找到的
|
//since 3.4.1 从接口查找,只取第一个找到的
|
||||||
for (Class<?> interfaceClazz : ClassUtils.getAllInterfacesForClassAsSet(userClass)) {
|
for (Class<?> interfaceClazz : ClassUtils.getAllInterfacesForClassAsSet(userClass)) {
|
||||||
dsAttr = findDataSourceAttribute(interfaceClazz);
|
dsAttr = findDataSourceAttribute(interfaceClazz, annotation);
|
||||||
if (dsAttr != null) {
|
if (dsAttr != null) {
|
||||||
return dsAttr;
|
return dsAttr;
|
||||||
}
|
}
|
||||||
@ -150,17 +193,17 @@ public class DataSourceClassResolver {
|
|||||||
// 如果存在桥接方法
|
// 如果存在桥接方法
|
||||||
if (specificMethod != method) {
|
if (specificMethod != method) {
|
||||||
// 从桥接方法查找
|
// 从桥接方法查找
|
||||||
dsAttr = findDataSourceAttribute(method);
|
dsAttr = findDataSourceAttribute(method, annotation);
|
||||||
if (dsAttr != null) {
|
if (dsAttr != null) {
|
||||||
return dsAttr;
|
return dsAttr;
|
||||||
}
|
}
|
||||||
// 从桥接方法声明的类查找
|
// 从桥接方法声明的类查找
|
||||||
dsAttr = findDataSourceAttribute(method.getDeclaringClass());
|
dsAttr = findDataSourceAttribute(method.getDeclaringClass(), annotation);
|
||||||
if (dsAttr != null && ClassUtils.isUserLevelMethod(method)) {
|
if (dsAttr != null && ClassUtils.isUserLevelMethod(method)) {
|
||||||
return dsAttr;
|
return dsAttr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return getDefaultDataSourceAttr(targetObject);
|
return getDefaultDataSourceAttr(targetObject, annotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -169,13 +212,13 @@ public class DataSourceClassResolver {
|
|||||||
* @param targetObject 目标对象
|
* @param targetObject 目标对象
|
||||||
* @return ds
|
* @return ds
|
||||||
*/
|
*/
|
||||||
private String getDefaultDataSourceAttr(Object targetObject) {
|
private <T> BasicAttribute<T> getDefaultDataSourceAttr(Object targetObject, Class<? extends Annotation> annotation) {
|
||||||
Class<?> targetClass = targetObject.getClass();
|
Class<?> targetClass = targetObject.getClass();
|
||||||
// 如果不是代理类, 从当前类开始, 不断的找父类的声明
|
// 如果不是代理类, 从当前类开始, 不断的找父类的声明
|
||||||
if (!Proxy.isProxyClass(targetClass)) {
|
if (!Proxy.isProxyClass(targetClass)) {
|
||||||
Class<?> currentClass = targetClass;
|
Class<?> currentClass = targetClass;
|
||||||
while (currentClass != Object.class) {
|
while (currentClass != Object.class) {
|
||||||
String datasourceAttr = findDataSourceAttribute(currentClass);
|
BasicAttribute<T> datasourceAttr = findDataSourceAttribute(currentClass, annotation);
|
||||||
if (datasourceAttr != null) {
|
if (datasourceAttr != null) {
|
||||||
return datasourceAttr;
|
return datasourceAttr;
|
||||||
}
|
}
|
||||||
@ -186,12 +229,12 @@ public class DataSourceClassResolver {
|
|||||||
if (mpEnabled) {
|
if (mpEnabled) {
|
||||||
final Class<?> clazz = getMapperInterfaceClass(targetObject);
|
final Class<?> clazz = getMapperInterfaceClass(targetObject);
|
||||||
if (clazz != null) {
|
if (clazz != null) {
|
||||||
String datasourceAttr = findDataSourceAttribute(clazz);
|
BasicAttribute<T> datasourceAttr = findDataSourceAttribute(clazz, annotation);
|
||||||
if (datasourceAttr != null) {
|
if (datasourceAttr != null) {
|
||||||
return datasourceAttr;
|
return datasourceAttr;
|
||||||
}
|
}
|
||||||
// 尝试从其父接口获取
|
// 尝试从其父接口获取
|
||||||
return findDataSourceAttribute(clazz.getSuperclass());
|
return findDataSourceAttribute(clazz.getSuperclass(), annotation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@ -222,15 +265,27 @@ public class DataSourceClassResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通过 AnnotatedElement 查找标记的注解, 映射为 DatasourceHolder
|
* 通过 AnnotatedElement 查找标记的注解, 映射为BasicAttribute
|
||||||
*
|
*
|
||||||
* @param ae AnnotatedElement
|
* @param ae AnnotatedElement
|
||||||
* @return 数据源映射持有者
|
* @return 数据源映射持有者
|
||||||
*/
|
*/
|
||||||
private String findDataSourceAttribute(AnnotatedElement ae) {
|
private <T> BasicAttribute<T> findDataSourceAttribute(AnnotatedElement ae, Class<? extends Annotation> annotation) {
|
||||||
AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ae, DS.class);
|
if (annotation.isAssignableFrom(DS.class)) {
|
||||||
if (attributes != null) {
|
//AnnotatedElementUtils.findMergedAnnotation()会委托给findMergedAnnotationAttributes()
|
||||||
return attributes.getString("value");
|
DS ds = AnnotatedElementUtils.findMergedAnnotation(ae, DS.class);
|
||||||
|
if (ds != null) {
|
||||||
|
return (BasicAttribute<T>) new BasicAttribute<>(ds.value());
|
||||||
|
}
|
||||||
|
} else if (annotation.isAssignableFrom(DSTransactional.class)) {
|
||||||
|
DSTransactional dsTransactional = AnnotatedElementUtils.findMergedAnnotation(ae, DSTransactional.class);
|
||||||
|
if (dsTransactional != null) {
|
||||||
|
TransactionalInfo transactionalInfo = new TransactionalInfo();
|
||||||
|
transactionalInfo.setPropagation(dsTransactional.propagation());
|
||||||
|
transactionalInfo.setRollbackFor(dsTransactional.rollbackFor());
|
||||||
|
transactionalInfo.setNoRollbackFor(dsTransactional.noRollbackFor());
|
||||||
|
return (BasicAttribute<T>) new BasicAttribute(transactionalInfo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user