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:
parent
f2e9d112b1
commit
2f20d6322b
|
|
@ -43,6 +43,7 @@ import org.springframework.beans.factory.support.BeanDefinitionValueResolver;
|
|||
import org.springframework.beans.factory.support.InstanceSupplier;
|
||||
import org.springframework.beans.factory.support.RegisteredBean;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.support.SimpleInstantiationStrategy;
|
||||
import org.springframework.core.CollectionFactory;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
|
@ -207,9 +208,24 @@ public final class BeanInstanceSupplier<T> extends AutowiredElementResolver impl
|
|||
Executable executable = this.lookup.get(registeredBean);
|
||||
AutowiredArguments arguments = resolveArguments(registeredBean, executable);
|
||||
if (this.generator != null) {
|
||||
return this.generator.apply(registeredBean, arguments);
|
||||
return invokeBeanSupplier(executable, () ->
|
||||
this.generator.apply(registeredBean, arguments));
|
||||
}
|
||||
return invokeBeanSupplier(executable, () ->
|
||||
instantiate(registeredBean.getBeanFactory(), executable, arguments.toArray()));
|
||||
}
|
||||
|
||||
private T invokeBeanSupplier(Executable executable, ThrowingSupplier<T> beanSupplier) {
|
||||
if (!(executable instanceof Method)) {
|
||||
return beanSupplier.get();
|
||||
}
|
||||
try {
|
||||
SimpleInstantiationStrategy.setCurrentlyInvokedFactoryMethod((Method) executable);
|
||||
return beanSupplier.get();
|
||||
}
|
||||
finally {
|
||||
SimpleInstantiationStrategy.setCurrentlyInvokedFactoryMethod(null);
|
||||
}
|
||||
return instantiate(registeredBean.getBeanFactory(), executable, arguments.toArray());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
|
|
|||
|
|
@ -109,8 +109,7 @@ class InstanceSupplierCodeGenerator {
|
|||
|
||||
String beanName = registeredBean.getBeanName();
|
||||
Class<?> beanClass = registeredBean.getBeanClass();
|
||||
Class<?> declaringClass = ClassUtils
|
||||
.getUserClass(constructor.getDeclaringClass());
|
||||
Class<?> declaringClass = constructor.getDeclaringClass();
|
||||
boolean dependsOnBean = ClassUtils.isInnerClass(declaringClass);
|
||||
Visibility accessVisibility = getAccessVisibility(registeredBean, constructor);
|
||||
if (accessVisibility != Visibility.PRIVATE) {
|
||||
|
|
|
|||
|
|
@ -53,6 +53,14 @@ public class SimpleInstantiationStrategy implements InstantiationStrategy {
|
|||
return currentlyInvokedFactoryMethod.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the factory method currently being invoked or {@code null} to reset.
|
||||
* @param method the factory method currently being invoked or {@code null}
|
||||
*/
|
||||
public static void setCurrentlyInvokedFactoryMethod(@Nullable Method method) {
|
||||
currentlyInvokedFactoryMethod.set(method);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -36,14 +36,17 @@ import org.springframework.beans.factory.InitializingBean;
|
|||
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RegisteredBean;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.testfixture.beans.factory.aot.MockBeanFactoryInitializationCode;
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.SimpleConfiguration;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.context.testfixture.context.generator.SimpleComponent;
|
||||
import org.springframework.context.testfixture.context.generator.annotation.CglibConfiguration;
|
||||
import org.springframework.context.testfixture.context.generator.annotation.ImportAwareConfiguration;
|
||||
import org.springframework.context.testfixture.context.generator.annotation.ImportConfiguration;
|
||||
import org.springframework.context.testfixture.context.generator.annotation.SimpleConfiguration;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
|
|
@ -78,7 +81,7 @@ class ConfigurationClassPostProcessorAotContributionTests {
|
|||
|
||||
@Test
|
||||
void processAheadOfTimeWhenNoImportAwareConfigurationReturnsNull() {
|
||||
assertThat(getContribution(SimpleConfiguration.class)).isNull();
|
||||
assertThat(getContribution(SimpleComponent.class)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -323,6 +326,34 @@ class ConfigurationClassPostProcessorAotContributionTests {
|
|||
|
||||
}
|
||||
|
||||
@Nested
|
||||
class ConfigurationClassProxyTests {
|
||||
|
||||
private final DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
|
||||
private final ConfigurationClassPostProcessor processor = new ConfigurationClassPostProcessor();
|
||||
|
||||
@Test
|
||||
void processAheadOfTimeRegularConfigurationClass() {
|
||||
assertThat(this.processor.processAheadOfTime(
|
||||
getRegisteredBean(SimpleConfiguration.class))).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void processAheadOfTimeFullConfigurationClass() {
|
||||
assertThat(this.processor.processAheadOfTime(
|
||||
getRegisteredBean(CglibConfiguration.class))).isNotNull();
|
||||
}
|
||||
|
||||
|
||||
private RegisteredBean getRegisteredBean(Class<?> bean) {
|
||||
this.beanFactory.registerBeanDefinition("test", new RootBeanDefinition(bean));
|
||||
this.processor.postProcessBeanFactory(this.beanFactory);
|
||||
return RegisteredBean.of(this.beanFactory, "test");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private BeanFactoryInitializationAotContribution getContribution(Class<?>... types) {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import java.util.List;
|
|||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aot.generate.GeneratedFiles.Kind;
|
||||
|
|
@ -54,6 +55,7 @@ import org.springframework.context.support.GenericApplicationContext;
|
|||
import org.springframework.context.testfixture.context.generator.SimpleComponent;
|
||||
import org.springframework.context.testfixture.context.generator.annotation.AutowiredComponent;
|
||||
import org.springframework.context.testfixture.context.generator.annotation.CglibConfiguration;
|
||||
import org.springframework.context.testfixture.context.generator.annotation.ConfigurableCglibConfiguration;
|
||||
import org.springframework.context.testfixture.context.generator.annotation.InitDestroyComponent;
|
||||
import org.springframework.context.testfixture.context.generator.annotation.LazyAutowiredFieldComponent;
|
||||
import org.springframework.context.testfixture.context.generator.annotation.LazyAutowiredMethodComponent;
|
||||
|
|
@ -64,8 +66,10 @@ import org.springframework.core.env.ConfigurableEnvironment;
|
|||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.test.tools.CompileWithForkedClassLoader;
|
||||
import org.springframework.core.test.tools.Compiled;
|
||||
import org.springframework.core.test.tools.TestCompiler;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
|
|
@ -93,13 +97,13 @@ class ApplicationContextAotGeneratorTests {
|
|||
GenericApplicationContext applicationContext = new GenericApplicationContext();
|
||||
applicationContext.registerBeanDefinition(AnnotationConfigUtils.AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME,
|
||||
BeanDefinitionBuilder
|
||||
.rootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class)
|
||||
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE).getBeanDefinition());
|
||||
.rootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class)
|
||||
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE).getBeanDefinition());
|
||||
applicationContext.registerBeanDefinition("autowiredComponent", new RootBeanDefinition(AutowiredComponent.class));
|
||||
applicationContext.registerBeanDefinition("number",
|
||||
BeanDefinitionBuilder
|
||||
.rootBeanDefinition(Integer.class, "valueOf")
|
||||
.addConstructorArgValue("42").getBeanDefinition());
|
||||
.rootBeanDefinition(Integer.class, "valueOf")
|
||||
.addConstructorArgValue("42").getBeanDefinition());
|
||||
testCompiledResult(applicationContext, (initializer, compiled) -> {
|
||||
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
|
||||
assertThat(freshApplicationContext.getBeanDefinitionNames()).containsOnly("autowiredComponent", "number");
|
||||
|
|
@ -200,8 +204,8 @@ class ApplicationContextAotGeneratorTests {
|
|||
applicationContext.registerBeanDefinition(
|
||||
AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME,
|
||||
BeanDefinitionBuilder
|
||||
.rootBeanDefinition(CommonAnnotationBeanPostProcessor.class)
|
||||
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE).getBeanDefinition());
|
||||
.rootBeanDefinition(CommonAnnotationBeanPostProcessor.class)
|
||||
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE).getBeanDefinition());
|
||||
applicationContext.registerBeanDefinition("initDestroyComponent",
|
||||
new RootBeanDefinition(InitDestroyComponent.class));
|
||||
testCompiledResult(applicationContext, (initializer, compiled) -> {
|
||||
|
|
@ -220,8 +224,8 @@ class ApplicationContextAotGeneratorTests {
|
|||
applicationContext.registerBeanDefinition(
|
||||
AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME,
|
||||
BeanDefinitionBuilder
|
||||
.rootBeanDefinition(CommonAnnotationBeanPostProcessor.class)
|
||||
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE).getBeanDefinition());
|
||||
.rootBeanDefinition(CommonAnnotationBeanPostProcessor.class)
|
||||
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE).getBeanDefinition());
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(InitDestroyComponent.class);
|
||||
beanDefinition.setInitMethodName("customInit");
|
||||
beanDefinition.setDestroyMethodName("customDestroy");
|
||||
|
|
@ -270,17 +274,6 @@ class ApplicationContextAotGeneratorTests {
|
|||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void processAheadOfTimeWhenHasCglibProxyWriteProxyAndGenerateReflectionHints() throws IOException {
|
||||
GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext();
|
||||
applicationContext.registerBean(CglibConfiguration.class);
|
||||
TestGenerationContext context = processAheadOfTime(applicationContext);
|
||||
String proxyClassName = CglibConfiguration.class.getName() + "$$SpringCGLIB$$0";
|
||||
assertThat(context.getGeneratedFiles()
|
||||
.getGeneratedFileContent(Kind.CLASS, proxyClassName.replace('.', '/') + ".class")).isNotNull();
|
||||
assertThat(RuntimeHintsPredicates.reflection().onType(TypeReference.of(proxyClassName))
|
||||
.withMemberCategory(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)).accepts(context.getRuntimeHints());
|
||||
}
|
||||
|
||||
@Test
|
||||
void processAheadOfTimeWithPropertySource() {
|
||||
|
|
@ -295,8 +288,51 @@ class ApplicationContextAotGeneratorTests {
|
|||
});
|
||||
}
|
||||
|
||||
@Nested
|
||||
@CompileWithForkedClassLoader
|
||||
class ConfigurationClassCglibProxy {
|
||||
|
||||
@Test
|
||||
void processAheadOfTimeWhenHasCglibProxyWriteProxyAndGenerateReflectionHints() throws IOException {
|
||||
GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext();
|
||||
applicationContext.registerBean(CglibConfiguration.class);
|
||||
TestGenerationContext context = processAheadOfTime(applicationContext);
|
||||
String proxyClassName = CglibConfiguration.class.getName() + "$$SpringCGLIB$$0";
|
||||
assertThat(context.getGeneratedFiles()
|
||||
.getGeneratedFileContent(Kind.CLASS, proxyClassName.replace('.', '/') + ".class")).isNotNull();
|
||||
assertThat(RuntimeHintsPredicates.reflection().onType(TypeReference.of(proxyClassName))
|
||||
.withMemberCategory(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)).accepts(context.getRuntimeHints());
|
||||
}
|
||||
|
||||
@Test
|
||||
void processAheadOfTimeWhenHasCglibProxyUseProxy() {
|
||||
GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext();
|
||||
applicationContext.registerBean(CglibConfiguration.class);
|
||||
testCompiledResult(applicationContext, (initializer, compiled) -> {
|
||||
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
|
||||
assertThat(freshApplicationContext.getBean("prefix", String.class)).isEqualTo("Hello0");
|
||||
assertThat(freshApplicationContext.getBean("text", String.class)).isEqualTo("Hello0 World");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void processAheadOfTimeWhenHasCglibProxyWithArgumentsUseProxy() {
|
||||
GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext();
|
||||
applicationContext.registerBean(ConfigurableCglibConfiguration.class);
|
||||
testCompiledResult(applicationContext, (initializer, compiled) -> {
|
||||
GenericApplicationContext freshApplicationContext = createFreshApplicationContext(initializer);
|
||||
freshApplicationContext.setEnvironment(new MockEnvironment().withProperty("test.prefix", "Hi"));
|
||||
freshApplicationContext.refresh();
|
||||
assertThat(freshApplicationContext.getBean("prefix", String.class)).isEqualTo("Hi0");
|
||||
assertThat(freshApplicationContext.getBean("text", String.class)).isEqualTo("Hi0 World");
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Consumer<List<? extends JdkProxyHint>> doesNotHaveProxyFor(Class<?> target) {
|
||||
return hints -> assertThat(hints).noneMatch(hint -> hint.getProxiedInterfaces().get(0).equals(target));
|
||||
return hints -> assertThat(hints).noneMatch(hint ->
|
||||
hint.getProxiedInterfaces().get(0).equals(TypeReference.of(target)));
|
||||
}
|
||||
|
||||
private static TestGenerationContext processAheadOfTime(GenericApplicationContext applicationContext) {
|
||||
|
|
@ -307,23 +343,29 @@ class ApplicationContextAotGeneratorTests {
|
|||
return generationContext;
|
||||
}
|
||||
|
||||
private void testCompiledResult(GenericApplicationContext applicationContext,
|
||||
private static void testCompiledResult(GenericApplicationContext applicationContext,
|
||||
BiConsumer<ApplicationContextInitializer<GenericApplicationContext>, Compiled> result) {
|
||||
testCompiledResult(processAheadOfTime(applicationContext), result);
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private void testCompiledResult(TestGenerationContext generationContext,
|
||||
private static void testCompiledResult(TestGenerationContext generationContext,
|
||||
BiConsumer<ApplicationContextInitializer<GenericApplicationContext>, Compiled> result) {
|
||||
TestCompiler.forSystem().with(generationContext).compile(compiled ->
|
||||
result.accept(compiled.getInstance(ApplicationContextInitializer.class), compiled));
|
||||
}
|
||||
|
||||
private GenericApplicationContext toFreshApplicationContext(
|
||||
private static GenericApplicationContext toFreshApplicationContext(
|
||||
ApplicationContextInitializer<GenericApplicationContext> initializer) {
|
||||
GenericApplicationContext freshApplicationContext = createFreshApplicationContext(initializer);
|
||||
freshApplicationContext.refresh();
|
||||
return freshApplicationContext;
|
||||
}
|
||||
|
||||
private static GenericApplicationContext createFreshApplicationContext(
|
||||
ApplicationContextInitializer<GenericApplicationContext> initializer) {
|
||||
GenericApplicationContext freshApplicationContext = new GenericApplicationContext();
|
||||
initializer.initialize(freshApplicationContext);
|
||||
freshApplicationContext.refresh();
|
||||
return freshApplicationContext;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ public class CglibConfiguration {
|
|||
|
||||
@Bean
|
||||
public String prefix() {
|
||||
return "Hello" + counter.getAndIncrement();
|
||||
return getPrefix() + counter.getAndIncrement();
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
|
@ -36,4 +36,8 @@ public class CglibConfiguration {
|
|||
return prefix() + " World";
|
||||
}
|
||||
|
||||
protected String getPrefix() {
|
||||
return "Hello";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.context.testfixture.context.generator.annotation;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
@Configuration
|
||||
public class ConfigurableCglibConfiguration extends CglibConfiguration {
|
||||
|
||||
private final Environment environment;
|
||||
|
||||
public ConfigurableCglibConfiguration(Environment environment) {
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getPrefix() {
|
||||
return this.environment.getProperty("test.prefix", String.class, "Howdy");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -31,7 +31,7 @@ import org.springframework.transaction.PlatformTransactionManager;
|
|||
* @author Sam Brannen
|
||||
* @since 4.1
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Configuration
|
||||
public class EmptyDatabaseConfig {
|
||||
|
||||
@Bean
|
||||
|
|
|
|||
Loading…
Reference in New Issue