Merge branch '6.0.x'

This commit is contained in:
Juergen Hoeller 2023-07-04 15:59:38 +02:00
commit 2a77665be7
3 changed files with 93 additions and 17 deletions

View File

@ -638,7 +638,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
* Resolve the specified cached method argument or field value. * Resolve the specified cached method argument or field value.
*/ */
@Nullable @Nullable
private Object resolvedCachedArgument(@Nullable String beanName, @Nullable Object cachedArgument) { private Object resolveCachedArgument(@Nullable String beanName, @Nullable Object cachedArgument) {
if (cachedArgument instanceof DependencyDescriptor descriptor) { if (cachedArgument instanceof DependencyDescriptor descriptor) {
Assert.state(this.beanFactory != null, "No BeanFactory available"); Assert.state(this.beanFactory != null, "No BeanFactory available");
return this.beanFactory.resolveDependency(descriptor, beanName, null, null); return this.beanFactory.resolveDependency(descriptor, beanName, null, null);
@ -683,10 +683,12 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
Object value; Object value;
if (this.cached) { if (this.cached) {
try { try {
value = resolvedCachedArgument(beanName, this.cachedFieldValue); value = resolveCachedArgument(beanName, this.cachedFieldValue);
} }
catch (NoSuchBeanDefinitionException ex) { catch (BeansException ex) {
// Unexpected removal of target bean for cached argument -> re-resolve // Unexpected target bean mismatch for cached argument -> re-resolve
this.cached = false;
logger.debug("Failed to resolve cached argument", ex);
value = resolveFieldValue(field, bean, beanName); value = resolveFieldValue(field, bean, beanName);
} }
} }
@ -715,9 +717,8 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
} }
synchronized (this) { synchronized (this) {
if (!this.cached) { if (!this.cached) {
Object cachedFieldValue = null;
if (value != null || this.required) { if (value != null || this.required) {
cachedFieldValue = desc; Object cachedFieldValue = desc;
registerDependentBeans(beanName, autowiredBeanNames); registerDependentBeans(beanName, autowiredBeanNames);
if (value != null && autowiredBeanNames.size() == 1) { if (value != null && autowiredBeanNames.size() == 1) {
String autowiredBeanName = autowiredBeanNames.iterator().next(); String autowiredBeanName = autowiredBeanNames.iterator().next();
@ -727,9 +728,13 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
desc, autowiredBeanName, field.getType()); desc, autowiredBeanName, field.getType());
} }
} }
this.cachedFieldValue = cachedFieldValue;
this.cached = true;
}
else {
this.cachedFieldValue = null;
// cached flag remains false
} }
this.cachedFieldValue = cachedFieldValue;
this.cached = true;
} }
} }
return value; return value;
@ -760,10 +765,12 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
Object[] arguments; Object[] arguments;
if (this.cached) { if (this.cached) {
try { try {
arguments = resolveCachedArguments(beanName); arguments = resolveCachedArguments(beanName, this.cachedMethodArguments);
} }
catch (NoSuchBeanDefinitionException ex) { catch (BeansException ex) {
// Unexpected removal of target bean for cached argument -> re-resolve // Unexpected target bean mismatch for cached argument -> re-resolve
this.cached = false;
logger.debug("Failed to resolve cached argument", ex);
arguments = resolveMethodArguments(method, bean, beanName); arguments = resolveMethodArguments(method, bean, beanName);
} }
} }
@ -782,14 +789,13 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
} }
@Nullable @Nullable
private Object[] resolveCachedArguments(@Nullable String beanName) { private Object[] resolveCachedArguments(@Nullable String beanName, @Nullable Object[] cachedMethodArguments) {
Object[] cachedMethodArguments = this.cachedMethodArguments;
if (cachedMethodArguments == null) { if (cachedMethodArguments == null) {
return null; return null;
} }
Object[] arguments = new Object[cachedMethodArguments.length]; Object[] arguments = new Object[cachedMethodArguments.length];
for (int i = 0; i < arguments.length; i++) { for (int i = 0; i < arguments.length; i++) {
arguments[i] = resolvedCachedArgument(beanName, cachedMethodArguments[i]); arguments[i] = resolveCachedArgument(beanName, cachedMethodArguments[i]);
} }
return arguments; return arguments;
} }
@ -822,7 +828,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
synchronized (this) { synchronized (this) {
if (!this.cached) { if (!this.cached) {
if (arguments != null) { if (arguments != null) {
DependencyDescriptor[] cachedMethodArguments = Arrays.copyOf(descriptors, arguments.length); DependencyDescriptor[] cachedMethodArguments = Arrays.copyOf(descriptors, argumentCount);
registerDependentBeans(beanName, autowiredBeans); registerDependentBeans(beanName, autowiredBeans);
if (autowiredBeans.size() == argumentCount) { if (autowiredBeans.size() == argumentCount) {
Iterator<String> it = autowiredBeans.iterator(); Iterator<String> it = autowiredBeans.iterator();
@ -837,11 +843,12 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
} }
} }
this.cachedMethodArguments = cachedMethodArguments; this.cachedMethodArguments = cachedMethodArguments;
this.cached = true;
} }
else { else {
this.cachedMethodArguments = null; this.cachedMethodArguments = null;
// cached flag remains false
} }
this.cached = true;
} }
} }
return arguments; return arguments;

