Skip to content

Commit e714fc5

Browse files
committed
DefaultListableBeanFactory checks for pre-converted Optional wrappers
Issue: SPR-17607
1 parent c02446c commit e714fc5

File tree

3 files changed

+39
-29
lines changed

3 files changed

+39
-29
lines changed

spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -1594,7 +1594,7 @@ public NestedDependencyDescriptor(DependencyDescriptor original) {
15941594

15951595

15961596
/**
1597-
* A dependency descriptor marker for multiple elements.
1597+
* A dependency descriptor for a multi-element declaration with nested elements.
15981598
*/
15991599
private static class MultiElementDescriptor extends NestedDependencyDescriptor {
16001600

@@ -1622,7 +1622,8 @@ public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFacto
16221622
super.resolveCandidate(beanName, requiredType, beanFactory));
16231623
}
16241624
};
1625-
return Optional.ofNullable(doResolveDependency(descriptorToUse, beanName, null, null));
1625+
Object result = doResolveDependency(descriptorToUse, beanName, null, null);
1626+
return (result instanceof Optional ? (Optional<?>) result : Optional.ofNullable(result));
16261627
}
16271628
}
16281629

spring-context/src/test/java/org/springframework/context/expression/ApplicationContextExpressionTests.java

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -25,6 +25,7 @@
2525
import java.net.URL;
2626
import java.security.AccessControlException;
2727
import java.security.Permission;
28+
import java.util.Optional;
2829
import java.util.Properties;
2930

3031
import org.apache.commons.logging.Log;
@@ -45,7 +46,7 @@
4546
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
4647
import org.springframework.context.annotation.AnnotationConfigUtils;
4748
import org.springframework.context.support.GenericApplicationContext;
48-
import org.springframework.core.convert.converter.Converter;
49+
import org.springframework.core.convert.support.DefaultConversionService;
4950
import org.springframework.core.convert.support.GenericConversionService;
5051
import org.springframework.core.io.ClassPathResource;
5152
import org.springframework.core.io.Resource;
@@ -61,6 +62,7 @@
6162

6263
/**
6364
* @author Juergen Hoeller
65+
* @author Sam Brannen
6466
* @since 3.0
6567
*/
6668
public class ApplicationContextExpressionTests {
@@ -100,6 +102,8 @@ public String getConversationId() {
100102
}
101103
});
102104

