Merge branch '5.3.x'

# Conflicts:
#	spring-core/src/main/java/org/springframework/cglib/core/ReflectUtils.java
This commit is contained in:
Juergen Hoeller 2021-09-30 17:37:28 +02:00
commit dc5807ea51
3 changed files with 57 additions and 37 deletions

View File

@ -679,13 +679,19 @@ class CglibAopProxy implements AopProxy, Serializable {
Object retVal; Object retVal;
// Check whether we only have one InvokerInterceptor: that is, // Check whether we only have one InvokerInterceptor: that is,
// no real advice, but just reflective invocation of the target. // no real advice, but just reflective invocation of the target.
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) { if (chain.isEmpty() && CglibMethodInvocation.isMethodProxyCompatible(method)) {
// We can skip creating a MethodInvocation: just invoke the target directly. // We can skip creating a MethodInvocation: just invoke the target directly.
// Note that the final invoker must be an InvokerInterceptor, so we know // Note that the final invoker must be an InvokerInterceptor, so we know
// it does nothing but a reflective operation on the target, and no hot // it does nothing but a reflective operation on the target, and no hot
// swapping or fancy proxying. // swapping or fancy proxying.
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = methodProxy.invoke(target, argsToUse); try {
retVal = methodProxy.invoke(target, argsToUse);
}
catch (CodeGenerationException ex) {
CglibMethodInvocation.logFastClassGenerationFailure(method);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
} }
else { else {
// We need to create a method invocation... // We need to create a method invocation...
@ -737,10 +743,7 @@ class CglibAopProxy implements AopProxy, Serializable {
super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers); super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers);
// Only use method proxy for public methods not derived from java.lang.Object // Only use method proxy for public methods not derived from java.lang.Object
this.methodProxy = (Modifier.isPublic(method.getModifiers()) && this.methodProxy = (isMethodProxyCompatible(method) ? methodProxy : null);
method.getDeclaringClass() != Object.class && !AopUtils.isEqualsMethod(method) &&
!AopUtils.isHashCodeMethod(method) && !AopUtils.isToStringMethod(method) ?
methodProxy : null);
} }
@Override @Override
@ -776,10 +779,25 @@ class CglibAopProxy implements AopProxy, Serializable {
@Override @Override
protected Object invokeJoinpoint() throws Throwable { protected Object invokeJoinpoint() throws Throwable {
if (this.methodProxy != null) { if (this.methodProxy != null) {
return this.methodProxy.invoke(this.target, this.arguments); try {
return this.methodProxy.invoke(this.target, this.arguments);
}
catch (CodeGenerationException ex) {
logFastClassGenerationFailure(this.method);
}
} }
else { return super.invokeJoinpoint();
return super.invokeJoinpoint(); }
static boolean isMethodProxyCompatible(Method method) {
return (Modifier.isPublic(method.getModifiers()) &&
method.getDeclaringClass() != Object.class && !AopUtils.isEqualsMethod(method) &&
!AopUtils.isHashCodeMethod(method) && !AopUtils.isToStringMethod(method));
}
static void logFastClassGenerationFailure(Method method) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to generate CGLIB fast class for method: " + method);
} }
} }
} }

View File