View File

@ -151,6 +151,59 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertThat(bean.getTestBean3()).isNull(); assertThat(bean.getTestBean3()).isNull();
} }
@Test
void resourceInjectionWithSometimesNullBean() {
RootBeanDefinition bd = new RootBeanDefinition(OptionalResourceInjectionBean.class);
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
bf.registerBeanDefinition("annotatedBean", bd);
RootBeanDefinition tb = new RootBeanDefinition(SometimesNullFactoryMethods.class);
tb.setFactoryMethodName("createTestBean");
tb.setScope(BeanDefinition.SCOPE_PROTOTYPE);
bf.registerBeanDefinition("testBean", tb);
SometimesNullFactoryMethods.active = false;
OptionalResourceInjectionBean bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).isNull();
assertThat(bean.getTestBean2()).isNull();
assertThat(bean.getTestBean3()).isNull();
SometimesNullFactoryMethods.active = true;
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).isNotNull();
assertThat(bean.getTestBean2()).isNotNull();
assertThat(bean.getTestBean3()).isNotNull();
SometimesNullFactoryMethods.active = false;
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).isNull();
assertThat(bean.getTestBean2()).isNull();
assertThat(bean.getTestBean3()).isNull();
SometimesNullFactoryMethods.active = false;
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).isNull();
assertThat(bean.getTestBean2()).isNull();
assertThat(bean.getTestBean3()).isNull();
SometimesNullFactoryMethods.active = true;
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).isNotNull();
assertThat(bean.getTestBean2()).isNotNull();
assertThat(bean.getTestBean3()).isNotNull();
SometimesNullFactoryMethods.active = true;
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).isNotNull();
assertThat(bean.getTestBean2()).isNotNull();
assertThat(bean.getTestBean3()).isNotNull();
SometimesNullFactoryMethods.active = false;
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).isNull();
assertThat(bean.getTestBean2()).isNull();
assertThat(bean.getTestBean3()).isNull();
}
@Test @Test
void extendedResourceInjection() { void extendedResourceInjection() {
RootBeanDefinition bd = new RootBeanDefinition(TypedExtendedResourceInjectionBean.class); RootBeanDefinition bd = new RootBeanDefinition(TypedExtendedResourceInjectionBean.class);
@ -3881,6 +3934,20 @@ public class AutowiredAnnotationBeanPostProcessorTests {
} }
public static class SometimesNullFactoryMethods {
public static boolean active = false;
public static TestBean createTestBean() {
return (active ? new TestBean() : null);
}
public static NestedTestBean createNestedTestBean() {
return (active ? new NestedTestBean() : null);
}
}
public static class ProvidedArgumentBean { public static class ProvidedArgumentBean {
public ProvidedArgumentBean(String[] args) { public ProvidedArgumentBean(String[] args) {

View File

@ -124,8 +124,10 @@ public class FieldError extends ObjectError {
@Override @Override
public String toString() { public String toString() {
// We would preferably use ObjectUtils.nullSafeConciseToString(rejectedValue) here but
// keep including the full nullSafeToString representation for backwards compatibility.
return "Field error in object '" + getObjectName() + "' on field '" + this.field + return "Field error in object '" + getObjectName() + "' on field '" + this.field +
"': rejected value [" + ObjectUtils.nullSafeConciseToString(this.rejectedValue) + "]; " + "': rejected value [" + ObjectUtils.nullSafeToString(this.rejectedValue) + "]; " +
resolvableToString(); resolvableToString();
} }