105+
ac.getBeanFactory().setConversionService(new DefaultConversionService());
106+
103107
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
104108
Properties placeholders = new Properties();
105109
placeholders.setProperty("code", "123");
@@ -174,6 +178,9 @@ public String getConversationId() {
174178
System.getProperties().put("country", "UK");
175179
assertEquals("123 UK", tb3.country);
176180
assertEquals("123 UK", tb3.countryFactory.getObject());
181+
assertEquals("123", tb3.optionalValue1.get());
182+
assertEquals("123", tb3.optionalValue2.get());
183+
assertFalse(tb3.optionalValue3.isPresent());
177184
assertSame(tb0, tb3.tb);
178185

179186
tb3 = (ValueTestBean) SerializationTestUtils.serializeAndDeserialize(tb3);
@@ -207,12 +214,7 @@ public void prototypeCreationReevaluatesExpressions() {
207214
GenericApplicationContext ac = new GenericApplicationContext();
208215
AnnotationConfigUtils.registerAnnotationConfigProcessors(ac);
209216
GenericConversionService cs = new GenericConversionService();
210-
cs.addConverter(String.class, String.class, new Converter<String, String>() {
211-
@Override
212-
public String convert(String source) {
213-
return source.trim();
214-
}
215-
});
217+
cs.addConverter(String.class, String.class, String::trim);
216218
ac.getBeanFactory().registerSingleton(GenericApplicationContext.CONVERSION_SERVICE_BEAN_NAME, cs);
217219
RootBeanDefinition rbd = new RootBeanDefinition(PrototypeTestBean.class);
218220
rbd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
@@ -274,8 +276,7 @@ public void prototypeCreationIsFastEnough() {
274276

275277
@Test
276278
public void systemPropertiesSecurityManager() {
277-
GenericApplicationContext ac = new GenericApplicationContext();
278-
AnnotationConfigUtils.registerAnnotationConfigProcessors(ac);
279+
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
279280

280281
GenericBeanDefinition bd = new GenericBeanDefinition();
281282
bd.setBeanClass(TestBean.class);
@@ -311,8 +312,7 @@ public void checkPermission(Permission perm) {
311312

312313
@Test
313314
public void stringConcatenationWithDebugLogging() {
314-
GenericApplicationContext ac = new GenericApplicationContext();
315-
AnnotationConfigUtils.registerAnnotationConfigProcessors(ac);
315+
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
316316

317317
GenericBeanDefinition bd = new GenericBeanDefinition();
318318
bd.setBeanClass(String.class);
@@ -326,11 +326,10 @@ public void stringConcatenationWithDebugLogging() {
326326

327327
@Test
328328
public void resourceInjection() throws IOException {
329-
System.setProperty("logfile", "log4j.properties");
330-
try {
331-
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(ResourceInjectionBean.class);
329+
System.setProperty("logfile", "do_not_delete_me.txt");
330+
try (AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(ResourceInjectionBean.class)) {
332331
ResourceInjectionBean resourceInjectionBean = ac.getBean(ResourceInjectionBean.class);
333-
Resource resource = new ClassPathResource("log4j.properties");
332+
Resource resource = new ClassPathResource("do_not_delete_me.txt");
334333
assertEquals(resource, resourceInjectionBean.resource);
335334
assertEquals(resource.getURL(), resourceInjectionBean.url);
336335
assertEquals(resource.getURI(), resourceInjectionBean.uri);
@@ -364,6 +363,15 @@ public static class ValueTestBean implements Serializable {
364363
@Value("${code} #{systemProperties.country}")
365364
public ObjectFactory<String> countryFactory;
366365

366+
@Value("${code}")
367+
private transient Optional<String> optionalValue1;
368+
369+
@Value("${code:#{null}}")
370+
private transient Optional<String> optionalValue2;
371+
372+
@Value("${codeX:#{null}}")
373+
private transient Optional<String> optionalValue3;
374+
367375
@Autowired @Qualifier("original")
368376
public transient TestBean tb;
369377
}

spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -83,7 +83,7 @@ public class TypeDescriptor implements Serializable {
8383
*/
8484
public TypeDescriptor(MethodParameter methodParameter) {
8585
this.resolvableType = ResolvableType.forMethodParameter(methodParameter);
86-
this.type = this.resolvableType.resolve(methodParameter.getParameterType());
86+
this.type = this.resolvableType.resolve(methodParameter.getNestedParameterType());
8787
this.annotatedElement = new AnnotatedElementAdapter(methodParameter.getParameterIndex() == -1 ?
8888
methodParameter.getMethodAnnotations() : methodParameter.getParameterAnnotations());
8989
}
@@ -607,7 +607,7 @@ public static TypeDescriptor array(TypeDescriptor elementTypeDescriptor) {
607607
}
608608

609609
/**
610-
* Creates a type descriptor for a nested type declared within the method parameter.
610+
* Create a type descriptor for a nested type declared within the method parameter.
611611
* <p>For example, if the methodParameter is a {@code List<String>} and the
612612
* nesting level is 1, the nested type descriptor will be String.class.
613613
* <p>If the methodParameter is a {@code List<List<String>>} and the nesting
@@ -637,7 +637,7 @@ public static TypeDescriptor nested(MethodParameter methodParameter, int nesting
637637
}
638638

639639
/**
640-
* Creates a type descriptor for a nested type declared within the field.
640+
* Create a type descriptor for a nested type declared within the field.
641641
* <p>For example, if the field is a {@code List<String>} and the nesting
642642
* level is 1, the nested type descriptor will be {@code String.class}.
643643
* <p>If the field is a {@code List<List<String>>} and the nesting level is
@@ -646,8 +646,9 @@ public static TypeDescriptor nested(MethodParameter methodParameter, int nesting
646646
* is 1, the nested type descriptor will be String, derived from the map value.
647647
* <p>If the field is a {@code List<Map<Integer, String>>} and the nesting
648648
* level is 2, the nested type descriptor will be String, derived from the map value.
649-
* <p>Returns {@code null} if a nested type cannot be obtained because it was not declared.
650-
* For example, if the field is a {@code List<?>}, the nested type descriptor returned will be {@code null}.
649+
* <p>Returns {@code null} if a nested type cannot be obtained because it was not
650+
* declared. For example, if the field is a {@code List<?>}, the nested type
651+
* descriptor returned will be {@code null}.
651652
* @param field the field
652653
* @param nestingLevel the nesting level of the collection/array element or
653654
* map key/value declaration within the field
@@ -661,7 +662,7 @@ public static TypeDescriptor nested(Field field, int nestingLevel) {
661662
}
662663

663664
/**
664-
* Creates a type descriptor for a nested type declared within the property.
665+
* Create a type descriptor for a nested type declared within the property.
665666
* <p>For example, if the property is a {@code List<String>} and the nesting
666667
* level is 1, the nested type descriptor will be {@code String.class}.
667668
* <p>If the property is a {@code List<List<String>>} and the nesting level
@@ -670,9 +671,9 @@ public static TypeDescriptor nested(Field field, int nestingLevel) {
670671
* is 1, the nested type descriptor will be String, derived from the map value.
671672
* <p>If the property is a {@code List<Map<Integer, String>>} and the nesting
672673
* level is 2, the nested type descriptor will be String, derived from the map value.
673-
* <p>Returns {@code null} if a nested type cannot be obtained because it was not declared.
674-
* For example, if the property is a {@code List<?>}, the nested type descriptor
675-
* returned will be {@code null}.
674+
* <p>Returns {@code null} if a nested type cannot be obtained because it was not
675+
* declared. For example, if the property is a {@code List<?>}, the nested type
676+
* descriptor returned will be {@code null}.
676677
* @param property the property
677678
* @param nestingLevel the nesting level of the collection/array element or
678679
* map key/value declaration within the property

0 commit comments

Comments
 (0)