Enable cglib proxy for configuration classes if necessary
This commit updates code generation to customize the instantiation of a configuration class that requires a proxy. Rather than instantiating the raw class, the proxy is used. Closes gh-29107
This commit is contained in:
@@ -73,7 +73,7 @@ import org.springframework.util.ReflectionUtils;
|
||||
class ConfigurationClassEnhancer {
|
||||
|
||||
// The callbacks to use. Note that these callbacks must be stateless.
|
||||
private static final Callback[] CALLBACKS = new Callback[] {
|
||||
static final Callback[] CALLBACKS = new Callback[] {
|
||||
new BeanMethodInterceptor(),
|
||||
new BeanFactoryAwareMethodInterceptor(),
|
||||
NoOp.INSTANCE
|
||||
|
||||
@@ -18,6 +18,8 @@ package org.springframework.context.annotation;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Executable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@@ -26,6 +28,7 @@ import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import javax.lang.model.element.Modifier;
|
||||
@@ -46,6 +49,11 @@ import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution;
|
||||
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor;
|
||||
import org.springframework.beans.factory.aot.BeanFactoryInitializationCode;
|
||||
import org.springframework.beans.factory.aot.BeanRegistrationAotContribution;
|
||||
import org.springframework.beans.factory.aot.BeanRegistrationAotProcessor;
|
||||
import org.springframework.beans.factory.aot.BeanRegistrationCode;
|
||||
import org.springframework.beans.factory.aot.BeanRegistrationCodeFragments;
|
||||
import org.springframework.beans.factory.aot.BeanRegistrationCodeFragmentsDecorator;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
@@ -61,6 +69,7 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
|
||||
import org.springframework.beans.factory.support.BeanNameGenerator;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RegisteredBean;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.ApplicationStartupAware;
|
||||
import org.springframework.context.EnvironmentAware;
|
||||
@@ -110,8 +119,8 @@ import org.springframework.util.CollectionUtils;
|
||||
* @since 3.0
|
||||
*/
|
||||
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
|
||||
BeanFactoryInitializationAotProcessor, PriorityOrdered, ResourceLoaderAware, ApplicationStartupAware,
|
||||
BeanClassLoaderAware, EnvironmentAware {
|
||||
BeanRegistrationAotProcessor, BeanFactoryInitializationAotProcessor, PriorityOrdered,
|
||||
ResourceLoaderAware, ApplicationStartupAware, BeanClassLoaderAware, EnvironmentAware {
|
||||
|
||||
/**
|
||||
* A {@code BeanNameGenerator} using fully qualified class names as default bean names.
|
||||
@@ -294,6 +303,19 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) {
|
||||
Object configClassAttr = registeredBean.getMergedBeanDefinition()
|
||||
.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
|
||||
if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {
|
||||
Class<?> proxyClass = registeredBean.getBeanType().toClass();
|
||||
return BeanRegistrationAotContribution.withCustomCodeFragments(codeFragments ->
|
||||
new ConfigurationClassProxyBeanRegistrationCodeFragments(codeFragments, proxyClass));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) {
|
||||
boolean hasPropertySourceDescriptors = !CollectionUtils.isEmpty(this.propertySourceDescriptors);
|
||||
@@ -692,4 +714,47 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
|
||||
}
|
||||
|
||||
private static class ConfigurationClassProxyBeanRegistrationCodeFragments extends BeanRegistrationCodeFragmentsDecorator {
|
||||
|
||||
private final Class<?> proxyClass;
|
||||
|
||||
public ConfigurationClassProxyBeanRegistrationCodeFragments(BeanRegistrationCodeFragments codeFragments,
|
||||
Class<?> proxyClass) {
|
||||
super(codeFragments);
|
||||
this.proxyClass = proxyClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodeBlock generateSetBeanDefinitionPropertiesCode(GenerationContext generationContext,
|
||||
BeanRegistrationCode beanRegistrationCode, RootBeanDefinition beanDefinition, Predicate<String> attributeFilter) {
|
||||
CodeBlock.Builder code = CodeBlock.builder();
|
||||
code.add(super.generateSetBeanDefinitionPropertiesCode(generationContext,
|
||||
beanRegistrationCode, beanDefinition, attributeFilter));
|
||||
code.addStatement("$T.initializeConfigurationClass($T.class)",
|
||||
ConfigurationClassUtils.class, ClassUtils.getUserClass(this.proxyClass));
|
||||
return code.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodeBlock generateInstanceSupplierCode(GenerationContext generationContext,
|
||||
BeanRegistrationCode beanRegistrationCode, Executable constructorOrFactoryMethod,
|
||||
boolean allowDirectSupplierShortcut) {
|
||||
return super.generateInstanceSupplierCode(generationContext, beanRegistrationCode,
|
||||
proxyExecutable(constructorOrFactoryMethod), allowDirectSupplierShortcut);
|
||||
}
|
||||
|
||||
private Executable proxyExecutable(Executable rawClassExecutable) {
|
||||
if (rawClassExecutable instanceof Constructor<?>) {
|
||||
try {
|
||||
return this.proxyClass.getConstructor(rawClassExecutable.getParameterTypes());
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
throw new IllegalStateException("No matching constructor found on proxy " + this.proxyClass, ex);
|
||||
}
|
||||
}
|
||||
return rawClassExecutable;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -30,6 +30,7 @@ import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.cglib.proxy.Enhancer;
|
||||
import org.springframework.context.event.EventListenerFactory;
|
||||
import org.springframework.core.Conventions;
|
||||
import org.springframework.core.Ordered;
|
||||
@@ -42,23 +43,24 @@ import org.springframework.lang.Nullable;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Utilities for identifying {@link Configuration} classes.
|
||||
* Utilities for identifying and configuring {@link Configuration} classes.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @since 3.1
|
||||
* @author Stephane Nicoll
|
||||
* @since 6.0
|
||||
*/
|
||||
abstract class ConfigurationClassUtils {
|
||||
public abstract class ConfigurationClassUtils {
|
||||
|
||||
public static final String CONFIGURATION_CLASS_FULL = "full";
|
||||
static final String CONFIGURATION_CLASS_FULL = "full";
|
||||
|
||||
public static final String CONFIGURATION_CLASS_LITE = "lite";
|
||||
static final String CONFIGURATION_CLASS_LITE = "lite";
|
||||
|
||||
public static final String CONFIGURATION_CLASS_ATTRIBUTE =
|
||||
static final String CONFIGURATION_CLASS_ATTRIBUTE =
|
||||
Conventions.getQualifiedAttributeName(ConfigurationClassPostProcessor.class, "configurationClass");
|
||||
|
||||
private static final String ORDER_ATTRIBUTE =
|
||||
static final String ORDER_ATTRIBUTE =
|
||||
Conventions.getQualifiedAttributeName(ConfigurationClassPostProcessor.class, "order");
|
||||
|
||||
|
||||
@@ -73,6 +75,17 @@ abstract class ConfigurationClassUtils {
|
||||
candidateIndicators.add(ImportResource.class.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a configuration class proxy for the specified class.
|
||||
* @param userClass the configuration class to initialize
|
||||
*/
|
||||
@SuppressWarnings("unused") // Used by AOT-optimized generated code
|
||||
public static Class<?> initializeConfigurationClass(Class<?> userClass) {
|
||||
Class<?> configurationClass = new ConfigurationClassEnhancer().enhance(userClass, null);
|
||||
Enhancer.registerStaticCallbacks(configurationClass, ConfigurationClassEnhancer.CALLBACKS);
|
||||
return configurationClass;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check whether the given bean definition is a candidate for a configuration class
|
||||
@@ -82,7 +95,7 @@ abstract class ConfigurationClassUtils {
|
||||
* @param metadataReaderFactory the current factory in use by the caller
|
||||
* @return whether the candidate qualifies as (any kind of) configuration class
|
||||
*/
|
||||
public static boolean checkConfigurationClassCandidate(
|
||||
static boolean checkConfigurationClassCandidate(
|
||||
BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
|
||||
|
||||
String className = beanDef.getBeanClassName();
|
||||
@@ -149,7 +162,7 @@ abstract class ConfigurationClassUtils {
|
||||
* @return {@code true} if the given class is to be registered for
|
||||
* configuration class processing; {@code false} otherwise
|
||||
*/
|
||||
public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
|
||||
static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
|
||||
// Do not consider an interface or an annotation...
|
||||
if (metadata.isInterface()) {
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user