Merge branch '6.2.x'

# Conflicts:
#	spring-context/src/main/java/org/springframework/context/aot/BeanFactoryInitializationAotContributions.java
#	spring-context/src/test/java/org/springframework/context/aot/ApplicationContextAotGeneratorTests.java
#	spring-orm/src/main/java/org/springframework/orm/hibernate5/HibernateQueryException.java
This commit is contained in:
Juergen Hoeller 2025-06-24 22:01:58 +02:00
commit 90c875144a
14 changed files with 334 additions and 145 deletions

View File

@ -53,6 +53,9 @@ import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueH
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.AutowireCandidateQualifier;
import org.springframework.beans.factory.support.InstanceSupplier;
import org.springframework.beans.factory.support.LookupOverride;
import org.springframework.beans.factory.support.MethodOverride;
import org.springframework.beans.factory.support.ReplaceOverride;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.javapoet.CodeBlock;
import org.springframework.javapoet.CodeBlock.Builder;
@ -145,6 +148,7 @@ class BeanDefinitionPropertiesCodeGenerator {
addPropertyValues(code, beanDefinition);
addAttributes(code, beanDefinition);
addQualifiers(code, beanDefinition);
addMethodOverrides(code, beanDefinition);
return code.build();
}
@ -274,6 +278,36 @@ class BeanDefinitionPropertiesCodeGenerator {
}
}
private void addMethodOverrides(CodeBlock.Builder code, RootBeanDefinition beanDefinition) {
if (beanDefinition.hasMethodOverrides()) {
for (MethodOverride methodOverride : beanDefinition.getMethodOverrides().getOverrides()) {
if (methodOverride instanceof LookupOverride lookupOverride) {
Collection<CodeBlock> arguments = new ArrayList<>();
arguments.add(CodeBlock.of("$S", lookupOverride.getMethodName()));
arguments.add(CodeBlock.of("$S", lookupOverride.getBeanName()));
code.addStatement("$L.getMethodOverrides().addOverride(new $T($L))", BEAN_DEFINITION_VARIABLE,
LookupOverride.class, CodeBlock.join(arguments, ", "));
}
else if (methodOverride instanceof ReplaceOverride replaceOverride) {
Collection<CodeBlock> arguments = new ArrayList<>();
arguments.add(CodeBlock.of("$S", replaceOverride.getMethodName()));
arguments.add(CodeBlock.of("$S", replaceOverride.getMethodReplacerBeanName()));
List<String> typeIdentifiers = replaceOverride.getTypeIdentifiers();
if (!typeIdentifiers.isEmpty()) {
arguments.add(CodeBlock.of("java.util.List.of($S)",
StringUtils.collectionToDelimitedString(typeIdentifiers, ", ")));
}
code.addStatement("$L.getMethodOverrides().addOverride(new $T($L))", BEAN_DEFINITION_VARIABLE,
ReplaceOverride.class, CodeBlock.join(arguments, ", "));
}
else {
throw new UnsupportedOperationException("Unexpected MethodOverride subclass: " +
methodOverride.getClass().getName());
}
}
}
}
private CodeBlock generateValue(@Nullable String name, @Nullable Object value) {
PropertyNamesStack.push(name);
try {

View File

@ -40,6 +40,7 @@ import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueH
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionValueResolver;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.InstanceSupplier;
import org.springframework.beans.factory.support.RegisteredBean;
import org.springframework.beans.factory.support.RootBeanDefinition;
@ -343,6 +344,11 @@ public final class BeanInstanceSupplier<T> extends AutowiredElementResolver impl
private Object instantiate(RegisteredBean registeredBean, Executable executable, @Nullable Object[] args) {
if (executable instanceof Constructor<?> constructor) {
if (registeredBean.getBeanFactory() instanceof DefaultListableBeanFactory dlbf &&
registeredBean.getMergedBeanDefinition().hasMethodOverrides()) {
return dlbf.getInstantiationStrategy().instantiate(registeredBean.getMergedBeanDefinition(),
registeredBean.getBeanName(), registeredBean.getBeanFactory());
}
return BeanUtils.instantiateClass(constructor, args);
}
if (executable instanceof Method method) {

View File

@ -41,8 +41,7 @@ public interface BeanRegistrationAotContribution {
* default code generation isn't suitable.
* @param generationContext the generation context
* @param codeFragments the existing code fragments
* @return the code fragments to use, may be the original instance or a
* wrapper
* @return the code fragments to use, may be the original instance or a wrapper
*/
default BeanRegistrationCodeFragments customizeBeanRegistrationCodeFragments(
GenerationContext generationContext, BeanRegistrationCodeFragments codeFragments) {
@ -78,8 +77,7 @@ public interface BeanRegistrationAotContribution {
return defaultCodeFragments.apply(codeFragments);
}
@Override
public void applyTo(GenerationContext generationContext,
BeanRegistrationCode beanRegistrationCode) {
public void applyTo(GenerationContext generationContext, BeanRegistrationCode beanRegistrationCode) {
}
};
}

View File

@ -31,14 +31,15 @@ import org.springframework.javapoet.CodeBlock;
/**
* Generate the various fragments of code needed to register a bean.
* <p>
* A default implementation is provided that suits most needs and custom code
*
* <p>A default implementation is provided that suits most needs and custom code
* fragments are only expected to be used by library authors having built custom
* arrangement on top of the core container.
* <p>
* Users are not expected to implement this interface directly, but rather extends
* from {@link BeanRegistrationCodeFragmentsDecorator} and only override the
* necessary method(s).
*
* <p>Users are not expected to implement this interface directly, but rather
* extends from {@link BeanRegistrationCodeFragmentsDecorator} and only override
* the necessary method(s).
*
* @author Phillip Webb
* @author Stephane Nicoll
* @since 6.0
@ -48,12 +49,12 @@ import org.springframework.javapoet.CodeBlock;
public interface BeanRegistrationCodeFragments {
/**
* The variable name to used when creating the bean definition.
* The variable name used when creating the bean definition.
*/
String BEAN_DEFINITION_VARIABLE = "beanDefinition";
/**
* The variable name to used when creating the bean definition.
* The variable name used when creating the bean definition.
*/
String INSTANCE_SUPPLIER_VARIABLE = "instanceSupplier";
@ -69,8 +70,7 @@ public interface BeanRegistrationCodeFragments {
/**
* Generate the code that defines the new bean definition instance.
* <p>
* This should declare a variable named {@value BEAN_DEFINITION_VARIABLE}
* <p>This should declare a variable named {@value BEAN_DEFINITION_VARIABLE}
* so that further fragments can refer to the variable to further tune
* the bean definition.
* @param generationContext the generation context
@ -94,14 +94,13 @@ public interface BeanRegistrationCodeFragments {
/**
* Generate the code that sets the instance supplier on the bean definition.
* <p>
* The {@code postProcessors} represent methods to be exposed once the
* <p>The {@code postProcessors} represent methods to be exposed once the
* instance has been created to further configure it. Each method should
* accept two parameters, the {@link RegisteredBean} and the bean
* instance, and should return the modified bean instance.
* @param generationContext the generation context
* @param beanRegistrationCode the bean registration code
* @param instanceSupplierCode the instance supplier code supplier code
* @param instanceSupplierCode the instance supplier code
* @param postProcessors any instance post processors that should be applied
* @return the generated code
* @see #generateInstanceSupplierCode

View File

@ -58,6 +58,7 @@ class BeanRegistrationCodeGenerator implements BeanRegistrationCode {
this.codeFragments = codeFragments;
}
@Override
public ClassName getClassName() {
return this.className;

View File

@ -245,7 +245,7 @@ class DefaultBeanRegistrationCodeFragments implements BeanRegistrationCodeFragme
}
private boolean hasInstanceSupplier() {
return this.registeredBean.getMergedBeanDefinition().getInstanceSupplier() != null;
return (this.registeredBean.getMergedBeanDefinition().getInstanceSupplier() != null);
}
}

View File

@ -115,6 +115,7 @@ public class InstanceSupplierCodeGenerator {
this.allowDirectSupplierShortcut = allowDirectSupplierShortcut;
}
/**
* Generate the instance supplier code.
* @param registeredBean the bean to handle
@ -165,7 +166,8 @@ public class InstanceSupplierCodeGenerator {
hints -> hints.registerType(publicType, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS));
}
if (!isVisible(constructor, constructor.getDeclaringClass())) {
if (!isVisible(constructor, constructor.getDeclaringClass()) ||
registeredBean.getMergedBeanDefinition().hasMethodOverrides()) {
return generateCodeForInaccessibleConstructor(descriptor,
hints -> hints.registerConstructor(constructor, ExecutableMode.INVOKE));
}

View File

@ -18,6 +18,7 @@ package org.springframework.beans.factory.support;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
@ -54,6 +55,20 @@ public class ReplaceOverride extends MethodOverride {
this.methodReplacerBeanName = methodReplacerBeanName;
}
/**
* Construct a new ReplaceOverride.
* @param methodName the name of the method to override
* @param methodReplacerBeanName the bean name of the {@link MethodReplacer}
* @param typeIdentifiers a list of type identifiers for parameter types
* @since 6.2.9
*/
public ReplaceOverride(String methodName, String methodReplacerBeanName, List<String> typeIdentifiers) {
super(methodName);
Assert.notNull(methodReplacerBeanName, "Method replacer bean name must not be null");
this.methodReplacerBeanName = methodReplacerBeanName;
this.typeIdentifiers.addAll(typeIdentifiers);
}
/**
* Return the name of the bean implementing MethodReplacer.
@ -71,6 +86,15 @@ public class ReplaceOverride extends MethodOverride {
this.typeIdentifiers.add(identifier);
}
/**
* Return the list of registered type identifiers (fragments of a class string).
* @since 6.2.9
* @see #addTypeIdentifier
*/
public List<String> getTypeIdentifiers() {
return Collections.unmodifiableList(this.typeIdentifiers);
}
@Override
public boolean matches(Method method) {

View File

@ -32,9 +32,8 @@ import org.springframework.beans.factory.aot.BeanFactoryInitializationCode;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
/**
* A collection of {@link BeanFactoryInitializationAotContribution AOT
* contributions} obtained from {@link BeanFactoryInitializationAotProcessor AOT
* processors}.
* A collection of {@link BeanFactoryInitializationAotContribution AOT contributions}
* obtained from {@link BeanFactoryInitializationAotProcessor AOT processors}.
*
* @author Phillip Webb
* @since 6.0
@ -48,14 +47,12 @@ class BeanFactoryInitializationAotContributions {
this(beanFactory, AotServices.factoriesAndBeans(beanFactory));
}
BeanFactoryInitializationAotContributions(DefaultListableBeanFactory beanFactory,
AotServices.Loader loader) {
BeanFactoryInitializationAotContributions(DefaultListableBeanFactory beanFactory, AotServices.Loader loader) {
this.contributions = getContributions(beanFactory, getProcessors(loader));
}
private static List<BeanFactoryInitializationAotProcessor> getProcessors(
AotServices.Loader loader) {
private static List<BeanFactoryInitializationAotProcessor> getProcessors(AotServices.Loader loader) {
List<BeanFactoryInitializationAotProcessor> processors = new ArrayList<>(
loader.load(BeanFactoryInitializationAotProcessor.class).asList());
processors.add(new RuntimeHintsBeanFactoryInitializationAotProcessor());
@ -63,8 +60,8 @@ class BeanFactoryInitializationAotContributions {
}
private List<BeanFactoryInitializationAotContribution> getContributions(
DefaultListableBeanFactory beanFactory,
List<BeanFactoryInitializationAotProcessor> processors) {
DefaultListableBeanFactory beanFactory, List<BeanFactoryInitializationAotProcessor> processors) {
List<BeanFactoryInitializationAotContribution> contributions = new ArrayList<>();
for (BeanFactoryInitializationAotProcessor processor : processors) {
BeanFactoryInitializationAotContribution contribution = processAheadOfTime(processor, beanFactory);
@ -75,8 +72,8 @@ class BeanFactoryInitializationAotContributions {
return Collections.unmodifiableList(contributions);
}
private @Nullable BeanFactoryInitializationAotContribution processAheadOfTime(BeanFactoryInitializationAotProcessor processor,
DefaultListableBeanFactory beanFactory) {
private @Nullable BeanFactoryInitializationAotContribution processAheadOfTime(
BeanFactoryInitializationAotProcessor processor, DefaultListableBeanFactory beanFactory) {
try {
return processor.processAheadOfTime(beanFactory);
@ -92,6 +89,7 @@ class BeanFactoryInitializationAotContributions {
void applyTo(GenerationContext generationContext,
BeanFactoryInitializationCode beanFactoryInitializationCode) {
for (BeanFactoryInitializationAotContribution contribution : this.contributions) {
contribution.applyTo(generationContext, beanFactoryInitializationCode);
}

View File

@ -17,6 +17,7 @@
package org.springframework.context.aot;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;
import java.util.function.BiConsumer;
@ -50,7 +51,9 @@ import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.MethodReplacer;
import org.springframework.beans.factory.support.RegisteredBean;
import org.springframework.beans.factory.support.ReplaceOverride;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.testfixture.beans.Employee;
import org.springframework.beans.testfixture.beans.Pet;
@ -80,6 +83,7 @@ import org.springframework.context.testfixture.context.annotation.LazyConstructo
import org.springframework.context.testfixture.context.annotation.LazyFactoryMethodArgumentComponent;
import org.springframework.context.testfixture.context.annotation.LazyResourceFieldComponent;
import org.springframework.context.testfixture.context.annotation.LazyResourceMethodComponent;
import org.springframework.context.testfixture.context.annotation.LookupComponent;
import org.springframework.context.testfixture.context.annotation.PropertySourceConfiguration;
import org.springframework.context.testfixture.context.annotation.QualifierConfiguration;
import org.springframework.context.testfixture.context.annotation.ResourceComponent;
@ -111,6 +115,7 @@ class ApplicationContextAotGeneratorTests {
void processAheadOfTimeWhenHasSimpleBean() {
GenericApplicationContext applicationContext = new GenericApplicationContext();
applicationContext.registerBeanDefinition("test", new RootBeanDefinition(SimpleComponent.class));
testCompiledResult(applicationContext, (initializer, compiled) -> {
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
assertThat(freshApplicationContext.getBeanDefinitionNames()).containsOnly("test");
@ -118,6 +123,99 @@ class ApplicationContextAotGeneratorTests {
});
}
@Test
void processAheadOfTimeWhenHasNoAotContributions() {
GenericApplicationContext applicationContext = new GenericApplicationContext();
testCompiledResult(applicationContext, (initializer, compiled) -> {
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
assertThat(freshApplicationContext.getBeanDefinitionNames()).isEmpty();
assertThat(compiled.getSourceFile())
.contains("beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver())")
.contains("beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE)");
});
}
@Test
void processAheadOfTimeWhenHasBeanFactoryInitializationAotProcessorExcludesProcessor() {
GenericApplicationContext applicationContext = new GenericApplicationContext();
applicationContext.registerBeanDefinition("test",
new RootBeanDefinition(NoOpBeanFactoryInitializationAotProcessor.class));
testCompiledResult(applicationContext, (initializer, compiled) -> {
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
assertThat(freshApplicationContext.getBeanDefinitionNames()).isEmpty();
});
}
@Test
void processAheadOfTimeWhenHasBeanRegistrationAotProcessorExcludesProcessor() {
GenericApplicationContext applicationContext = new GenericApplicationContext();
applicationContext.registerBeanDefinition("test",
new RootBeanDefinition(NoOpBeanRegistrationAotProcessor.class));
testCompiledResult(applicationContext, (initializer, compiled) -> {
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
assertThat(freshApplicationContext.getBeanDefinitionNames()).isEmpty();
});
}
@Test
void processAheadOfTimeWithPropertySource() {
GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.registerBean(PropertySourceConfiguration.class);
testCompiledResult(applicationContext, (initializer, compiled) -> {
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
ConfigurableEnvironment environment = freshApplicationContext.getEnvironment();
PropertySource<?> propertySource = environment.getPropertySources().get("testp1");
assertThat(propertySource).isNotNull();
assertThat(propertySource.getProperty("from.p1")).isEqualTo("p1Value");
});
}
@Test
void processAheadOfTimeWithQualifier() {
GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.registerBean(QualifierConfiguration.class);
testCompiledResult(applicationContext, (initializer, compiled) -> {
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
QualifierConfiguration configuration = freshApplicationContext.getBean(QualifierConfiguration.class);
assertThat(configuration).hasFieldOrPropertyWithValue("bean", "one");
});
}
@Test
void processAheadOfTimeWithInjectionPoint() {
GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.registerBean(InjectionPointConfiguration.class);
testCompiledResult(applicationContext, (initializer, compiled) -> {
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
assertThat(freshApplicationContext.getBean("classToString"))
.isEqualTo(InjectionPointConfiguration.class.getName());
});
}
@Test // gh-30689
void processAheadOfTimeWithExplicitResolvableType() {
GenericApplicationContext applicationContext = new GenericApplicationContext();
DefaultListableBeanFactory beanFactory = applicationContext.getDefaultListableBeanFactory();
RootBeanDefinition beanDefinition = new RootBeanDefinition(One.class);
beanDefinition.setResolvedFactoryMethod(ReflectionUtils.findMethod(TestHierarchy.class, "oneBean"));
// Override target type
beanDefinition.setTargetType(Two.class);
beanFactory.registerBeanDefinition("hierarchyBean", beanDefinition);
testCompiledResult(applicationContext, (initializer, compiled) -> {
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
assertThat(freshApplicationContext.getBean(Two.class))
.isInstanceOf(Implementation.class);
});
}
@Nested
class Autowiring {
@ -128,6 +226,7 @@ class ApplicationContextAotGeneratorTests {
AnnotationConfigUtils.AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME, AutowiredAnnotationBeanPostProcessor.class);
applicationContext.registerBeanDefinition("autowiredComponent", new RootBeanDefinition(AutowiredComponent.class));
registerIntegerBean(applicationContext, "number", 42);
testCompiledResult(applicationContext, (initializer, compiled) -> {
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
assertThat(freshApplicationContext.getBeanDefinitionNames()).containsOnly("autowiredComponent", "number");
@ -137,11 +236,56 @@ class ApplicationContextAotGeneratorTests {
});
}
@Test
void processAheadOfTimeWhenHasReplacer() {
GenericApplicationContext applicationContext = new GenericApplicationContext();
registerBeanPostProcessor(applicationContext,
AnnotationConfigUtils.AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME, AutowiredAnnotationBeanPostProcessor.class);
RootBeanDefinition rbd = new RootBeanDefinition(AutowiredComponent.class);
rbd.getMethodOverrides().addOverride(
new ReplaceOverride("getCounter", "replacer"));
applicationContext.registerBeanDefinition("autowiredComponent", rbd);
registerIntegerBean(applicationContext, "number", 42);
applicationContext.registerBean("replacer", DummyReplacer.class);
testCompiledResult(applicationContext, (initializer, compiled) -> {
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
assertThat(freshApplicationContext.getBeanDefinitionNames()).containsOnly("autowiredComponent", "number", "replacer");
AutowiredComponent bean = freshApplicationContext.getBean(AutowiredComponent.class);
assertThat(bean.getEnvironment()).isSameAs(freshApplicationContext.getEnvironment());
assertThat(bean.getCounter()).isEqualTo(44);
assertThat(bean.getCounter(0)).isEqualTo(42);
});
}
@Test
void processAheadOfTimeWhenHasLookup() {
GenericApplicationContext applicationContext = new GenericApplicationContext();
registerBeanPostProcessor(applicationContext,
AnnotationConfigUtils.AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME, AutowiredAnnotationBeanPostProcessor.class);
RootBeanDefinition rbd = new RootBeanDefinition(LookupComponent.class);
rbd.getMethodOverrides().addOverride(
new ReplaceOverride("getCounter", "replacer", List.of( "Integer")));
applicationContext.registerBeanDefinition("autowiredComponent", rbd);
registerIntegerBean(applicationContext, "number", 42);
applicationContext.registerBean("replacer", DummyReplacer.class);
testCompiledResult(applicationContext, (initializer, compiled) -> {
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
assertThat(freshApplicationContext.getBeanDefinitionNames()).containsOnly("autowiredComponent", "number", "replacer");
LookupComponent bean = freshApplicationContext.getBean(LookupComponent.class);
assertThat(bean.getEnvironment()).isSameAs(freshApplicationContext.getEnvironment());
assertThat(bean.getCounter()).isEqualTo(42);
assertThat(bean.getCounter(0)).isEqualTo(44);
});
}
@Test
void processAheadOfTimeWhenHasAutowiringOnUnresolvedGeneric() {
GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.registerBean(GenericTemplateConfiguration.class);
applicationContext.registerBean("autowiredComponent", AutowiredGenericTemplate.class);
testCompiledResult(applicationContext, (initializer, compiled) -> {
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
AutowiredGenericTemplate bean = freshApplicationContext.getBean(AutowiredGenericTemplate.class);
@ -161,7 +305,6 @@ class ApplicationContextAotGeneratorTests {
assertThat(runtimeHints.proxies().jdkProxyHints()).anySatisfy(proxyHint ->
assertThat(proxyHint.getProxiedInterfaces()).isEqualTo(TypeReference.listOf(
environment.getClass().getInterfaces())));
});
}
@ -225,15 +368,16 @@ class ApplicationContextAotGeneratorTests {
AnnotationConfigUtils.AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME, AutowiredAnnotationBeanPostProcessor.class);
applicationContext.registerBeanDefinition("testComponent", beanDefinition);
TestGenerationContext generationContext = processAheadOfTime(applicationContext);
testCompiledResult(generationContext, (initializer, compiled) -> {
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
assertThat(freshApplicationContext.getBeanDefinitionNames()).containsOnly("testComponent");
assertions.accept(freshApplicationContext.getBean("testComponent", type), generationContext);
});
}
}
@Nested
class ResourceAutowiring {
@ -246,6 +390,7 @@ class ApplicationContextAotGeneratorTests {
registerStringBean(applicationContext, "text2", "hello2");
registerIntegerBean(applicationContext, "number", 42);
applicationContext.registerBeanDefinition("resourceComponent", new RootBeanDefinition(ResourceComponent.class));
testCompiledResult(applicationContext, (initializer, compiled) -> {
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
assertThat(freshApplicationContext.getBeanDefinitionNames()).containsOnly("resourceComponent", "text", "text2", "number");
@ -299,6 +444,7 @@ class ApplicationContextAotGeneratorTests {
AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME, CommonAnnotationBeanPostProcessor.class);
applicationContext.registerBeanDefinition("testComponent", beanDefinition);
TestGenerationContext generationContext = processAheadOfTime(applicationContext);
testCompiledResult(generationContext, (initializer, compiled) -> {
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
assertThat(freshApplicationContext.getBeanDefinitionNames()).containsOnly("testComponent");
@ -307,6 +453,7 @@ class ApplicationContextAotGeneratorTests {
}
}
@Nested
class InitDestroy {
@ -317,6 +464,7 @@ class ApplicationContextAotGeneratorTests {
AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME, CommonAnnotationBeanPostProcessor.class);
applicationContext.registerBeanDefinition("initDestroyComponent",
new RootBeanDefinition(InitDestroyComponent.class));
testCompiledResult(applicationContext, (initializer, compiled) -> {
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
assertThat(freshApplicationContext.getBeanDefinitionNames()).containsOnly("initDestroyComponent");
@ -336,6 +484,7 @@ class ApplicationContextAotGeneratorTests {
beanDefinition.setInitMethodName("customInit");
beanDefinition.setDestroyMethodName("customDestroy");
applicationContext.registerBeanDefinition("initDestroyComponent", beanDefinition);
testCompiledResult(applicationContext, (initializer, compiled) -> {
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
assertThat(freshApplicationContext.getBeanDefinitionNames()).containsOnly("initDestroyComponent");
@ -345,94 +494,8 @@ class ApplicationContextAotGeneratorTests {
assertThat(bean.events).containsExactly("init", "customInit", "destroy", "customDestroy");
});
}
}
@Test
void processAheadOfTimeWhenHasNoAotContributions() {
GenericApplicationContext applicationContext = new GenericApplicationContext();
testCompiledResult(applicationContext, (initializer, compiled) -> {
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
assertThat(freshApplicationContext.getBeanDefinitionNames()).isEmpty();
assertThat(compiled.getSourceFile())
.contains("beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver())")
.contains("beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE)");
});
}
@Test
void processAheadOfTimeWhenHasBeanFactoryInitializationAotProcessorExcludesProcessor() {
GenericApplicationContext applicationContext = new GenericApplicationContext();
applicationContext.registerBeanDefinition("test",
new RootBeanDefinition(NoOpBeanFactoryInitializationAotProcessor.class));
testCompiledResult(applicationContext, (initializer, compiled) -> {
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
assertThat(freshApplicationContext.getBeanDefinitionNames()).isEmpty();
});
}
@Test
void processAheadOfTimeWhenHasBeanRegistrationAotProcessorExcludesProcessor() {
GenericApplicationContext applicationContext = new GenericApplicationContext();
applicationContext.registerBeanDefinition("test",
new RootBeanDefinition(NoOpBeanRegistrationAotProcessor.class));
testCompiledResult(applicationContext, (initializer, compiled) -> {
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
assertThat(freshApplicationContext.getBeanDefinitionNames()).isEmpty();
});
}
@Test
void processAheadOfTimeWithPropertySource() {
GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.registerBean(PropertySourceConfiguration.class);
testCompiledResult(applicationContext, (initializer, compiled) -> {
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
ConfigurableEnvironment environment = freshApplicationContext.getEnvironment();
PropertySource<?> propertySource = environment.getPropertySources().get("testp1");
assertThat(propertySource).isNotNull();
assertThat(propertySource.getProperty("from.p1")).isEqualTo("p1Value");
});
}
@Test
void processAheadOfTimeWithQualifier() {
GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.registerBean(QualifierConfiguration.class);
testCompiledResult(applicationContext, (initializer, compiled) -> {
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
QualifierConfiguration configuration = freshApplicationContext.getBean(QualifierConfiguration.class);
assertThat(configuration).hasFieldOrPropertyWithValue("bean", "one");
});
}
@Test
void processAheadOfTimeWithInjectionPoint() {
GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.registerBean(InjectionPointConfiguration.class);
testCompiledResult(applicationContext, (initializer, compiled) -> {
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
assertThat(freshApplicationContext.getBean("classToString"))
.isEqualTo(InjectionPointConfiguration.class.getName());
});
}
@Test // gh-30689
void processAheadOfTimeWithExplicitResolvableType() {
GenericApplicationContext applicationContext = new GenericApplicationContext();
DefaultListableBeanFactory beanFactory = applicationContext.getDefaultListableBeanFactory();
RootBeanDefinition beanDefinition = new RootBeanDefinition(One.class);
beanDefinition.setResolvedFactoryMethod(ReflectionUtils.findMethod(TestHierarchy.class, "oneBean"));
// Override target type
beanDefinition.setTargetType(Two.class);
beanFactory.registerBeanDefinition("hierarchyBean", beanDefinition);
testCompiledResult(applicationContext, (initializer, compiled) -> {
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
assertThat(freshApplicationContext.getBean(Two.class))
.isInstanceOf(Implementation.class);
});
}
@Nested
@CompileWithForkedClassLoader
@ -498,6 +561,7 @@ class ApplicationContextAotGeneratorTests {
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");
@ -509,6 +573,7 @@ class ApplicationContextAotGeneratorTests {
void processAheadOfTimeWhenHasCglibProxyAndAutowiring() {
GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.registerBean(AutowiredCglibConfiguration.class);
testCompiledResult(applicationContext, (initializer, compiled) -> {
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(context -> {
context.setEnvironment(new MockEnvironment().withProperty("hello", "Hi"));
@ -522,6 +587,7 @@ class ApplicationContextAotGeneratorTests {
void processAheadOfTimeWhenHasCglibProxyAndMixedAutowiring() {
GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.registerBean(AutowiredMixedCglibConfiguration.class);
testCompiledResult(applicationContext, (initializer, compiled) -> {
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(context -> {
context.setEnvironment(new MockEnvironment().withProperty("hello", "Hi")
@ -536,6 +602,7 @@ class ApplicationContextAotGeneratorTests {
void processAheadOfTimeWhenHasCglibProxyWithAnnotationsOnTheUserClasConstructor() {
GenericApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.registerBean("config", ValueCglibConfiguration.class);
testCompiledResult(applicationContext, (initializer, compiled) -> {
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(context -> {
context.setEnvironment(new MockEnvironment().withProperty("name", "AOT World"));
@ -550,6 +617,7 @@ class ApplicationContextAotGeneratorTests {
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"));
@ -571,9 +639,9 @@ class ApplicationContextAotGeneratorTests {
private String toCglibClassSimpleName(Class<?> configClass) {
return configClass.getSimpleName() + CGLIB_CONFIGURATION_CLASS_SUFFIX;
}
}
@Nested
class ActiveProfile {
@ -584,6 +652,7 @@ class ApplicationContextAotGeneratorTests {
if (aotProfiles.length != 0) {
applicationContext.getEnvironment().setActiveProfiles(aotProfiles);
}
testCompiledResult(applicationContext, (initializer, compiled) -> {
GenericApplicationContext freshApplicationContext = new GenericApplicationContext();
if (runtimeProfiles.length != 0) {
@ -602,17 +671,18 @@ class ApplicationContextAotGeneratorTests {
Arguments.of(new String[] { "aot", "prod" }, new String[] { "aot", "prod" }, new String[] { "aot", "prod" }),
Arguments.of(new String[] { "default" }, new String[] {}, new String[] {}));
}
}
@Nested
class XmlSupport {
@Test
void processAheadOfTimeWhenHasTypedStringValue() {
GenericXmlApplicationContext applicationContext = new GenericXmlApplicationContext();
applicationContext
.load(new ClassPathResource("applicationContextAotGeneratorTests-values.xml", getClass()));
applicationContext.load(
new ClassPathResource("applicationContextAotGeneratorTests-values.xml", getClass()));
testCompiledResult(applicationContext, (initializer, compiled) -> {
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
Employee employee = freshApplicationContext.getBean(Employee.class);
@ -629,8 +699,9 @@ class ApplicationContextAotGeneratorTests {
@Test
void processAheadOfTimeWhenHasTypedStringValueWithType() {
GenericXmlApplicationContext applicationContext = new GenericXmlApplicationContext();
applicationContext
.load(new ClassPathResource("applicationContextAotGeneratorTests-values-types.xml", getClass()));
applicationContext.load(
new ClassPathResource("applicationContextAotGeneratorTests-values-types.xml", getClass()));
testCompiledResult(applicationContext, (initializer, compiled) -> {
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
Employee employee = freshApplicationContext.getBean(Employee.class);
@ -645,8 +716,9 @@ class ApplicationContextAotGeneratorTests {
@Test
void processAheadOfTimeWhenHasTypedStringValueWithExpression() {
GenericXmlApplicationContext applicationContext = new GenericXmlApplicationContext();
applicationContext
.load(new ClassPathResource("applicationContextAotGeneratorTests-values-expressions.xml", getClass()));
applicationContext.load(
new ClassPathResource("applicationContextAotGeneratorTests-values-expressions.xml", getClass()));
testCompiledResult(applicationContext, (initializer, compiled) -> {
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
Employee employee = freshApplicationContext.getBean(Employee.class);
@ -661,8 +733,9 @@ class ApplicationContextAotGeneratorTests {
@Test
void processAheadOfTimeWhenXmlHasBeanReferences() {
GenericXmlApplicationContext applicationContext = new GenericXmlApplicationContext();
applicationContext
.load(new ClassPathResource("applicationContextAotGeneratorTests-references.xml", getClass()));
applicationContext.load(
new ClassPathResource("applicationContextAotGeneratorTests-references.xml", getClass()));
testCompiledResult(applicationContext, (initializer, compiled) -> {
GenericApplicationContext freshApplicationContext = toFreshApplicationContext(initializer);
assertThat(freshApplicationContext.getBean("petInnerBean", Pet.class)
@ -671,11 +744,11 @@ class ApplicationContextAotGeneratorTests {
.getName()).isEqualTo("Dofi");
});
}
}
@Nested
class ExceptionHanding {
class ExceptionHandling {
@Test
void failureProcessingBeanFactoryAotContribution() {
@ -690,6 +763,7 @@ class ApplicationContextAotGeneratorTests {
}
}
private static void registerBeanPostProcessor(GenericApplicationContext applicationContext,
String beanName, Class<?> beanPostProcessorClass) {
@ -714,7 +788,7 @@ class ApplicationContextAotGeneratorTests {
.getBeanDefinition());
}
private Consumer<List<? extends JdkProxyHint>> doesNotHaveProxyFor(Class<?> target) {
private static Consumer<List<? extends JdkProxyHint>> doesNotHaveProxyFor(Class<?> target) {
return hints -> assertThat(hints).noneMatch(hint ->
hint.getProxiedInterfaces().get(0).equals(TypeReference.of(target)));
}
@ -769,7 +843,6 @@ class ApplicationContextAotGeneratorTests {
public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) {
return null;
}
}
@ -780,9 +853,9 @@ class ApplicationContextAotGeneratorTests {
public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) {
return null;
}
}
static class FailingBeanFactoryInitializationAotContribution implements BeanFactoryInitializationAotProcessor {
@Override
@ -791,4 +864,13 @@ class ApplicationContextAotGeneratorTests {
}
}
public static class DummyReplacer implements MethodReplacer {
@Override
public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {
return 44;
}
}
}

View File

@ -25,17 +25,13 @@ public class AutowiredComponent {
private Integer counter;
public Environment getEnvironment() {
return this.environment;
}
@Autowired
public void setEnvironment(Environment environment) {
this.environment = environment;
}
public Integer getCounter() {
return this.counter;
public Environment getEnvironment() {
return this.environment;
}
@Autowired
@ -43,4 +39,12 @@ public class AutowiredComponent {
this.counter = counter;
}
public Integer getCounter() {
return this.counter;
}
public Integer getCounter(Integer ignored) {
return this.counter;
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright 2002-present 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.annotation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Lookup;
import org.springframework.core.env.Environment;
public abstract class LookupComponent {
private Environment environment;
@Autowired
public void setEnvironment(Environment environment) {
this.environment = environment;
}
public Environment getEnvironment() {
return this.environment;
}
@Lookup
public abstract Integer getCounter();
public Integer getCounter(Integer ignored) {
return 0;
}
}

View File

@ -41,7 +41,7 @@ public class HibernateQueryException extends InvalidDataAccessResourceUsageExcep
*/
public @Nullable String getQueryString() {
QueryException cause = (QueryException) getCause();
return cause == null ? null : cause.getQueryString();
return (cause != null ? cause.getQueryString() : null);
}
}

View File

@ -367,13 +367,11 @@ public class LocalSessionFactoryBuilder extends Configuration {
* the current class descriptor contained in the metadata reader.
*/
private boolean matchesEntityTypeFilter(MetadataReader reader, MetadataReaderFactory readerFactory) throws IOException {
if (this.entityTypeFilters != null) {
for (TypeFilter filter : this.entityTypeFilters) {
if (filter.match(reader, readerFactory)) {
return true;
}
}
}
return false;
}