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:
@@ -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");
|
||||
* 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.type.AnnotationMetadata;
|
||||
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.StringUtils;
|
||||
|
||||
@@ -214,14 +217,24 @@ class ConfigurationClassBeanDefinitionReader {
|
||||
|
||||
if (metadata.isStatic()) {
|
||||
// static @Bean method
|
||||
beanDef.setBeanClassName(configClass.getMetadata().getClassName());
|
||||
beanDef.setFactoryMethodName(methodName);
|
||||
if (configClass.getMetadata() instanceof StandardAnnotationMetadata) {
|
||||
beanDef.setBeanClass(((StandardAnnotationMetadata) configClass.getMetadata()).getIntrospectedClass());
|
||||
}
|
||||
else {
|
||||
beanDef.setBeanClassName(configClass.getMetadata().getClassName());
|
||||
}
|
||||
beanDef.setUniqueFactoryMethodName(methodName);
|
||||
}
|
||||
else {
|
||||
// instance @Bean method
|
||||
beanDef.setFactoryBeanName(configClass.getBeanName());
|
||||
beanDef.setUniqueFactoryMethodName(methodName);
|
||||
}
|
||||
|
||||
if (metadata instanceof StandardMethodMetadata) {
|
||||
beanDef.setResolvedFactoryMethod(((StandardMethodMetadata) metadata).getIntrospectedMethod());
|
||||
}
|
||||
|
||||
beanDef.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
|
||||
beanDef.setAttribute(org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor.
|
||||
SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE);
|
||||
@@ -286,8 +299,16 @@ class ConfigurationClassBeanDefinitionReader {
|
||||
// preserve the existing bean definition.
|
||||
if (existingBeanDef instanceof ConfigurationClassBeanDefinition) {
|
||||
ConfigurationClassBeanDefinition ccbd = (ConfigurationClassBeanDefinition) existingBeanDef;
|
||||
return ccbd.getMetadata().getClassName().equals(
|
||||
beanMethod.getConfigurationClass().getMetadata().getClassName());
|
||||
if (ccbd.getMetadata().getClassName().equals(
|
||||
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
|
||||
@@ -403,6 +424,7 @@ class ConfigurationClassBeanDefinitionReader {
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public MethodMetadata getFactoryMethodMetadata() {
|
||||
return this.factoryMethodMetadata;
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.springframework.beans.PropertyValues;
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
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.BeanDefinitionHolder;
|
||||
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.ResourceLoader;
|
||||
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.MetadataReaderFactory;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import static org.springframework.context.annotation.AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR;
|
||||
|
||||
/**
|
||||
* {@link BeanFactoryPostProcessor} used for bootstrapping processing of
|
||||
* {@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
|
||||
* class names instead of standard component overriding).
|
||||
* <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
|
||||
* a standalone bean definition in XML, e.g. not using the dedicated
|
||||
* {@code AnnotationConfig*} application contexts or the {@code
|
||||
* <context:annotation-config>} element. Any bean name generator specified against
|
||||
* the application context will take precedence over any value set here.
|
||||
* <p>This setter is typically only appropriate when configuring the post-processor as a
|
||||
* standalone bean definition in XML, e.g. not using the dedicated {@code AnnotationConfig*}
|
||||
* application contexts or the {@code <context:annotation-config>} element. Any bean name
|
||||
* generator specified against the application context will take precedence over any set here.
|
||||
* @since 3.1.1
|
||||
* @see AnnotationConfigApplicationContext#setBeanNameGenerator(BeanNameGenerator)
|
||||
* @see AnnotationConfigUtils#CONFIGURATION_BEAN_NAME_GENERATOR
|
||||
@@ -264,8 +263,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
|
||||
for (String beanName : candidateNames) {
|
||||
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
|
||||
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
|
||||
ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
|
||||
if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
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) {
|
||||
sbr = (SingletonBeanRegistry) registry;
|
||||
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) {
|
||||
this.componentScanBeanNameGenerator = generator;
|
||||
this.importBeanNameGenerator = generator;
|
||||
@@ -371,7 +370,26 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
|
||||
for (String beanName : beanFactory.getBeanDefinitionNames()) {
|
||||
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)) {
|
||||
throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" +
|
||||
beanName + "' since it is not stored in an AbstractBeanDefinition subclass");
|
||||
@@ -395,22 +413,15 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
AbstractBeanDefinition beanDef = entry.getValue();
|
||||
// If a @Configuration class gets proxied, always proxy the target class
|
||||
beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
|
||||
try {
|
||||
// Set enhanced subclass of the user-specified bean class
|
||||
Class<?> configClass = beanDef.resolveBeanClass(this.beanClassLoader);
|
||||
if (configClass != null) {
|
||||
Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
|
||||
if (configClass != enhancedClass) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(String.format("Replacing bean definition '%s' existing class '%s' with " +
|
||||
"enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
|
||||
}
|
||||
beanDef.setBeanClass(enhancedClass);
|
||||
}
|
||||
// Set enhanced subclass of the user-specified bean class
|
||||
Class<?> configClass = beanDef.getBeanClass();
|
||||
Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
|
||||
if (configClass != enhancedClass) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(String.format("Replacing bean definition '%s' existing class '%s' with " +
|
||||
"enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new IllegalStateException("Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
|
||||
beanDef.setBeanClass(enhancedClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,11 +51,11 @@ import org.springframework.stereotype.Component;
|
||||
*/
|
||||
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");
|
||||
|
||||
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.
|
||||
* @param metadata the metadata of the annotated class
|
||||
|
||||
Reference in New Issue
Block a user