Avoid storage of null marker per method for proxy decision purposes
Includes missing isCandidateClass support on JCacheOperationSource. Closes gh-20072
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -29,6 +29,7 @@ import org.springframework.context.EmbeddedValueResolverAware;
|
||||
import org.springframework.core.MethodClassKey;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringValueResolver;
|
||||
|
||||
/**
|
||||
@@ -42,11 +43,6 @@ import org.springframework.util.StringValueResolver;
|
||||
* If none found on the target class, the interface that the invoked method
|
||||
* has been called through (in case of a JDK proxy) will be checked.
|
||||
*
|
||||
* <p>This implementation caches attributes by method after they are first used.
|
||||
* If it is ever desirable to allow dynamic changing of transaction attributes
|
||||
* (which is very unlikely), caching could be made configurable. Caching is
|
||||
* desirable because of the cost of evaluating rollback rules.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1
|
||||
@@ -91,42 +87,43 @@ public abstract class AbstractFallbackTransactionAttributeSource
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean hasTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
|
||||
return (getTransactionAttribute(method, targetClass, false) != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
|
||||
return getTransactionAttribute(method, targetClass, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the transaction attribute for this method invocation.
|
||||
* <p>Defaults to the class's transaction attribute if no method attribute is found.
|
||||
* @param method the method for the current invocation (never {@code null})
|
||||
* @param targetClass the target class for this invocation (may be {@code null})
|
||||
* @param targetClass the target class for this invocation (can be {@code null})
|
||||
* @param cacheNull whether {@code null} results should be cached as well
|
||||
* @return a TransactionAttribute for this method, or {@code null} if the method
|
||||
* is not transactional
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
|
||||
if (method.getDeclaringClass() == Object.class) {
|
||||
private TransactionAttribute getTransactionAttribute(
|
||||
Method method, @Nullable Class<?> targetClass, boolean cacheNull) {
|
||||
|
||||
if (ReflectionUtils.isObjectMethod(method)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// First, see if we have a cached value.
|
||||
Object cacheKey = getCacheKey(method, targetClass);
|
||||
TransactionAttribute cached = this.attributeCache.get(cacheKey);
|
||||
|
||||
if (cached != null) {
|
||||
// Value will either be canonical value indicating there is no transaction attribute,
|
||||
// or an actual transaction attribute.
|
||||
if (cached == NULL_TRANSACTION_ATTRIBUTE) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
return cached;
|
||||
}
|
||||
return (cached != NULL_TRANSACTION_ATTRIBUTE ? cached : null);
|
||||
}
|
||||
else {
|
||||
// We need to work it out.
|
||||
TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);
|
||||
// Put it in the cache.
|
||||
if (txAttr == null) {
|
||||
this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);
|
||||
}
|
||||
else {
|
||||
if (txAttr != null) {
|
||||
String methodIdentification = ClassUtils.getQualifiedMethodName(method, targetClass);
|
||||
if (txAttr instanceof DefaultTransactionAttribute dta) {
|
||||
dta.setDescriptor(methodIdentification);
|
||||
@@ -137,6 +134,9 @@ public abstract class AbstractFallbackTransactionAttributeSource
|
||||
}
|
||||
this.attributeCache.put(cacheKey, txAttr);
|
||||
}
|
||||
else if (cacheNull) {
|
||||
this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);
|
||||
}
|
||||
return txAttr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -48,16 +48,29 @@ public interface TransactionAttributeSource {
|
||||
* attributes at class or method level; {@code true} otherwise. The default
|
||||
* implementation returns {@code true}, leading to regular introspection.
|
||||
* @since 5.2
|
||||
* @see #hasTransactionAttribute
|
||||
*/
|
||||
default boolean isCandidateClass(Class<?> targetClass) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether there is a transaction attribute for the given method.
|
||||
* @param method the method to introspect
|
||||
* @param targetClass the target class (can be {@code null},
|
||||
* in which case the declaring class of the method must be used)
|
||||
* @since 6.2
|
||||
* @see #getTransactionAttribute
|
||||
*/
|
||||
default boolean hasTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
|
||||
return (getTransactionAttribute(method, targetClass) != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the transaction attribute for the given method,
|
||||
* or {@code null} if the method is non-transactional.
|
||||
* @param method the method to introspect
|
||||
* @param targetClass the target class (may be {@code null},
|
||||
* @param targetClass the target class (can be {@code null},
|
||||
* in which case the declaring class of the method must be used)
|
||||
* @return the matching transaction attribute, or {@code null} if none found
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -53,7 +53,7 @@ final class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointc
|
||||
@Override
|
||||
public boolean matches(Method method, Class<?> targetClass) {
|
||||
return (this.transactionAttributeSource == null ||
|
||||
this.transactionAttributeSource.getTransactionAttribute(method, targetClass) != null);
|
||||
this.transactionAttributeSource.hasTransactionAttribute(method, targetClass));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -77,7 +77,7 @@ final class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointc
|
||||
* {@link ClassFilter} that delegates to {@link TransactionAttributeSource#isCandidateClass}
|
||||
* for filtering classes whose methods are not worth searching to begin with.
|
||||
*/
|
||||
private class TransactionAttributeSourceClassFilter implements ClassFilter {
|
||||
private final class TransactionAttributeSourceClassFilter implements ClassFilter {
|
||||
|
||||
@Override
|
||||
public boolean matches(Class<?> clazz) {
|
||||
@@ -89,6 +89,7 @@ final class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointc
|
||||
return (transactionAttributeSource == null || transactionAttributeSource.isCandidateClass(clazz));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private TransactionAttributeSource getTransactionAttributeSource() {
|
||||
return transactionAttributeSource;
|
||||
}
|
||||
@@ -96,7 +97,7 @@ final class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointc
|
||||
@Override
|
||||
public boolean equals(@Nullable Object other) {
|
||||
return (this == other || (other instanceof TransactionAttributeSourceClassFilter that &&
|
||||
ObjectUtils.nullSafeEquals(transactionAttributeSource, that.getTransactionAttributeSource())));
|
||||
ObjectUtils.nullSafeEquals(getTransactionAttributeSource(), that.getTransactionAttributeSource())));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -106,9 +107,8 @@ final class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointc
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return TransactionAttributeSourceClassFilter.class.getName() + ": " + transactionAttributeSource;
|
||||
return TransactionAttributeSourceClassFilter.class.getName() + ": " + getTransactionAttributeSource();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user