Align AOT contributions for injection with the runtime behavior
Bean post processors that use InjectionMetadata checks if a property value for the element it is about to inject is set and skip it, so that the property value is used. Previously, the AOT contribution for the same behavior did not check if a matching property value is set and therefore override the user-defined value. This commit introduces an additional method that filters the injected element list so that only the elements that should be processed are defined. Closes gh-30476
This commit is contained in:
parent
a9b94241af
commit
c3c5eaf914
|
@ -286,7 +286,8 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||||
String beanName = registeredBean.getBeanName();
|
String beanName = registeredBean.getBeanName();
|
||||||
RootBeanDefinition beanDefinition = registeredBean.getMergedBeanDefinition();
|
RootBeanDefinition beanDefinition = registeredBean.getMergedBeanDefinition();
|
||||||
InjectionMetadata metadata = findInjectionMetadata(beanName, beanClass, beanDefinition);
|
InjectionMetadata metadata = findInjectionMetadata(beanName, beanClass, beanDefinition);
|
||||||
Collection<AutowiredElement> autowiredElements = getAutowiredElements(metadata);
|
Collection<AutowiredElement> autowiredElements = getAutowiredElements(metadata,
|
||||||
|
registeredBean.getMergedBeanDefinition().getPropertyValues());
|
||||||
if (!ObjectUtils.isEmpty(autowiredElements)) {
|
if (!ObjectUtils.isEmpty(autowiredElements)) {
|
||||||
return new AotContribution(beanClass, autowiredElements, getAutowireCandidateResolver());
|
return new AotContribution(beanClass, autowiredElements, getAutowireCandidateResolver());
|
||||||
}
|
}
|
||||||
|
@ -295,8 +296,8 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||||
private Collection<AutowiredElement> getAutowiredElements(InjectionMetadata metadata) {
|
private Collection<AutowiredElement> getAutowiredElements(InjectionMetadata metadata, PropertyValues propertyValues) {
|
||||||
return (Collection) metadata.getInjectedElements();
|
return (Collection) metadata.getInjectedElements(propertyValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@ -752,7 +753,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
|
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
|
||||||
if (checkPropertySkipping(pvs)) {
|
if (!shouldInject(pvs)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Method method = (Method) this.member;
|
Method method = (Method) this.member;
|
||||||
|
|
|
@ -97,6 +97,19 @@ public class InjectionMetadata {
|
||||||
return Collections.unmodifiableCollection(this.injectedElements);
|
return Collections.unmodifiableCollection(this.injectedElements);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the {@link InjectedElement elements} to inject based on the
|
||||||
|
* specified {@link PropertyValues}. If a property is already defined
|
||||||
|
* for an {@link InjectedElement}, it is excluded.
|
||||||
|
* @param pvs the property values to consider
|
||||||
|
* @return the elements to inject
|
||||||
|
* @since 6.0.10
|
||||||
|
*/
|
||||||
|
public Collection<InjectedElement> getInjectedElements(@Nullable PropertyValues pvs) {
|
||||||
|
return this.injectedElements.stream()
|
||||||
|
.filter(candidate -> candidate.shouldInject(pvs)).toList();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine whether this metadata instance needs to be refreshed.
|
* Determine whether this metadata instance needs to be refreshed.
|
||||||
* @param clazz the current target class
|
* @param clazz the current target class
|
||||||
|
@ -230,21 +243,28 @@ public class InjectionMetadata {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected boolean shouldInject(@Nullable PropertyValues pvs) {
|
||||||
|
if (this.isField) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return !checkPropertySkipping(pvs);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Either this or {@link #getResourceToInject} needs to be overridden.
|
* Either this or {@link #getResourceToInject} needs to be overridden.
|
||||||
*/
|
*/
|
||||||
protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs)
|
protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs)
|
||||||
throws Throwable {
|
throws Throwable {
|
||||||
|
|
||||||
|
if (!shouldInject(pvs)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (this.isField) {
|
if (this.isField) {
|
||||||
Field field = (Field) this.member;
|
Field field = (Field) this.member;
|
||||||
ReflectionUtils.makeAccessible(field);
|
ReflectionUtils.makeAccessible(field);
|
||||||
field.set(target, getResourceToInject(target, requestingBeanName));
|
field.set(target, getResourceToInject(target, requestingBeanName));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (checkPropertySkipping(pvs)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
Method method = (Method) this.member;
|
Method method = (Method) this.member;
|
||||||
ReflectionUtils.makeAccessible(method);
|
ReflectionUtils.makeAccessible(method);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2022 the original author or authors.
|
* Copyright 2002-2023 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -65,11 +65,15 @@ class AutowiredAnnotationBeanRegistrationAotContributionTests {
|
||||||
|
|
||||||
private final DefaultListableBeanFactory beanFactory;
|
private final DefaultListableBeanFactory beanFactory;
|
||||||
|
|
||||||
|
private final AutowiredAnnotationBeanPostProcessor beanPostProcessor;
|
||||||
|
|
||||||
|
|
||||||
AutowiredAnnotationBeanRegistrationAotContributionTests() {
|
AutowiredAnnotationBeanRegistrationAotContributionTests() {
|
||||||
this.generationContext = new TestGenerationContext();
|
this.generationContext = new TestGenerationContext();
|
||||||
this.beanRegistrationCode = new MockBeanRegistrationCode(this.generationContext);
|
this.beanRegistrationCode = new MockBeanRegistrationCode(this.generationContext);
|
||||||
this.beanFactory = new DefaultListableBeanFactory();
|
this.beanFactory = new DefaultListableBeanFactory();
|
||||||
|
this.beanPostProcessor = new AutowiredAnnotationBeanPostProcessor();
|
||||||
|
this.beanPostProcessor.setBeanFactory(this.beanFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -185,10 +189,19 @@ class AutowiredAnnotationBeanRegistrationAotContributionTests {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void contributeWhenMethodInjectionHasMatchingPropertyValue() {
|
||||||
|
RootBeanDefinition beanDefinition = new RootBeanDefinition(InjectionBean.class);
|
||||||
|
beanDefinition.getPropertyValues().addPropertyValue("counter", 42);
|
||||||
|
this.beanFactory.registerBeanDefinition("test", beanDefinition);
|
||||||
|
BeanRegistrationAotContribution contribution = this.beanPostProcessor
|
||||||
|
.processAheadOfTime(RegisteredBean.of(this.beanFactory, "test"));
|
||||||
|
assertThat(contribution).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
private RegisteredBean getAndApplyContribution(Class<?> beanClass) {
|
private RegisteredBean getAndApplyContribution(Class<?> beanClass) {
|
||||||
RegisteredBean registeredBean = registerBean(beanClass);
|
RegisteredBean registeredBean = registerBean(beanClass);
|
||||||
BeanRegistrationAotContribution contribution = new AutowiredAnnotationBeanPostProcessor()
|
BeanRegistrationAotContribution contribution = this.beanPostProcessor.processAheadOfTime(registeredBean);
|
||||||
.processAheadOfTime(registeredBean);
|
|
||||||
assertThat(contribution).isNotNull();
|
assertThat(contribution).isNotNull();
|
||||||
contribution.applyTo(this.generationContext, this.beanRegistrationCode);
|
contribution.applyTo(this.generationContext, this.beanRegistrationCode);
|
||||||
return registeredBean;
|
return registeredBean;
|
||||||
|
@ -229,4 +242,15 @@ class AutowiredAnnotationBeanRegistrationAotContributionTests {
|
||||||
result.accept(compiled.getInstance(BiFunction.class), compiled));
|
result.accept(compiled.getInstance(BiFunction.class), compiled));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class InjectionBean {
|
||||||
|
|
||||||
|
private Integer counter;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public void setCounter(Integer counter) {
|
||||||
|
this.counter = counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -359,7 +359,8 @@ public class PersistenceAnnotationBeanPostProcessor implements InstantiationAwar
|
||||||
String beanName = registeredBean.getBeanName();
|
String beanName = registeredBean.getBeanName();
|
||||||
RootBeanDefinition beanDefinition = registeredBean.getMergedBeanDefinition();
|
RootBeanDefinition beanDefinition = registeredBean.getMergedBeanDefinition();
|
||||||
InjectionMetadata metadata = findInjectionMetadata(beanDefinition, beanClass, beanName);
|
InjectionMetadata metadata = findInjectionMetadata(beanDefinition, beanClass, beanName);
|
||||||
Collection<InjectedElement> injectedElements = metadata.getInjectedElements();
|
Collection<InjectedElement> injectedElements = metadata.getInjectedElements(
|
||||||
|
registeredBean.getMergedBeanDefinition().getPropertyValues());
|
||||||
if (!CollectionUtils.isEmpty(injectedElements)) {
|
if (!CollectionUtils.isEmpty(injectedElements)) {
|
||||||
return new AotContribution(beanClass, injectedElements);
|
return new AotContribution(beanClass, injectedElements);
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,7 @@ import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
import org.springframework.core.test.tools.CompileWithForkedClassLoader;
|
import org.springframework.core.test.tools.CompileWithForkedClassLoader;
|
||||||
import org.springframework.core.test.tools.Compiled;
|
import org.springframework.core.test.tools.Compiled;
|
||||||
import org.springframework.core.test.tools.TestCompiler;
|
import org.springframework.core.test.tools.TestCompiler;
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
@ -67,6 +68,20 @@ class PersistenceAnnotationBeanPostProcessorAotContributionTests {
|
||||||
this.generationContext = new TestGenerationContext();
|
this.generationContext = new TestGenerationContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void processAheadOfTimeWhenPersistenceUnitOnFieldAndPropertyValueSet() {
|
||||||
|
RegisteredBean registeredBean = registerBean(DefaultPersistenceUnitField.class);
|
||||||
|
registeredBean.getMergedBeanDefinition().getPropertyValues().add("emf", "myEntityManagerFactory");
|
||||||
|
assertThat(processAheadOfTime(registeredBean)).isNotNull(); // Field not handled by property values
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void processAheadOfTimeWhenPersistenceUnitOnMethodAndPropertyValueSet() {
|
||||||
|
RegisteredBean registeredBean = registerBean(DefaultPersistenceUnitMethod.class);
|
||||||
|
registeredBean.getMergedBeanDefinition().getPropertyValues().add("emf", "myEntityManagerFactory");
|
||||||
|
assertThat(processAheadOfTime(registeredBean)).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void processAheadOfTimeWhenPersistenceUnitOnPublicField() {
|
void processAheadOfTimeWhenPersistenceUnitOnPublicField() {
|
||||||
RegisteredBean registeredBean = registerBean(DefaultPersistenceUnitField.class);
|
RegisteredBean registeredBean = registerBean(DefaultPersistenceUnitField.class);
|
||||||
|
@ -192,9 +207,7 @@ class PersistenceAnnotationBeanPostProcessorAotContributionTests {
|
||||||
|
|
||||||
private void testCompile(RegisteredBean registeredBean,
|
private void testCompile(RegisteredBean registeredBean,
|
||||||
BiConsumer<BiConsumer<RegisteredBean, Object>, Compiled> result) {
|
BiConsumer<BiConsumer<RegisteredBean, Object>, Compiled> result) {
|
||||||
PersistenceAnnotationBeanPostProcessor postProcessor = new PersistenceAnnotationBeanPostProcessor();
|
BeanRegistrationAotContribution contribution = processAheadOfTime(registeredBean);
|
||||||
BeanRegistrationAotContribution contribution = postProcessor
|
|
||||||
.processAheadOfTime(registeredBean);
|
|
||||||
BeanRegistrationCode beanRegistrationCode = mock();
|
BeanRegistrationCode beanRegistrationCode = mock();
|
||||||
contribution.applyTo(generationContext, beanRegistrationCode);
|
contribution.applyTo(generationContext, beanRegistrationCode);
|
||||||
generationContext.writeGeneratedContent();
|
generationContext.writeGeneratedContent();
|
||||||
|
@ -202,6 +215,12 @@ class PersistenceAnnotationBeanPostProcessorAotContributionTests {
|
||||||
.compile(compiled -> result.accept(new Invoker(compiled), compiled));
|
.compile(compiled -> result.accept(new Invoker(compiled), compiled));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) {
|
||||||
|
PersistenceAnnotationBeanPostProcessor postProcessor = new PersistenceAnnotationBeanPostProcessor();
|
||||||
|
return postProcessor.processAheadOfTime(registeredBean);
|
||||||
|
}
|
||||||
|
|
||||||
static class Invoker implements BiConsumer<RegisteredBean, Object> {
|
static class Invoker implements BiConsumer<RegisteredBean, Object> {
|
||||||
|
|
||||||
private Compiled compiled;
|
private Compiled compiled;
|
||||||
|
|
Loading…
Reference in New Issue