Early resolution of unique factory methods in configuration classes
Includes consistent bean class resolution in the enhancement step as well as general reflection optimizations for user-declared methods. Closes gh-22420
This commit is contained in:
parent
a35adc6ea6
commit
40c62139ae
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2018 the original author or authors.
|
* Copyright 2002-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -153,7 +153,7 @@ public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFacto
|
||||||
if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) {
|
if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) {
|
||||||
methods.add(method);
|
methods.add(method);
|
||||||
}
|
}
|
||||||
});
|
}, ReflectionUtils.USER_DECLARED_METHODS);
|
||||||
methods.sort(METHOD_COMPARATOR);
|
methods.sort(METHOD_COMPARATOR);
|
||||||
return methods;
|
return methods;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -706,96 +706,100 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
||||||
return cachedReturnType.resolve();
|
return cachedReturnType.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
Class<?> factoryClass;
|
|
||||||
boolean isStatic = true;
|
|
||||||
|
|
||||||
String factoryBeanName = mbd.getFactoryBeanName();
|
|
||||||
if (factoryBeanName != null) {
|
|
||||||
if (factoryBeanName.equals(beanName)) {
|
|
||||||
throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,
|
|
||||||
"factory-bean reference points back to the same bean definition");
|
|
||||||
}
|
|
||||||
// Check declared factory method return type on factory class.
|
|
||||||
factoryClass = getType(factoryBeanName);
|
|
||||||
isStatic = false;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Check declared factory method return type on bean class.
|
|
||||||
factoryClass = resolveBeanClass(mbd, beanName, typesToMatch);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (factoryClass == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
factoryClass = ClassUtils.getUserClass(factoryClass);
|
|
||||||
|
|
||||||
// If all factory methods have the same return type, return that type.
|
|
||||||
// Can't clearly figure out exact method due to type converting / autowiring!
|
|
||||||
Class<?> commonType = null;
|
Class<?> commonType = null;
|
||||||
Method uniqueCandidate = null;
|
Method uniqueCandidate = mbd.factoryMethodToIntrospect;
|
||||||
int minNrOfArgs =
|
|
||||||
(mbd.hasConstructorArgumentValues() ? mbd.getConstructorArgumentValues().getArgumentCount() : 0);
|
|
||||||
Method[] candidates = this.factoryMethodCandidateCache.computeIfAbsent(
|
|
||||||
factoryClass, ReflectionUtils::getUniqueDeclaredMethods);
|
|
||||||
|
|
||||||
for (Method candidate : candidates) {
|
if (uniqueCandidate == null) {
|
||||||
if (Modifier.isStatic(candidate.getModifiers()) == isStatic && mbd.isFactoryMethod(candidate) &&
|
Class<?> factoryClass;
|
||||||
candidate.getParameterCount() >= minNrOfArgs) {
|
boolean isStatic = true;
|
||||||
// Declared type variables to inspect?
|
|
||||||
if (candidate.getTypeParameters().length > 0) {
|
String factoryBeanName = mbd.getFactoryBeanName();
|
||||||
try {
|
if (factoryBeanName != null) {
|
||||||
// Fully resolve parameter names and argument values.
|
if (factoryBeanName.equals(beanName)) {
|
||||||
Class<?>[] paramTypes = candidate.getParameterTypes();
|
throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,
|
||||||
String[] paramNames = null;
|
"factory-bean reference points back to the same bean definition");
|
||||||
ParameterNameDiscoverer pnd = getParameterNameDiscoverer();
|
}
|
||||||
if (pnd != null) {
|
// Check declared factory method return type on factory class.
|
||||||
paramNames = pnd.getParameterNames(candidate);
|
factoryClass = getType(factoryBeanName);
|
||||||
}
|
isStatic = false;
|
||||||
ConstructorArgumentValues cav = mbd.getConstructorArgumentValues();
|
}
|
||||||
Set<ConstructorArgumentValues.ValueHolder> usedValueHolders = new HashSet<>(paramTypes.length);
|
else {
|
||||||
Object[] args = new Object[paramTypes.length];
|
// Check declared factory method return type on bean class.
|
||||||
for (int i = 0; i < args.length; i++) {
|
factoryClass = resolveBeanClass(mbd, beanName, typesToMatch);
|
||||||
ConstructorArgumentValues.ValueHolder valueHolder = cav.getArgumentValue(
|
}
|
||||||
i, paramTypes[i], (paramNames != null ? paramNames[i] : null), usedValueHolders);
|
|
||||||
if (valueHolder == null) {
|
if (factoryClass == null) {
|
||||||
valueHolder = cav.getGenericArgumentValue(null, null, usedValueHolders);
|
return null;
|
||||||
|
}
|
||||||
|
factoryClass = ClassUtils.getUserClass(factoryClass);
|
||||||
|
|
||||||
|
// If all factory methods have the same return type, return that type.
|
||||||
|
// Can't clearly figure out exact method due to type converting / autowiring!
|
||||||
|
int minNrOfArgs =
|
||||||
|
(mbd.hasConstructorArgumentValues() ? mbd.getConstructorArgumentValues().getArgumentCount() : 0);
|
||||||
|
Method[] candidates = this.factoryMethodCandidateCache.computeIfAbsent(factoryClass,
|
||||||
|
clazz -> ReflectionUtils.getUniqueDeclaredMethods(clazz, ReflectionUtils.USER_DECLARED_METHODS));
|
||||||
|
|
||||||
|
for (Method candidate : candidates) {
|
||||||
|
if (Modifier.isStatic(candidate.getModifiers()) == isStatic && mbd.isFactoryMethod(candidate) &&
|
||||||
|
candidate.getParameterCount() >= minNrOfArgs) {
|
||||||
|
// Declared type variables to inspect?
|
||||||
|
if (candidate.getTypeParameters().length > 0) {
|
||||||
|
try {
|
||||||
|
// Fully resolve parameter names and argument values.
|
||||||
|
Class<?>[] paramTypes = candidate.getParameterTypes();
|
||||||
|
String[] paramNames = null;
|
||||||
|
ParameterNameDiscoverer pnd = getParameterNameDiscoverer();
|
||||||
|
if (pnd != null) {
|
||||||
|
paramNames = pnd.getParameterNames(candidate);
|
||||||
}
|
}
|
||||||
if (valueHolder != null) {
|
ConstructorArgumentValues cav = mbd.getConstructorArgumentValues();
|
||||||
args[i] = valueHolder.getValue();
|
Set<ConstructorArgumentValues.ValueHolder> usedValueHolders = new HashSet<>(paramTypes.length);
|
||||||
usedValueHolders.add(valueHolder);
|
Object[] args = new Object[paramTypes.length];
|
||||||
|
for (int i = 0; i < args.length; i++) {
|
||||||
|
ConstructorArgumentValues.ValueHolder valueHolder = cav.getArgumentValue(
|
||||||
|
i, paramTypes[i], (paramNames != null ? paramNames[i] : null), usedValueHolders);
|
||||||
|
if (valueHolder == null) {
|
||||||
|
valueHolder = cav.getGenericArgumentValue(null, null, usedValueHolders);
|
||||||
|
}
|
||||||
|
if (valueHolder != null) {
|
||||||
|
args[i] = valueHolder.getValue();
|
||||||
|
usedValueHolders.add(valueHolder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Class<?> returnType = AutowireUtils.resolveReturnTypeForFactoryMethod(
|
||||||
|
candidate, args, getBeanClassLoader());
|
||||||
|
uniqueCandidate = (commonType == null && returnType == candidate.getReturnType() ?
|
||||||
|
candidate : null);
|
||||||
|
commonType = ClassUtils.determineCommonAncestor(returnType, commonType);
|
||||||
|
if (commonType == null) {
|
||||||
|
// Ambiguous return types found: return null to indicate "not determinable".
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Class<?> returnType = AutowireUtils.resolveReturnTypeForFactoryMethod(
|
catch (Throwable ex) {
|
||||||
candidate, args, getBeanClassLoader());
|
if (logger.isDebugEnabled()) {
|
||||||
uniqueCandidate = (commonType == null && returnType == candidate.getReturnType() ?
|
logger.debug("Failed to resolve generic return type for factory method: " + ex);
|
||||||
candidate : null);
|
}
|
||||||
commonType = ClassUtils.determineCommonAncestor(returnType, commonType);
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
uniqueCandidate = (commonType == null ? candidate : null);
|
||||||
|
commonType = ClassUtils.determineCommonAncestor(candidate.getReturnType(), commonType);
|
||||||
if (commonType == null) {
|
if (commonType == null) {
|
||||||
// Ambiguous return types found: return null to indicate "not determinable".
|
// Ambiguous return types found: return null to indicate "not determinable".
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Throwable ex) {
|
|
||||||
if (logger.isDebugEnabled()) {
|
|
||||||
logger.debug("Failed to resolve generic return type for factory method: " + ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
uniqueCandidate = (commonType == null ? candidate : null);
|
|
||||||
commonType = ClassUtils.determineCommonAncestor(candidate.getReturnType(), commonType);
|
|
||||||
if (commonType == null) {
|
|
||||||
// Ambiguous return types found: return null to indicate "not determinable".
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mbd.factoryMethodToIntrospect = uniqueCandidate;
|
||||||
|
if (commonType == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mbd.factoryMethodToIntrospect = uniqueCandidate;
|
|
||||||
if (commonType == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
// Common return type found: all factory methods return same type. For a non-parameterized
|
// Common return type found: all factory methods return same type. For a non-parameterized
|
||||||
// unique candidate, cache the full type declaration context of the target factory method.
|
// unique candidate, cache the full type declaration context of the target factory method.
|
||||||
cachedReturnType = (uniqueCandidate != null ?
|
cachedReturnType = (uniqueCandidate != null ?
|
||||||
|
|
@ -926,7 +930,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
||||||
objectType.value = ClassUtils.determineCommonAncestor(currentType, objectType.value);
|
objectType.value = ClassUtils.determineCommonAncestor(currentType, objectType.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}, ReflectionUtils.USER_DECLARED_METHODS);
|
||||||
|
|
||||||
return (objectType.value != null && Object.class != objectType.value ? objectType.value : null);
|
return (objectType.value != null && Object.class != objectType.value ? objectType.value : null);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2018 the original author or authors.
|
* Copyright 2002-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -26,6 +26,7 @@ import java.security.AccessController;
|
||||||
import java.security.PrivilegedAction;
|
import java.security.PrivilegedAction;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
|
@ -436,11 +437,22 @@ class ConstructorResolver {
|
||||||
// Try all methods with this name to see if they match the given arguments.
|
// Try all methods with this name to see if they match the given arguments.
|
||||||
factoryClass = ClassUtils.getUserClass(factoryClass);
|
factoryClass = ClassUtils.getUserClass(factoryClass);
|
||||||
|
|
||||||
Method[] rawCandidates = getCandidateMethods(factoryClass, mbd);
|
List<Method> candidateList = null;
|
||||||
List<Method> candidateList = new ArrayList<>();
|
if (mbd.isFactoryMethodUnique) {
|
||||||
for (Method candidate : rawCandidates) {
|
if (factoryMethodToUse == null) {
|
||||||
if (Modifier.isStatic(candidate.getModifiers()) == isStatic && mbd.isFactoryMethod(candidate)) {
|
factoryMethodToUse = mbd.getResolvedFactoryMethod();
|
||||||
candidateList.add(candidate);
|
}
|
||||||
|
if (factoryMethodToUse != null) {
|
||||||
|
candidateList = Collections.singletonList(factoryMethodToUse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (candidateList == null) {
|
||||||
|
candidateList = new ArrayList<>();
|
||||||
|
Method[] rawCandidates = getCandidateMethods(factoryClass, mbd);
|
||||||
|
for (Method candidate : rawCandidates) {
|
||||||
|
if (Modifier.isStatic(candidate.getModifiers()) == isStatic && mbd.isFactoryMethod(candidate)) {
|
||||||
|
candidateList.add(candidate);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2018 the original author or authors.
|
* Copyright 2002-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -237,6 +237,7 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
|
||||||
this.allowCaching = original.allowCaching;
|
this.allowCaching = original.allowCaching;
|
||||||
this.isFactoryMethodUnique = original.isFactoryMethodUnique;
|
this.isFactoryMethodUnique = original.isFactoryMethodUnique;
|
||||||
this.targetType = original.targetType;
|
this.targetType = original.targetType;
|
||||||
|
this.factoryMethodToIntrospect = original.factoryMethodToIntrospect;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -361,6 +362,16 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
|
||||||
this.isFactoryMethodUnique = true;
|
this.isFactoryMethodUnique = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify a factory method name that refers to an overloaded method.
|
||||||
|
* @since 5.2
|
||||||
|
*/
|
||||||
|
public void setNonUniqueFactoryMethodName(String name) {
|
||||||
|
Assert.hasText(name, "Factory method name must not be empty");
|
||||||
|
setFactoryMethodName(name);
|
||||||
|
this.isFactoryMethodUnique = false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether the given candidate qualifies as a factory method.
|
* Check whether the given candidate qualifies as a factory method.
|
||||||
*/
|
*/
|
||||||
|
|
@ -368,6 +379,15 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
|
||||||
return candidate.getName().equals(getFactoryMethodName());
|
return candidate.getName().equals(getFactoryMethodName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a resolved Java Method for the factory method on this bean definition.
|
||||||
|
* @param method the resolved factory method, or {@code null} to reset it
|
||||||
|
* @since 5.2
|
||||||
|
*/
|
||||||
|
public void setResolvedFactoryMethod(@Nullable Method method) {
|
||||||
|
this.factoryMethodToIntrospect = method;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the resolved factory method as a Java Method object, if available.
|
* Return the resolved factory method as a Java Method object, if available.
|
||||||
* @return the factory method, or {@code null} if not found or not resolved yet
|
* @return the factory method, or {@code null} if not found or not resolved yet
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2018 the original author or authors.
|
* Copyright 2002-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -50,6 +50,9 @@ import org.springframework.core.io.Resource;
|
||||||
import org.springframework.core.io.ResourceLoader;
|
import org.springframework.core.io.ResourceLoader;
|
||||||
import org.springframework.core.type.AnnotationMetadata;
|
import org.springframework.core.type.AnnotationMetadata;
|
||||||
import org.springframework.core.type.MethodMetadata;
|
import org.springframework.core.type.MethodMetadata;
|
||||||
|
import org.springframework.core.type.StandardAnnotationMetadata;
|
||||||
|
import org.springframework.core.type.StandardMethodMetadata;
|
||||||
|
import org.springframework.lang.NonNull;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
|
@ -214,14 +217,24 @@ class ConfigurationClassBeanDefinitionReader {
|
||||||
|
|
||||||
if (metadata.isStatic()) {
|
if (metadata.isStatic()) {
|
||||||
// static @Bean method
|
// static @Bean method
|
||||||
beanDef.setBeanClassName(configClass.getMetadata().getClassName());
|
if (configClass.getMetadata() instanceof StandardAnnotationMetadata) {
|
||||||
beanDef.setFactoryMethodName(methodName);
|
beanDef.setBeanClass(((StandardAnnotationMetadata) configClass.getMetadata()).getIntrospectedClass());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
beanDef.setBeanClassName(configClass.getMetadata().getClassName());
|
||||||
|
}
|
||||||
|
beanDef.setUniqueFactoryMethodName(methodName);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// instance @Bean method
|
// instance @Bean method
|
||||||
beanDef.setFactoryBeanName(configClass.getBeanName());
|
beanDef.setFactoryBeanName(configClass.getBeanName());
|
||||||
beanDef.setUniqueFactoryMethodName(methodName);
|
beanDef.setUniqueFactoryMethodName(methodName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (metadata instanceof StandardMethodMetadata) {
|
||||||
|
beanDef.setResolvedFactoryMethod(((StandardMethodMetadata) metadata).getIntrospectedMethod());
|
||||||
|
}
|
||||||
|
|
||||||
beanDef.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
|
beanDef.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
|
||||||
beanDef.setAttribute(org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor.
|
beanDef.setAttribute(org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor.
|
||||||
SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE);
|
SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE);
|
||||||
|
|
@ -286,8 +299,16 @@ class ConfigurationClassBeanDefinitionReader {
|
||||||
// preserve the existing bean definition.
|
// preserve the existing bean definition.
|
||||||
if (existingBeanDef instanceof ConfigurationClassBeanDefinition) {
|
if (existingBeanDef instanceof ConfigurationClassBeanDefinition) {
|
||||||
ConfigurationClassBeanDefinition ccbd = (ConfigurationClassBeanDefinition) existingBeanDef;
|
ConfigurationClassBeanDefinition ccbd = (ConfigurationClassBeanDefinition) existingBeanDef;
|
||||||
return ccbd.getMetadata().getClassName().equals(
|
if (ccbd.getMetadata().getClassName().equals(
|
||||||
beanMethod.getConfigurationClass().getMetadata().getClassName());
|
beanMethod.getConfigurationClass().getMetadata().getClassName())) {
|
||||||
|
if (ccbd.getFactoryMethodMetadata().getMethodName().equals(ccbd.getFactoryMethodName())) {
|
||||||
|
ccbd.setNonUniqueFactoryMethodName(ccbd.getFactoryMethodMetadata().getMethodName());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A bean definition resulting from a component scan can be silently overridden
|
// A bean definition resulting from a component scan can be silently overridden
|
||||||
|
|
@ -403,6 +424,7 @@ class ConfigurationClassBeanDefinitionReader {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@NonNull
|
||||||
public MethodMetadata getFactoryMethodMetadata() {
|
public MethodMetadata getFactoryMethodMetadata() {
|
||||||
return this.factoryMethodMetadata;
|
return this.factoryMethodMetadata;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ import org.springframework.beans.PropertyValues;
|
||||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||||
import org.springframework.beans.factory.BeanFactory;
|
import org.springframework.beans.factory.BeanFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||||
|
|
@ -57,14 +58,13 @@ import org.springframework.core.env.StandardEnvironment;
|
||||||
import org.springframework.core.io.DefaultResourceLoader;
|
import org.springframework.core.io.DefaultResourceLoader;
|
||||||
import org.springframework.core.io.ResourceLoader;
|
import org.springframework.core.io.ResourceLoader;
|
||||||
import org.springframework.core.type.AnnotationMetadata;
|
import org.springframework.core.type.AnnotationMetadata;
|
||||||
|
import org.springframework.core.type.MethodMetadata;
|
||||||
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
|
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
|
||||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
|
|
||||||
import static org.springframework.context.annotation.AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link BeanFactoryPostProcessor} used for bootstrapping processing of
|
* {@link BeanFactoryPostProcessor} used for bootstrapping processing of
|
||||||
* {@link Configuration @Configuration} classes.
|
* {@link Configuration @Configuration} classes.
|
||||||
|
|
@ -173,11 +173,10 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||||
* and a variant thereof for imported configuration classes (using unique fully-qualified
|
* and a variant thereof for imported configuration classes (using unique fully-qualified
|
||||||
* class names instead of standard component overriding).
|
* class names instead of standard component overriding).
|
||||||
* <p>Note that this strategy does <em>not</em> apply to {@link Bean} methods.
|
* <p>Note that this strategy does <em>not</em> apply to {@link Bean} methods.
|
||||||
* <p>This setter is typically only appropriate when configuring the post-processor as
|
* <p>This setter is typically only appropriate when configuring the post-processor as a
|
||||||
* a standalone bean definition in XML, e.g. not using the dedicated
|
* standalone bean definition in XML, e.g. not using the dedicated {@code AnnotationConfig*}
|
||||||
* {@code AnnotationConfig*} application contexts or the {@code
|
* application contexts or the {@code <context:annotation-config>} element. Any bean name
|
||||||
* <context:annotation-config>} element. Any bean name generator specified against
|
* generator specified against the application context will take precedence over any set here.
|
||||||
* the application context will take precedence over any value set here.
|
|
||||||
* @since 3.1.1
|
* @since 3.1.1
|
||||||
* @see AnnotationConfigApplicationContext#setBeanNameGenerator(BeanNameGenerator)
|
* @see AnnotationConfigApplicationContext#setBeanNameGenerator(BeanNameGenerator)
|
||||||
* @see AnnotationConfigUtils#CONFIGURATION_BEAN_NAME_GENERATOR
|
* @see AnnotationConfigUtils#CONFIGURATION_BEAN_NAME_GENERATOR
|
||||||
|
|
@ -264,8 +263,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||||
|
|
||||||
for (String beanName : candidateNames) {
|
for (String beanName : candidateNames) {
|
||||||
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
|
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
|
||||||
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
|
if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
|
||||||
ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
|
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
|
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
|
||||||
}
|
}
|
||||||
|
|
@ -292,7 +290,8 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||||
if (registry instanceof SingletonBeanRegistry) {
|
if (registry instanceof SingletonBeanRegistry) {
|
||||||
sbr = (SingletonBeanRegistry) registry;
|
sbr = (SingletonBeanRegistry) registry;
|
||||||
if (!this.localBeanNameGeneratorSet) {
|
if (!this.localBeanNameGeneratorSet) {
|
||||||
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
|
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(
|
||||||
|
AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
|
||||||
if (generator != null) {
|
if (generator != null) {
|
||||||
this.componentScanBeanNameGenerator = generator;
|
this.componentScanBeanNameGenerator = generator;
|
||||||
this.importBeanNameGenerator = generator;
|
this.importBeanNameGenerator = generator;
|
||||||
|
|
@ -371,7 +370,26 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||||
Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
|
Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
|
||||||
for (String beanName : beanFactory.getBeanDefinitionNames()) {
|
for (String beanName : beanFactory.getBeanDefinitionNames()) {
|
||||||
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
|
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
|
||||||
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef)) {
|
Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
|
||||||
|
MethodMetadata methodMetadata = null;
|
||||||
|
if (beanDef instanceof AnnotatedBeanDefinition) {
|
||||||
|
methodMetadata = ((AnnotatedBeanDefinition) beanDef).getFactoryMethodMetadata();
|
||||||
|
}
|
||||||
|
if ((configClassAttr != null || methodMetadata != null) && beanDef instanceof AbstractBeanDefinition) {
|
||||||
|
// Configuration class (full or lite) or a configuration-derived @Bean method
|
||||||
|
// -> resolve bean class at this point...
|
||||||
|
AbstractBeanDefinition abd = (AbstractBeanDefinition) beanDef;
|
||||||
|
if (!abd.hasBeanClass()) {
|
||||||
|
try {
|
||||||
|
abd.resolveBeanClass(this.beanClassLoader);
|
||||||
|
}
|
||||||
|
catch (Throwable ex) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {
|
||||||
if (!(beanDef instanceof AbstractBeanDefinition)) {
|
if (!(beanDef instanceof AbstractBeanDefinition)) {
|
||||||
throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" +
|
throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" +
|
||||||
beanName + "' since it is not stored in an AbstractBeanDefinition subclass");
|
beanName + "' since it is not stored in an AbstractBeanDefinition subclass");
|
||||||
|
|
@ -395,22 +413,15 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||||
AbstractBeanDefinition beanDef = entry.getValue();
|
AbstractBeanDefinition beanDef = entry.getValue();
|
||||||
// If a @Configuration class gets proxied, always proxy the target class
|
// If a @Configuration class gets proxied, always proxy the target class
|
||||||
beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
|
beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
|
||||||
try {
|
// Set enhanced subclass of the user-specified bean class
|
||||||
// Set enhanced subclass of the user-specified bean class
|
Class<?> configClass = beanDef.getBeanClass();
|
||||||
Class<?> configClass = beanDef.resolveBeanClass(this.beanClassLoader);
|
Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
|
||||||
if (configClass != null) {
|
if (configClass != enhancedClass) {
|
||||||
Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
|
if (logger.isTraceEnabled()) {
|
||||||
if (configClass != enhancedClass) {
|
logger.trace(String.format("Replacing bean definition '%s' existing class '%s' with " +
|
||||||
if (logger.isTraceEnabled()) {
|
"enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
|
||||||
logger.trace(String.format("Replacing bean definition '%s' existing class '%s' with " +
|
|
||||||
"enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
|
|
||||||
}
|
|
||||||
beanDef.setBeanClass(enhancedClass);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
beanDef.setBeanClass(enhancedClass);
|
||||||
catch (Throwable ex) {
|
|
||||||
throw new IllegalStateException("Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,11 +51,11 @@ import org.springframework.stereotype.Component;
|
||||||
*/
|
*/
|
||||||
abstract class ConfigurationClassUtils {
|
abstract class ConfigurationClassUtils {
|
||||||
|
|
||||||
private static final String CONFIGURATION_CLASS_FULL = "full";
|
public static final String CONFIGURATION_CLASS_FULL = "full";
|
||||||
|
|
||||||
private static final String CONFIGURATION_CLASS_LITE = "lite";
|
public static final String CONFIGURATION_CLASS_LITE = "lite";
|
||||||
|
|
||||||
private static final String CONFIGURATION_CLASS_ATTRIBUTE =
|
public static final String CONFIGURATION_CLASS_ATTRIBUTE =
|
||||||
Conventions.getQualifiedAttributeName(ConfigurationClassPostProcessor.class, "configurationClass");
|
Conventions.getQualifiedAttributeName(ConfigurationClassPostProcessor.class, "configurationClass");
|
||||||
|
|
||||||
private static final String ORDER_ATTRIBUTE =
|
private static final String ORDER_ATTRIBUTE =
|
||||||
|
|
@ -174,22 +174,6 @@ abstract class ConfigurationClassUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine whether the given bean definition indicates a full {@code @Configuration}
|
|
||||||
* class, through checking {@link #checkConfigurationClassCandidate}'s metadata marker.
|
|
||||||
*/
|
|
||||||
public static boolean isFullConfigurationClass(BeanDefinition beanDef) {
|
|
||||||
return CONFIGURATION_CLASS_FULL.equals(beanDef.getAttribute(CONFIGURATION_CLASS_ATTRIBUTE));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine whether the given bean definition indicates a lite {@code @Configuration}
|
|
||||||
* class, through checking {@link #checkConfigurationClassCandidate}'s metadata marker.
|
|
||||||
*/
|
|
||||||
public static boolean isLiteConfigurationClass(BeanDefinition beanDef) {
|
|
||||||
return CONFIGURATION_CLASS_LITE.equals(beanDef.getAttribute(CONFIGURATION_CLASS_ATTRIBUTE));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine the order for the given configuration class metadata.
|
* Determine the order for the given configuration class metadata.
|
||||||
* @param metadata the metadata of the annotated class
|
* @param metadata the metadata of the annotated class
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,19 @@ public class ConfigurationClassPostProcessorTests {
|
||||||
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(SingletonBeanConfig.class));
|
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(SingletonBeanConfig.class));
|
||||||
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||||
pp.postProcessBeanFactory(beanFactory);
|
pp.postProcessBeanFactory(beanFactory);
|
||||||
|
assertTrue(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).hasBeanClass());
|
||||||
|
Foo foo = beanFactory.getBean("foo", Foo.class);
|
||||||
|
Bar bar = beanFactory.getBean("bar", Bar.class);
|
||||||
|
assertSame(foo, bar.foo);
|
||||||
|
assertTrue(Arrays.asList(beanFactory.getDependentBeans("foo")).contains("bar"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void enhancementIsPresentBecauseSingletonSemanticsAreRespectedUsingAsm() {
|
||||||
|
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(SingletonBeanConfig.class.getName()));
|
||||||
|
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||||
|
pp.postProcessBeanFactory(beanFactory);
|
||||||
|
assertTrue(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).hasBeanClass());
|
||||||
Foo foo = beanFactory.getBean("foo", Foo.class);
|
Foo foo = beanFactory.getBean("foo", Foo.class);
|
||||||
Bar bar = beanFactory.getBean("bar", Bar.class);
|
Bar bar = beanFactory.getBean("bar", Bar.class);
|
||||||
assertSame(foo, bar.foo);
|
assertSame(foo, bar.foo);
|
||||||
|
|
@ -108,6 +121,44 @@ public class ConfigurationClassPostProcessorTests {
|
||||||
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(NonEnhancedSingletonBeanConfig.class));
|
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(NonEnhancedSingletonBeanConfig.class));
|
||||||
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||||
pp.postProcessBeanFactory(beanFactory);
|
pp.postProcessBeanFactory(beanFactory);
|
||||||
|
assertTrue(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).hasBeanClass());
|
||||||
|
Foo foo = beanFactory.getBean("foo", Foo.class);
|
||||||
|
Bar bar = beanFactory.getBean("bar", Bar.class);
|
||||||
|
assertNotSame(foo, bar.foo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void enhancementIsNotPresentForProxyBeanMethodsFlagSetToFalseUsingAsm() {
|
||||||
|
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(NonEnhancedSingletonBeanConfig.class.getName()));
|
||||||
|
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||||
|
pp.postProcessBeanFactory(beanFactory);
|
||||||
|
assertTrue(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).hasBeanClass());
|
||||||
|
Foo foo = beanFactory.getBean("foo", Foo.class);
|
||||||
|
Bar bar = beanFactory.getBean("bar", Bar.class);
|
||||||
|
assertNotSame(foo, bar.foo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void enhancementIsNotPresentForStaticMethods() {
|
||||||
|
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(StaticSingletonBeanConfig.class));
|
||||||
|
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||||
|
pp.postProcessBeanFactory(beanFactory);
|
||||||
|
assertTrue(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).hasBeanClass());
|
||||||
|
assertTrue(((RootBeanDefinition) beanFactory.getBeanDefinition("foo")).hasBeanClass());
|
||||||
|
assertTrue(((RootBeanDefinition) beanFactory.getBeanDefinition("bar")).hasBeanClass());
|
||||||
|
Foo foo = beanFactory.getBean("foo", Foo.class);
|
||||||
|
Bar bar = beanFactory.getBean("bar", Bar.class);
|
||||||
|
assertNotSame(foo, bar.foo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void enhancementIsNotPresentForStaticMethodsUsingAsm() {
|
||||||
|
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(StaticSingletonBeanConfig.class.getName()));
|
||||||
|
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||||
|
pp.postProcessBeanFactory(beanFactory);
|
||||||
|
assertTrue(((RootBeanDefinition) beanFactory.getBeanDefinition("config")).hasBeanClass());
|
||||||
|
assertTrue(((RootBeanDefinition) beanFactory.getBeanDefinition("foo")).hasBeanClass());
|
||||||
|
assertTrue(((RootBeanDefinition) beanFactory.getBeanDefinition("bar")).hasBeanClass());
|
||||||
Foo foo = beanFactory.getBean("foo", Foo.class);
|
Foo foo = beanFactory.getBean("foo", Foo.class);
|
||||||
Bar bar = beanFactory.getBean("bar", Bar.class);
|
Bar bar = beanFactory.getBean("bar", Bar.class);
|
||||||
assertNotSame(foo, bar.foo);
|
assertNotSame(foo, bar.foo);
|
||||||
|
|
@ -1090,6 +1141,18 @@ public class ConfigurationClassPostProcessorTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
static class StaticSingletonBeanConfig {
|
||||||
|
|
||||||
|
public static @Bean Foo foo() {
|
||||||
|
return new Foo();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @Bean Bar bar() {
|
||||||
|
return new Bar(foo());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@Order(2)
|
@Order(2)
|
||||||
static class OverridingSingletonBeanConfig {
|
static class OverridingSingletonBeanConfig {
|
||||||
|
|
|
||||||
|
|
@ -51,13 +51,13 @@ public abstract class ReflectionUtils {
|
||||||
* @since 3.0.5
|
* @since 3.0.5
|
||||||
*/
|
*/
|
||||||
public static final MethodFilter USER_DECLARED_METHODS =
|
public static final MethodFilter USER_DECLARED_METHODS =
|
||||||
(method -> (!method.isBridge() && !method.isSynthetic() && method.getDeclaringClass() != Object.class));
|
(method -> !method.isBridge() && !method.isSynthetic());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pre-built FieldFilter that matches all non-static, non-final fields.
|
* Pre-built FieldFilter that matches all non-static, non-final fields.
|
||||||
*/
|
*/
|
||||||
public static final FieldFilter COPYABLE_FIELDS =
|
public static final FieldFilter COPYABLE_FIELDS =
|
||||||
field -> !(Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers()));
|
(field -> !(Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers())));
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -536,7 +536,7 @@ public abstract class ReflectionUtils {
|
||||||
throw new IllegalStateException("Not allowed to access method '" + method.getName() + "': " + ex);
|
throw new IllegalStateException("Not allowed to access method '" + method.getName() + "': " + ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (clazz.getSuperclass() != null) {
|
if (clazz.getSuperclass() != null && (mf != USER_DECLARED_METHODS || clazz.getSuperclass() != Object.class)) {
|
||||||
doWithMethods(clazz.getSuperclass(), mc, mf);
|
doWithMethods(clazz.getSuperclass(), mc, mf);
|
||||||
}
|
}
|
||||||
else if (clazz.isInterface()) {
|
else if (clazz.isInterface()) {
|
||||||
|
|
@ -566,6 +566,19 @@ public abstract class ReflectionUtils {
|
||||||
* @throws IllegalStateException if introspection fails
|
* @throws IllegalStateException if introspection fails
|
||||||
*/
|
*/
|
||||||
public static Method[] getUniqueDeclaredMethods(Class<?> leafClass) {
|
public static Method[] getUniqueDeclaredMethods(Class<?> leafClass) {
|
||||||
|
return getUniqueDeclaredMethods(leafClass, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the unique set of declared methods on the leaf class and all superclasses.
|
||||||
|
* Leaf class methods are included first and while traversing the superclass hierarchy
|
||||||
|
* any methods found with signatures matching a method already included are filtered out.
|
||||||
|
* @param leafClass the class to introspect
|
||||||
|
* @param mf the filter that determines the methods to take into account
|
||||||
|
* @throws IllegalStateException if introspection fails
|
||||||
|
* @since 5.2
|
||||||
|
*/
|
||||||
|
public static Method[] getUniqueDeclaredMethods(Class<?> leafClass, @Nullable MethodFilter mf) {
|
||||||
final List<Method> methods = new ArrayList<>(32);
|
final List<Method> methods = new ArrayList<>(32);
|
||||||
doWithMethods(leafClass, method -> {
|
doWithMethods(leafClass, method -> {
|
||||||
boolean knownSignature = false;
|
boolean knownSignature = false;
|
||||||
|
|
@ -590,7 +603,7 @@ public abstract class ReflectionUtils {
|
||||||
if (!knownSignature && !isCglibRenamedMethod(method)) {
|
if (!knownSignature && !isCglibRenamedMethod(method)) {
|
||||||
methods.add(method);
|
methods.add(method);
|
||||||
}
|
}
|
||||||
});
|
}, mf);
|
||||||
return methods.toArray(new Method[0]);
|
return methods.toArray(new Method[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2018 the original author or authors.
|
* Copyright 2002-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -423,7 +423,7 @@ public class TransactionalTestExecutionListener extends AbstractTestExecutionLis
|
||||||
* as well as annotated interface default methods
|
* as well as annotated interface default methods
|
||||||
*/
|
*/
|
||||||
private List<Method> getAnnotatedMethods(Class<?> clazz, Class<? extends Annotation> annotationType) {
|
private List<Method> getAnnotatedMethods(Class<?> clazz, Class<? extends Annotation> annotationType) {
|
||||||
return Arrays.stream(ReflectionUtils.getUniqueDeclaredMethods(clazz))
|
return Arrays.stream(ReflectionUtils.getUniqueDeclaredMethods(clazz, ReflectionUtils.USER_DECLARED_METHODS))
|
||||||
.filter(method -> AnnotatedElementUtils.hasAnnotation(method, annotationType))
|
.filter(method -> AnnotatedElementUtils.hasAnnotation(method, annotationType))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue