Skip to content

Commit 5cb3f8e

Browse files
committed
Support for java.util.Optional within ObjectFactory/Provider
Includes support for arbitrary deep nesting levels in DependencyDescriptor's getDependencyType() and MethodParameter's getNestedParameterType(). Issue: SPR-11833
1 parent 7d03daf commit 5cb3f8e

File tree

4 files changed

+147
-51
lines changed

4 files changed

+147
-51
lines changed

spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2014 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.
@@ -263,18 +263,20 @@ public Class<?> getDependencyType() {
263263
if (this.field != null) {
264264
if (this.nestingLevel > 1) {
265265
Type type = this.field.getGenericType();
266-
if (type instanceof ParameterizedType) {
267-
Type[] args = ((ParameterizedType) type).getActualTypeArguments();
268-
Type arg = args[args.length - 1];
266+
for (int i = 2; i <= this.nestingLevel; i++) {
267+
if (type instanceof ParameterizedType) {
268+
Type[] args = ((ParameterizedType) type).getActualTypeArguments();
269+
type = args[args.length - 1];
270+
}
271+
}
272+
if (type instanceof Class) {
273+
return (Class<?>) type;
274+
}
275+
else if (type instanceof ParameterizedType) {
276+
Type arg = ((ParameterizedType) type).getRawType();
269277
if (arg instanceof Class) {
270278
return (Class<?>) arg;
271279
}
272-
else if (arg instanceof ParameterizedType) {
273-
arg = ((ParameterizedType) arg).getRawType();
274-
if (arg instanceof Class) {
275-
return (Class<?>) arg;
276-
}
277-
}
278280
}
279281
return Object.class;
280282
}

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

Lines changed: 41 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
import org.springframework.core.annotation.AnnotationUtils;
6565
import org.springframework.core.annotation.OrderProviderComparator;
6666
import org.springframework.core.annotation.OrderUtils;
67+
import org.springframework.lang.UsesJava8;
6768
import org.springframework.util.Assert;
6869
import org.springframework.util.ClassUtils;
6970
import org.springframework.util.ObjectUtils;
@@ -107,24 +108,24 @@
107108
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
108109
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
109110

110-
private static Class<?> javaxInjectProviderClass = null;
111-
112111
private static Class<?> javaUtilOptionalClass = null;
113112

113+
private static Class<?> javaxInjectProviderClass = null;
114+
114115
static {
115116
try {
116-
javaxInjectProviderClass =
117-
ClassUtils.forName("javax.inject.Provider", DefaultListableBeanFactory.class.getClassLoader());
117+
javaUtilOptionalClass =
118+
ClassUtils.forName("java.util.Optional", DefaultListableBeanFactory.class.getClassLoader());
118119
}
119120
catch (ClassNotFoundException ex) {
120-
// JSR-330 API not available - Provider interface simply not supported then.
121+
// Java 8 not available - Optional references simply not supported then.
121122
}
122123
try {
123-
javaUtilOptionalClass =
124-
ClassUtils.forName("java.util.Optional", DefaultListableBeanFactory.class.getClassLoader());
124+
javaxInjectProviderClass =
125+
ClassUtils.forName("javax.inject.Provider", DefaultListableBeanFactory.class.getClassLoader());
125126
}
126127
catch (ClassNotFoundException ex) {
127-
// Java 8 not available - Optional references simply not supported then.
128+
// JSR-330 API not available - Provider interface simply not supported then.
128129
}
129130
}
130131

@@ -858,15 +859,15 @@ public Object resolveDependency(DependencyDescriptor descriptor, String beanName
858859
Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
859860

860861
descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
861-
if (descriptor.getDependencyType().equals(ObjectFactory.class)) {
862+
if (descriptor.getDependencyType().equals(javaUtilOptionalClass)) {
863+
return new OptionalDependencyFactory().createOptionalDependency(descriptor, beanName);
864+
}
865+
else if (descriptor.getDependencyType().equals(ObjectFactory.class)) {
862866
return new DependencyObjectFactory(descriptor, beanName);
863867
}
864868
else if (descriptor.getDependencyType().equals(javaxInjectProviderClass)) {
865869
return new DependencyProviderFactory().createDependencyProvider(descriptor, beanName);
866870
}
867-
else if (descriptor.getDependencyType().equals(javaUtilOptionalClass)) {
868-
return new OptionalDependencyFactory().createOptionalDependency(descriptor, beanName);
869-
}
870871
else {
871872
Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(descriptor, beanName);
872873
if (result == null) {
@@ -1284,24 +1285,51 @@ private Object readResolve() {
12841285
}
12851286

12861287

1288+
/**
1289+
* Separate inner class for avoiding a hard dependency on the {@code javax.inject} API.
1290+
*/
1291+
@UsesJava8
1292+
private class OptionalDependencyFactory {
1293+
1294+
public Object createOptionalDependency(DependencyDescriptor descriptor, String beanName) {
1295+
DependencyDescriptor descriptorToUse = new DependencyDescriptor(descriptor) {
1296+
@Override
1297+
public boolean isRequired() {
1298+
return false;
1299+
}
1300+
};
1301+
descriptorToUse.increaseNestingLevel();
1302+
return Optional.ofNullable(doResolveDependency(descriptorToUse, beanName, null, null));
1303+
}
1304+
}
1305+
1306+
12871307
/**
12881308
* Serializable ObjectFactory for lazy resolution of a dependency.
12891309
*/
12901310
private class DependencyObjectFactory implements ObjectFactory<Object>, Serializable {
12911311

12921312
private final DependencyDescriptor descriptor;
12931313

1314+
private final boolean optional;
1315+
12941316
private final String beanName;
12951317

12961318
public DependencyObjectFactory(DependencyDescriptor descriptor, String beanName) {
12971319
this.descriptor = new DependencyDescriptor(descriptor);
12981320
this.descriptor.increaseNestingLevel();
1321+
this.optional = this.descriptor.getDependencyType().equals(javaUtilOptionalClass);
12991322
this.beanName = beanName;
13001323
}
13011324

13021325
@Override
13031326
public Object getObject() throws BeansException {
1304-
return doResolveDependency(this.descriptor, this.beanName, null, null);
1327+
if (this.optional) {
1328+
return new OptionalDependencyFactory().createOptionalDependency(this.descriptor, this.beanName);
1329+
}
1330+
else {
1331+
return doResolveDependency(this.descriptor, this.beanName, null, null);
1332+
}
13051333
}
13061334
}
13071335

@@ -1332,22 +1360,4 @@ public Object createDependencyProvider(DependencyDescriptor descriptor, String b
13321360
}
13331361
}
13341362

1335-
1336-
/**
1337-
* Separate inner class for avoiding a hard dependency on the {@code javax.inject} API.
1338-
*/
1339-
private class OptionalDependencyFactory {
1340-
1341-
public Object createOptionalDependency(DependencyDescriptor descriptor, String beanName) {
1342-
DependencyDescriptor descriptorToUse = new DependencyDescriptor(descriptor) {
1343-
@Override
1344-
public boolean isRequired() {
1345-
return false;
1346-
}
1347-
};
1348-
descriptorToUse.increaseNestingLevel();
1349-
return Optional.ofNullable(doResolveDependency(descriptorToUse, beanName, null, null));
1350-
}
1351-
}
1352-
13531363
}

spring-beans/src/test/java/org/springframework/beans/factory/annotation/InjectAnnotationBeanPostProcessorTests.java

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,62 @@ public void testOptionalMethodInjectionWithBeanNotAvailable() {
630630
bf.destroySingletons();
631631
}
632632

633+
@Test
634+
public void testProviderOfOptionalFieldInjectionWithBeanAvailable() {
635+
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
636+
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
637+
bpp.setBeanFactory(bf);
638+
bf.addBeanPostProcessor(bpp);
639+
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ProviderOfOptionalFieldInjectionBean.class));
640+
bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class));
641+
642+
ProviderOfOptionalFieldInjectionBean bean = (ProviderOfOptionalFieldInjectionBean) bf.getBean("annotatedBean");
643+
assertTrue(bean.getTestBean().isPresent());
644+
assertSame(bf.getBean("testBean"), bean.getTestBean().get());
645+
bf.destroySingletons();
646+
}
647+
648+
@Test
649+
public void testProviderOfOptionalFieldInjectionWithBeanNotAvailable() {
650+
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
651+
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
652+
bpp.setBeanFactory(bf);
653+
bf.addBeanPostProcessor(bpp);
654+
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ProviderOfOptionalFieldInjectionBean.class));
655+
656+
ProviderOfOptionalFieldInjectionBean bean = (ProviderOfOptionalFieldInjectionBean) bf.getBean("annotatedBean");
657+
assertFalse(bean.getTestBean().isPresent());
658+
bf.destroySingletons();
659+
}
660+
661+
@Test
662+
public void testProviderOfOptionalMethodInjectionWithBeanAvailable() {
663+
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
664+
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
665+
bpp.setBeanFactory(bf);
666+
bf.addBeanPostProcessor(bpp);
667+
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ProviderOfOptionalMethodInjectionBean.class));
668+
bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class));
669+
670+
ProviderOfOptionalMethodInjectionBean bean = (ProviderOfOptionalMethodInjectionBean) bf.getBean("annotatedBean");
671+
assertTrue(bean.getTestBean().isPresent());
672+
assertSame(bf.getBean("testBean"), bean.getTestBean().get());
673+
bf.destroySingletons();
674+
}
675+
676+
@Test
677+
public void testProviderOfOptionalMethodInjectionWithBeanNotAvailable() {
678+
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
679+
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
680+
bpp.setBeanFactory(bf);
681+
bf.addBeanPostProcessor(bpp);
682+
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ProviderOfOptionalMethodInjectionBean.class));
683+
684+
ProviderOfOptionalMethodInjectionBean bean = (ProviderOfOptionalMethodInjectionBean) bf.getBean("annotatedBean");
685+
assertFalse(bean.getTestBean().isPresent());
686+
bf.destroySingletons();
687+
}
688+
633689

634690
public static class ResourceInjectionBean {
635691

@@ -1182,4 +1238,30 @@ public Optional<TestBean> getTestBean() {
11821238
}
11831239
}
11841240

1241+
1242+
public static class ProviderOfOptionalFieldInjectionBean {
1243+
1244+
@Inject
1245+
private Provider<Optional<TestBean>> testBean;
1246+
1247+
public Optional<TestBean> getTestBean() {
1248+
return this.testBean.get();
1249+
}
1250+
}
1251+
1252+
1253+
public static class ProviderOfOptionalMethodInjectionBean {
1254+
1255+
private Provider<Optional<TestBean>> testBean;
1256+
1257+
@Inject
1258+
public void setTestBean(Provider<Optional<TestBean>> testBeanFactory) {
1259+
this.testBean = testBeanFactory;
1260+
}
1261+
1262+
public Optional<TestBean> getTestBean() {
1263+
return this.testBean.get();
1264+
}
1265+
}
1266+
11851267
}

spring-core/src/main/java/org/springframework/core/MethodParameter.java

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -260,19 +260,21 @@ public Type getGenericParameterType() {
260260
public Class<?> getNestedParameterType() {
261261
if (this.nestingLevel > 1) {
262262
Type type = getGenericParameterType();
263-
if (type instanceof ParameterizedType) {
264-
Integer index = getTypeIndexForCurrentLevel();
265-
Type[] args = ((ParameterizedType) type).getActualTypeArguments();
266-
Type arg = args[index != null ? index : args.length - 1];
263+
for (int i = 2; i <= this.nestingLevel; i++) {
264+
if (type instanceof ParameterizedType) {
265+
Type[] args = ((ParameterizedType) type).getActualTypeArguments();
266+
Integer index = getTypeIndexForLevel(i);
267+
type = args[index != null ? index : args.length - 1];
268+
}
269+
}
270+
if (type instanceof Class) {
271+
return (Class<?>) type;
272+
}
273+
else if (type instanceof ParameterizedType) {
274+
Type arg = ((ParameterizedType) type).getRawType();
267275
if (arg instanceof Class) {
268276
return (Class<?>) arg;
269277
}
270-
else if (arg instanceof ParameterizedType) {
271-
arg = ((ParameterizedType) arg).getRawType();
272-
if (arg instanceof Class) {
273-
return (Class<?>) arg;
274-
}
275-
}
276278
}
277279
return Object.class;
278280
}

0 commit comments

Comments
 (0)