diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java index bb5ff2cf64a..074478e063d 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -1594,7 +1594,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto /** - * A dependency descriptor marker for multiple elements. + * A dependency descriptor for a multi-element declaration with nested elements. */ private static class MultiElementDescriptor extends NestedDependencyDescriptor { @@ -1622,7 +1622,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto super.resolveCandidate(beanName, requiredType, beanFactory)); } }; - return Optional.ofNullable(doResolveDependency(descriptorToUse, beanName, null, null)); + Object result = doResolveDependency(descriptorToUse, beanName, null, null); + return (result instanceof Optional ? (Optional) result : Optional.ofNullable(result)); } } diff --git a/spring-context/src/test/java/org/springframework/context/expression/ApplicationContextExpressionTests.java b/spring-context/src/test/java/org/springframework/context/expression/ApplicationContextExpressionTests.java index f744427a926..3eb87d70064 100644 --- a/spring-context/src/test/java/org/springframework/context/expression/ApplicationContextExpressionTests.java +++ b/spring-context/src/test/java/org/springframework/context/expression/ApplicationContextExpressionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2019 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. @@ -25,6 +25,7 @@ import java.net.URI; import java.net.URL; import java.security.AccessControlException; import java.security.Permission; +import java.util.Optional; import java.util.Properties; import org.apache.commons.logging.Log; @@ -45,7 +46,7 @@ import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.AnnotationConfigUtils; import org.springframework.context.support.GenericApplicationContext; -import org.springframework.core.convert.converter.Converter; +import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.core.convert.support.GenericConversionService; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; @@ -61,6 +62,7 @@ import static org.junit.Assert.*; /** * @author Juergen Hoeller + * @author Sam Brannen * @since 3.0 */ public class ApplicationContextExpressionTests { @@ -100,6 +102,8 @@ public class ApplicationContextExpressionTests { } }); + ac.getBeanFactory().setConversionService(new DefaultConversionService()); + PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer(); Properties placeholders = new Properties(); placeholders.setProperty("code", "123"); @@ -174,6 +178,9 @@ public class ApplicationContextExpressionTests { System.getProperties().put("country", "UK"); assertEquals("123 UK", tb3.country); assertEquals("123 UK", tb3.countryFactory.getObject()); + assertEquals("123", tb3.optionalValue1.get()); + assertEquals("123", tb3.optionalValue2.get()); + assertFalse(tb3.optionalValue3.isPresent()); assertSame(tb0, tb3.tb); tb3 = (ValueTestBean) SerializationTestUtils.serializeAndDeserialize(tb3); @@ -207,12 +214,7 @@ public class ApplicationContextExpressionTests { GenericApplicationContext ac = new GenericApplicationContext(); AnnotationConfigUtils.registerAnnotationConfigProcessors(ac); GenericConversionService cs = new GenericConversionService(); - cs.addConverter(String.class, String.class, new Converter() { - @Override - public String convert(String source) { - return source.trim(); - } - }); + cs.addConverter(String.class, String.class, String::trim); ac.getBeanFactory().registerSingleton(GenericApplicationContext.CONVERSION_SERVICE_BEAN_NAME, cs); RootBeanDefinition rbd = new RootBeanDefinition(PrototypeTestBean.class); rbd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); @@ -274,8 +276,7 @@ public class ApplicationContextExpressionTests { @Test public void systemPropertiesSecurityManager() { - GenericApplicationContext ac = new GenericApplicationContext(); - AnnotationConfigUtils.registerAnnotationConfigProcessors(ac); + AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(); GenericBeanDefinition bd = new GenericBeanDefinition(); bd.setBeanClass(TestBean.class); @@ -311,8 +312,7 @@ public class ApplicationContextExpressionTests { @Test public void stringConcatenationWithDebugLogging() { - GenericApplicationContext ac = new GenericApplicationContext(); - AnnotationConfigUtils.registerAnnotationConfigProcessors(ac); + AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(); GenericBeanDefinition bd = new GenericBeanDefinition(); bd.setBeanClass(String.class); @@ -326,11 +326,10 @@ public class ApplicationContextExpressionTests { @Test public void resourceInjection() throws IOException { - System.setProperty("logfile", "log4j.properties"); - try { - AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(ResourceInjectionBean.class); + System.setProperty("logfile", "do_not_delete_me.txt"); + try (AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(ResourceInjectionBean.class)) { ResourceInjectionBean resourceInjectionBean = ac.getBean(ResourceInjectionBean.class); - Resource resource = new ClassPathResource("log4j.properties"); + Resource resource = new ClassPathResource("do_not_delete_me.txt"); assertEquals(resource, resourceInjectionBean.resource); assertEquals(resource.getURL(), resourceInjectionBean.url); assertEquals(resource.getURI(), resourceInjectionBean.uri); @@ -364,6 +363,15 @@ public class ApplicationContextExpressionTests { @Value("${code} #{systemProperties.country}") public ObjectFactory countryFactory; + @Value("${code}") + private transient Optional optionalValue1; + + @Value("${code:#{null}}") + private transient Optional optionalValue2; + + @Value("${codeX:#{null}}") + private transient Optional optionalValue3; + @Autowired @Qualifier("original") public transient TestBean tb; } diff --git a/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java b/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java index 03374f757f3..4cab681ad85 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java +++ b/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -83,7 +83,7 @@ public class TypeDescriptor implements Serializable { */ public TypeDescriptor(MethodParameter methodParameter) { this.resolvableType = ResolvableType.forMethodParameter(methodParameter); - this.type = this.resolvableType.resolve(methodParameter.getParameterType()); + this.type = this.resolvableType.resolve(methodParameter.getNestedParameterType()); this.annotatedElement = new AnnotatedElementAdapter(methodParameter.getParameterIndex() == -1 ? methodParameter.getMethodAnnotations() : methodParameter.getParameterAnnotations()); } @@ -607,7 +607,7 @@ public class TypeDescriptor implements Serializable { } /** - * Creates a type descriptor for a nested type declared within the method parameter. + * Create a type descriptor for a nested type declared within the method parameter. *

For example, if the methodParameter is a {@code List} and the * nesting level is 1, the nested type descriptor will be String.class. *

If the methodParameter is a {@code List>} and the nesting @@ -637,7 +637,7 @@ public class TypeDescriptor implements Serializable { } /** - * Creates a type descriptor for a nested type declared within the field. + * Create a type descriptor for a nested type declared within the field. *

For example, if the field is a {@code List} and the nesting * level is 1, the nested type descriptor will be {@code String.class}. *

If the field is a {@code List>} and the nesting level is @@ -646,8 +646,9 @@ public class TypeDescriptor implements Serializable { * is 1, the nested type descriptor will be String, derived from the map value. *

If the field is a {@code List>} and the nesting * level is 2, the nested type descriptor will be String, derived from the map value. - *

Returns {@code null} if a nested type cannot be obtained because it was not declared. - * For example, if the field is a {@code List}, the nested type descriptor returned will be {@code null}. + *

Returns {@code null} if a nested type cannot be obtained because it was not + * declared. For example, if the field is a {@code List}, the nested type + * descriptor returned will be {@code null}. * @param field the field * @param nestingLevel the nesting level of the collection/array element or * map key/value declaration within the field @@ -661,7 +662,7 @@ public class TypeDescriptor implements Serializable { } /** - * Creates a type descriptor for a nested type declared within the property. + * Create a type descriptor for a nested type declared within the property. *

For example, if the property is a {@code List} and the nesting * level is 1, the nested type descriptor will be {@code String.class}. *

If the property is a {@code List>} and the nesting level @@ -670,9 +671,9 @@ public class TypeDescriptor implements Serializable { * is 1, the nested type descriptor will be String, derived from the map value. *

If the property is a {@code List>} and the nesting * level is 2, the nested type descriptor will be String, derived from the map value. - *

Returns {@code null} if a nested type cannot be obtained because it was not declared. - * For example, if the property is a {@code List}, the nested type descriptor - * returned will be {@code null}. + *

Returns {@code null} if a nested type cannot be obtained because it was not + * declared. For example, if the property is a {@code List}, the nested type + * descriptor returned will be {@code null}. * @param property the property * @param nestingLevel the nesting level of the collection/array element or * map key/value declaration within the property