@ -55,43 +55,40 @@ public class ReflectUtils {
private static final Method classLoaderDefineClassMethod; private static final Method classLoaderDefineClassMethod;
private static final ProtectionDomain PROTECTION_DOMAIN;
private static final Throwable THROWABLE; private static final Throwable THROWABLE;
private static final ProtectionDomain PROTECTION_DOMAIN;
private static final List<Method> OBJECT_METHODS = new ArrayList<Method>(); private static final List<Method> OBJECT_METHODS = new ArrayList<Method>();
// SPRING PATCH BEGIN // SPRING PATCH BEGIN
static { static {
Method classLoaderDefineClass; Method classLoaderDefineClass;
ProtectionDomain protectionDomain;
Throwable throwable = null; Throwable throwable = null;
try { try {
classLoaderDefineClass = ClassLoader.class.getDeclaredMethod("defineClass", classLoaderDefineClass = ClassLoader.class.getDeclaredMethod("defineClass",
String.class, byte[].class, Integer.TYPE, Integer.TYPE, ProtectionDomain.class); String.class, byte[].class, Integer.TYPE, Integer.TYPE, ProtectionDomain.class);
protectionDomain = getProtectionDomain(ReflectUtils.class);
for (Method method : Object.class.getDeclaredMethods()) {
if ("finalize".equals(method.getName())
|| (method.getModifiers() & (Modifier.FINAL | Modifier.STATIC)) > 0) {
continue;
}
OBJECT_METHODS.add(method);
}
} }
catch (Throwable t) { catch (Throwable t) {
classLoaderDefineClass = null; classLoaderDefineClass = null;
protectionDomain = null;
throwable = t; throwable = t;
} }
classLoaderDefineClassMethod = classLoaderDefineClass; classLoaderDefineClassMethod = classLoaderDefineClass;
PROTECTION_DOMAIN = protectionDomain;
THROWABLE = throwable; THROWABLE = throwable;
PROTECTION_DOMAIN = getProtectionDomain(ReflectUtils.class);
for (Method method : Object.class.getDeclaredMethods()) {
if ("finalize".equals(method.getName())
|| (method.getModifiers() & (Modifier.FINAL | Modifier.STATIC)) > 0) {
continue;
}
OBJECT_METHODS.add(method);
}
} }
// SPRING PATCH END // SPRING PATCH END
private static final String[] CGLIB_PACKAGES = { private static final String[] CGLIB_PACKAGES = {"java.lang"};
"java.lang",
};
static { static {
primitives.put("byte", Byte.TYPE); primitives.put("byte", Byte.TYPE);
@ -444,6 +441,7 @@ public class ReflectUtils {
ProtectionDomain protectionDomain, Class<?> contextClass) throws Exception { ProtectionDomain protectionDomain, Class<?> contextClass) throws Exception {
Class c = null; Class c = null;
Throwable t = THROWABLE;
// Preferred option: JDK 9+ Lookup.defineClass API if ClassLoader matches // Preferred option: JDK 9+ Lookup.defineClass API if ClassLoader matches
if (contextClass != null && contextClass.getClassLoader() == loader) { if (contextClass != null && contextClass.getClassLoader() == loader) {
@ -455,6 +453,7 @@ public class ReflectUtils {
// in case of plain LinkageError (class already defined) // in case of plain LinkageError (class already defined)
// or IllegalArgumentException (class in different package): // or IllegalArgumentException (class in different package):
// fall through to traditional ClassLoader.defineClass below // fall through to traditional ClassLoader.defineClass below
t = ex;
} }
catch (Throwable ex) { catch (Throwable ex) {
throw new CodeGenerationException(ex); throw new CodeGenerationException(ex);
@ -478,9 +477,11 @@ public class ReflectUtils {
throw new CodeGenerationException(ex.getTargetException()); throw new CodeGenerationException(ex.getTargetException());
} }
// in case of UnsupportedOperationException, fall through // in case of UnsupportedOperationException, fall through
t = ex.getTargetException();
} }
catch (Throwable ex) { catch (Throwable ex) {
// publicDefineClass method not available -> fall through // publicDefineClass method not available -> fall through
t = ex;
} }
// Classic option: protected ClassLoader.defineClass method // Classic option: protected ClassLoader.defineClass method
@ -501,6 +502,7 @@ public class ReflectUtils {
if (!ex.getClass().getName().endsWith("InaccessibleObjectException")) { if (!ex.getClass().getName().endsWith("InaccessibleObjectException")) {
throw new CodeGenerationException(ex); throw new CodeGenerationException(ex);
} }
t = ex;
} }
} }
} }
@ -518,7 +520,7 @@ public class ReflectUtils {
// No defineClass variant available at all? // No defineClass variant available at all?
if (c == null) { if (c == null) {
throw new CodeGenerationException(THROWABLE); throw new CodeGenerationException(t);
} }
// Force static initializers to run. // Force static initializers to run.

View File

@ -89,8 +89,8 @@ public class SpringExtension implements BeforeAllCallback, AfterAllCallback, Tes
* {@link Namespace} in which {@code @Autowired} validation error messages * {@link Namespace} in which {@code @Autowired} validation error messages
* are stored, keyed by test class. * are stored, keyed by test class.
*/ */
private static final Namespace AUTOWIRED_VALIDATION_NAMESPACE = Namespace.create(SpringExtension.class.getName() + private static final Namespace AUTOWIRED_VALIDATION_NAMESPACE =
"#autowired.validation"); Namespace.create(SpringExtension.class.getName() + "#autowired.validation");
private static final String NO_AUTOWIRED_VIOLATIONS_DETECTED = "NO AUTOWIRED VIOLATIONS DETECTED"; private static final String NO_AUTOWIRED_VIOLATIONS_DETECTED = "NO AUTOWIRED VIOLATIONS DETECTED";
@ -101,8 +101,8 @@ public class SpringExtension implements BeforeAllCallback, AfterAllCallback, Tes
private static final MethodFilter autowiredTestOrLifecycleMethodFilter = private static final MethodFilter autowiredTestOrLifecycleMethodFilter =
ReflectionUtils.USER_DECLARED_METHODS ReflectionUtils.USER_DECLARED_METHODS
.and(method -> !Modifier.isPrivate(method.getModifiers())) .and(method -> !Modifier.isPrivate(method.getModifiers()))
.and(SpringExtension::isAutowiredTestOrLifecycleMethod); .and(SpringExtension::isAutowiredTestOrLifecycleMethod);
/** /**
@ -147,16 +147,16 @@ public class SpringExtension implements BeforeAllCallback, AfterAllCallback, Tes
// We save the result in the ExtensionContext.Store so that we don't // We save the result in the ExtensionContext.Store so that we don't
// re-validate all methods for the same test class multiple times. // re-validate all methods for the same test class multiple times.
Store store = context.getStore(AUTOWIRED_VALIDATION_NAMESPACE); Store store = context.getStore(AUTOWIRED_VALIDATION_NAMESPACE);
String errorMessage = store.getOrComputeIfAbsent(context.getRequiredTestClass(),
testClass -> { String errorMessage = store.getOrComputeIfAbsent(context.getRequiredTestClass(), testClass -> {
Method[] methodsWithErrors = Method[] methodsWithErrors =
ReflectionUtils.getUniqueDeclaredMethods(testClass, autowiredTestOrLifecycleMethodFilter); ReflectionUtils.getUniqueDeclaredMethods(testClass, autowiredTestOrLifecycleMethodFilter);
return (methodsWithErrors.length == 0 ? NO_AUTOWIRED_VIOLATIONS_DETECTED : return (methodsWithErrors.length == 0 ? NO_AUTOWIRED_VIOLATIONS_DETECTED :
String.format( String.format(
"Test methods and test lifecycle methods must not be annotated with @Autowired. " + "Test methods and test lifecycle methods must not be annotated with @Autowired. " +
"You should instead annotate individual method parameters with @Autowired, " + "You should instead annotate individual method parameters with @Autowired, " +
"@Qualifier, or @Value. Offending methods in test class %s: %s", "@Qualifier, or @Value. Offending methods in test class %s: %s",
testClass.getName(), Arrays.toString(methodsWithErrors))); testClass.getName(), Arrays.toString(methodsWithErrors)));
}, String.class); }, String.class);
if (errorMessage != NO_AUTOWIRED_VIOLATIONS_DETECTED) { if (errorMessage != NO_AUTOWIRED_VIOLATIONS_DETECTED) {
@ -247,7 +247,7 @@ public class SpringExtension implements BeforeAllCallback, AfterAllCallback, Tes
private boolean supportsApplicationEvents(ParameterContext parameterContext) { private boolean supportsApplicationEvents(ParameterContext parameterContext) {
if (ApplicationEvents.class.isAssignableFrom(parameterContext.getParameter().getType())) { if (ApplicationEvents.class.isAssignableFrom(parameterContext.getParameter().getType())) {
Assert.isTrue(parameterContext.getDeclaringExecutable() instanceof Method, Assert.isTrue(parameterContext.getDeclaringExecutable() instanceof Method,
"ApplicationEvents can only be injected into test and lifecycle methods"); "ApplicationEvents can only be injected into test and lifecycle methods");
return true; return true;
} }
return false; return false;