Merge branch '6.0.x'
# Conflicts: # spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java
This commit is contained in:
commit
f7c3e6480a
|
|
@ -17,6 +17,7 @@
|
|||
package org.springframework.beans.factory.annotation;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.lang.reflect.Constructor;
|
||||
|
|
@ -79,6 +80,10 @@ import org.springframework.core.PriorityOrdered;
|
|||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.annotation.MergedAnnotation;
|
||||
import org.springframework.core.annotation.MergedAnnotations;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.core.type.MethodMetadata;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
|
||||
import org.springframework.javapoet.ClassName;
|
||||
import org.springframework.javapoet.CodeBlock;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
|
@ -167,6 +172,9 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
|||
@Nullable
|
||||
private ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
@Nullable
|
||||
private MetadataReaderFactory metadataReaderFactory;
|
||||
|
||||
private final Set<String> lookupMethodsChecked = Collections.newSetFromMap(new ConcurrentHashMap<>(256));
|
||||
|
||||
private final Map<Class<?>, Constructor<?>[]> candidateConstructorsCache = new ConcurrentHashMap<>(256);
|
||||
|
|
@ -271,6 +279,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
|||
"AutowiredAnnotationBeanPostProcessor requires a ConfigurableListableBeanFactory: " + beanFactory);
|
||||
}
|
||||
this.beanFactory = clbf;
|
||||
this.metadataReaderFactory = new SimpleMetadataReaderFactory(clbf.getBeanClassLoader());
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -539,12 +548,11 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
|||
return InjectionMetadata.EMPTY;
|
||||
}
|
||||
|
||||
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
|
||||
final List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
|
||||
Class<?> targetClass = clazz;
|
||||
|
||||
do {
|
||||
final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
|
||||
|
||||
final List<InjectionMetadata.InjectedElement> fieldElements = new ArrayList<>();
|
||||
ReflectionUtils.doWithLocalFields(targetClass, field -> {
|
||||
MergedAnnotation<?> ann = findAutowiredAnnotation(field);
|
||||
if (ann != null) {
|
||||
|
|
@ -555,10 +563,11 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
|||
return;
|
||||
}
|
||||
boolean required = determineRequiredStatus(ann);
|
||||
currElements.add(new AutowiredFieldElement(field, required));
|
||||
fieldElements.add(new AutowiredFieldElement(field, required));
|
||||
}
|
||||
});
|
||||
|
||||
final List<InjectionMetadata.InjectedElement> methodElements = new ArrayList<>();
|
||||
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
|
||||
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
|
||||
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
|
||||
|
|
@ -580,11 +589,12 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
|||
}
|
||||
boolean required = determineRequiredStatus(ann);
|
||||
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
|
||||
currElements.add(new AutowiredMethodElement(method, required, pd));
|
||||
methodElements.add(new AutowiredMethodElement(method, required, pd));
|
||||
}
|
||||
});
|
||||
|
||||
elements.addAll(0, currElements);
|
||||
elements.addAll(0, sortMethodElements(methodElements, targetClass));
|
||||
elements.addAll(0, fieldElements);
|
||||
targetClass = targetClass.getSuperclass();
|
||||
}
|
||||
while (targetClass != null && targetClass != Object.class);
|
||||
|
|
@ -617,6 +627,47 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
|||
this.requiredParameterValue == ann.getBoolean(this.requiredParameterName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort the method elements via ASM for deterministic declaration order if possible.
|
||||
*/
|
||||
private List<InjectionMetadata.InjectedElement> sortMethodElements(
|
||||
List<InjectionMetadata.InjectedElement> methodElements, Class<?> targetClass) {
|
||||
|
||||
if (this.metadataReaderFactory != null && methodElements.size() > 1) {
|
||||
// Try reading the class file via ASM for deterministic declaration order...
|
||||
// Unfortunately, the JVM's standard reflection returns methods in arbitrary
|
||||
// order, even between different runs of the same application on the same JVM.
|
||||
try {
|
||||
AnnotationMetadata asm =
|
||||
this.metadataReaderFactory.getMetadataReader(targetClass.getName()).getAnnotationMetadata();
|
||||
Set<MethodMetadata> asmMethods = asm.getAnnotatedMethods(Autowired.class.getName());
|
||||
if (asmMethods.size() >= methodElements.size()) {
|
||||
List<InjectionMetadata.InjectedElement> candidateMethods = new ArrayList<>(methodElements);
|
||||
List<InjectionMetadata.InjectedElement> selectedMethods = new ArrayList<>(asmMethods.size());
|
||||
for (MethodMetadata asmMethod : asmMethods) {
|
||||
for (Iterator<InjectionMetadata.InjectedElement> it = candidateMethods.iterator(); it.hasNext();) {
|
||||
InjectionMetadata.InjectedElement element = it.next();
|
||||
if (element.getMember().getName().equals(asmMethod.getMethodName())) {
|
||||
selectedMethods.add(element);
|
||||
it.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (selectedMethods.size() == methodElements.size()) {
|
||||
// All reflection-detected methods found in ASM method set -> proceed
|
||||
return selectedMethods;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
logger.debug("Failed to read class file via ASM for determining @Autowired method order", ex);
|
||||
// No worries, let's continue with the reflection metadata we started with...
|
||||
}
|
||||
}
|
||||
return methodElements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the specified bean as dependent on the autowired beans.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -840,16 +840,13 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
|||
return result;
|
||||
}
|
||||
|
||||
ResolvableType beanType =
|
||||
(mbd.hasBeanClass() ? ResolvableType.forClass(mbd.getBeanClass()) : ResolvableType.NONE);
|
||||
|
||||
// For instance supplied beans try the target type and bean class
|
||||
// For instance supplied beans, try the target type and bean class immediately
|
||||
if (mbd.getInstanceSupplier() != null) {
|
||||
result = getFactoryBeanGeneric(mbd.targetType);
|
||||
if (result.resolve() != null) {
|
||||
return result;
|
||||
}
|
||||
result = getFactoryBeanGeneric(beanType);
|
||||
result = getFactoryBeanGeneric(mbd.hasBeanClass() ? ResolvableType.forClass(mbd.getBeanClass()) : null);
|
||||
if (result.resolve() != null) {
|
||||
return result;
|
||||
}
|
||||
|
|
@ -912,22 +909,20 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
|||
return getTypeForFactoryBeanFromMethod(mbd.getBeanClass(), factoryMethodName);
|
||||
}
|
||||
|
||||
result = getFactoryBeanGeneric(mbd.targetType);
|
||||
if (result.resolve() != null) {
|
||||
return result;
|
||||
// For regular beans, try the target type and bean class as fallback
|
||||
if (mbd.getInstanceSupplier() == null) {
|
||||
result = getFactoryBeanGeneric(mbd.targetType);
|
||||
if (result.resolve() != null) {
|
||||
return result;
|
||||
}
|
||||
result = getFactoryBeanGeneric(mbd.hasBeanClass() ? ResolvableType.forClass(mbd.getBeanClass()) : null);
|
||||
if (result.resolve() != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
result = getFactoryBeanGeneric(beanType);
|
||||
if (result.resolve() != null) {
|
||||
return result;
|
||||
}
|
||||
return ResolvableType.NONE;
|
||||
}
|
||||
|
||||
private ResolvableType getFactoryBeanGeneric(@Nullable ResolvableType type) {
|
||||
if (type == null) {
|
||||
return ResolvableType.NONE;
|
||||
}
|
||||
return type.as(FactoryBean.class).getGeneric();
|
||||
// FactoryBean type not resolvable
|
||||
return ResolvableType.NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -64,7 +64,6 @@ import org.springframework.beans.factory.config.DestructionAwareBeanPostProcesso
|
|||
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.Scope;
|
||||
import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
|
||||
import org.springframework.core.AttributeAccessor;
|
||||
import org.springframework.core.DecoratingClassLoader;
|
||||
import org.springframework.core.NamedThreadLocal;
|
||||
import org.springframework.core.ResolvableType;
|
||||
|
|
@ -1690,30 +1689,9 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
|||
onSuppressedException(ex);
|
||||
}
|
||||
}
|
||||
return ResolvableType.NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the bean type for a FactoryBean by inspecting its attributes for a
|
||||
* {@link FactoryBean#OBJECT_TYPE_ATTRIBUTE} value.
|
||||
* @param attributes the attributes to inspect
|
||||
* @return a {@link ResolvableType} extracted from the attributes or
|
||||
* {@code ResolvableType.NONE}
|
||||
* @since 5.2
|
||||
*/
|
||||
ResolvableType getTypeForFactoryBeanFromAttributes(AttributeAccessor attributes) {
|
||||
Object attribute = attributes.getAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE);
|
||||
if (attribute == null) {
|
||||
return ResolvableType.NONE;
|
||||
}
|
||||
if (attribute instanceof ResolvableType resolvableType) {
|
||||
return resolvableType;
|
||||
}
|
||||
if (attribute instanceof Class<?> clazz) {
|
||||
return ResolvableType.forClass(clazz);
|
||||
}
|
||||
throw new IllegalArgumentException("Invalid value type for attribute '" +
|
||||
FactoryBean.OBJECT_TYPE_ATTRIBUTE + "': " + attribute.getClass().getName());
|
||||
// FactoryBean type not resolvable
|
||||
return ResolvableType.NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ import org.springframework.beans.factory.BeanCreationException;
|
|||
import org.springframework.beans.factory.BeanCurrentlyInCreationException;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.FactoryBeanNotInitializedException;
|
||||
import org.springframework.core.AttributeAccessor;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
|
|
@ -61,6 +63,34 @@ public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanReg
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the bean type for a FactoryBean by inspecting its attributes for a
|
||||
* {@link FactoryBean#OBJECT_TYPE_ATTRIBUTE} value.
|
||||
* @param attributes the attributes to inspect
|
||||
* @return a {@link ResolvableType} extracted from the attributes or
|
||||
* {@code ResolvableType.NONE}
|
||||
* @since 5.2
|
||||
*/
|
||||
ResolvableType getTypeForFactoryBeanFromAttributes(AttributeAccessor attributes) {
|
||||
Object attribute = attributes.getAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE);
|
||||
if (attribute instanceof ResolvableType resolvableType) {
|
||||
return resolvableType;
|
||||
}
|
||||
if (attribute instanceof Class<?> clazz) {
|
||||
return ResolvableType.forClass(clazz);
|
||||
}
|
||||
return ResolvableType.NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the FactoryBean object type from the given generic declaration.
|
||||
* @param type the FactoryBean type
|
||||
* @return the nested object type, or {@code NONE} if not resolvable
|
||||
*/
|
||||
ResolvableType getFactoryBeanGeneric(@Nullable ResolvableType type) {
|
||||
return (type != null ? type.as(FactoryBean.class).getGeneric() : ResolvableType.NONE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain an object to expose from the given FactoryBean, if available
|
||||
* in cached form. Quick check for minimal synchronization.
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
|||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.testfixture.io.SerializationTestUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
|
|
@ -2605,13 +2606,12 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||
@Autowired(required = false)
|
||||
private TestBean testBean;
|
||||
|
||||
private TestBean testBean2;
|
||||
TestBean testBean2;
|
||||
|
||||
@Autowired
|
||||
public void setTestBean2(TestBean testBean2) {
|
||||
if (this.testBean2 != null) {
|
||||
throw new IllegalStateException("Already called");
|
||||
}
|
||||
Assert.state(this.testBean != null, "Wrong initialization order");
|
||||
Assert.state(this.testBean2 == null, "Already called");
|
||||
this.testBean2 = testBean2;
|
||||
}
|
||||
|
||||
|
|
@ -2643,9 +2643,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||
|
||||
@Override
|
||||
@Autowired
|
||||
@SuppressWarnings("deprecation")
|
||||
public void setTestBean2(TestBean testBean2) {
|
||||
super.setTestBean2(testBean2);
|
||||
this.testBean2 = testBean2;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
|
|
@ -2661,6 +2660,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||
|
||||
@Autowired
|
||||
protected void initBeanFactory(BeanFactory beanFactory) {
|
||||
Assert.state(this.baseInjected, "Wrong initialization order");
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
|
|
@ -4084,9 +4084,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||
private RT obj;
|
||||
|
||||
protected void setObj(RT obj) {
|
||||
if (this.obj != null) {
|
||||
throw new IllegalStateException("Already called");
|
||||
}
|
||||
Assert.state(this.obj == null, "Already called");
|
||||
this.obj = obj;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 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.
|
||||
|
|
@ -314,8 +314,7 @@ class ConfigurationClassBeanDefinitionReader {
|
|||
|
||||
// At this point, it's a top-level override (probably XML), just having been parsed
|
||||
// before configuration class processing kicks in...
|
||||
if (this.registry instanceof DefaultListableBeanFactory dlbf &&
|
||||
!dlbf.isAllowBeanDefinitionOverriding()) {
|
||||
if (this.registry instanceof DefaultListableBeanFactory dlbf && !dlbf.isAllowBeanDefinitionOverriding()) {
|
||||
throw new BeanDefinitionStoreException(beanMethod.getConfigurationClass().getResource().getDescription(),
|
||||
beanName, "@Bean definition illegally overridden by existing bean definition: " + existingBeanDef);
|
||||
}
|
||||
|
|
@ -401,6 +400,7 @@ class ConfigurationClassBeanDefinitionReader {
|
|||
|
||||
public ConfigurationClassBeanDefinition(RootBeanDefinition original,
|
||||
ConfigurationClass configClass, MethodMetadata beanMethodMetadata, String derivedBeanName) {
|
||||
|
||||
super(original);
|
||||
this.annotationMetadata = configClass.getMetadata();
|
||||
this.factoryMethodMetadata = beanMethodMetadata;
|
||||
|
|
|
|||
|
|
@ -403,11 +403,14 @@ class ConfigurationClassParser {
|
|||
this.metadataReaderFactory.getMetadataReader(original.getClassName()).getAnnotationMetadata();
|
||||
Set<MethodMetadata> asmMethods = asm.getAnnotatedMethods(Bean.class.getName());
|
||||
if (asmMethods.size() >= beanMethods.size()) {
|
||||
Set<MethodMetadata> candidateMethods = new LinkedHashSet<>(beanMethods);
|
||||
Set<MethodMetadata> selectedMethods = new LinkedHashSet<>(asmMethods.size());
|
||||
for (MethodMetadata asmMethod : asmMethods) {
|
||||
for (MethodMetadata beanMethod : beanMethods) {
|
||||
for (Iterator<MethodMetadata> it = candidateMethods.iterator(); it.hasNext();) {
|
||||
MethodMetadata beanMethod = it.next();
|
||||
if (beanMethod.getMethodName().equals(asmMethod.getMethodName())) {
|
||||
selectedMethods.add(beanMethod);
|
||||
it.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue