Skip to content

Commit 7d03daf

Browse files
committed
Support for Java 8's java.util.Optional at injection points
Issue: SPR-11833
1 parent 6aa9c40 commit 7d03daf

File tree

2 files changed

+115
-1
lines changed

2 files changed

+115
-1
lines changed

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import java.util.LinkedHashMap;
3737
import java.util.List;
3838
import java.util.Map;
39+
import java.util.Optional;
3940
import java.util.Set;
4041
import java.util.concurrent.ConcurrentHashMap;
4142
import javax.inject.Provider;
@@ -108,6 +109,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
108109

109110
private static Class<?> javaxInjectProviderClass = null;
110111

112+
private static Class<?> javaUtilOptionalClass = null;
113+
111114
static {
112115
try {
113116
javaxInjectProviderClass =
@@ -116,6 +119,13 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
116119
catch (ClassNotFoundException ex) {
117120
// JSR-330 API not available - Provider interface simply not supported then.
118121
}
122+
try {
123+
javaUtilOptionalClass =
124+
ClassUtils.forName("java.util.Optional", DefaultListableBeanFactory.class.getClassLoader());
125+
}
126+
catch (ClassNotFoundException ex) {
127+
// Java 8 not available - Optional references simply not supported then.
128+
}
119129
}
120130

121131

@@ -854,6 +864,9 @@ public Object resolveDependency(DependencyDescriptor descriptor, String beanName
854864
else if (descriptor.getDependencyType().equals(javaxInjectProviderClass)) {
855865
return new DependencyProviderFactory().createDependencyProvider(descriptor, beanName);
856866
}
867+
else if (descriptor.getDependencyType().equals(javaUtilOptionalClass)) {
868+
return new OptionalDependencyFactory().createOptionalDependency(descriptor, beanName);
869+
}
857870
else {
858871
Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(descriptor, beanName);
859872
if (result == null) {
@@ -1319,4 +1332,22 @@ public Object createDependencyProvider(DependencyDescriptor descriptor, String b
13191332
}
13201333
}
13211334

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+
13221353
}

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

Lines changed: 84 additions & 1 deletion
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.
@@ -19,6 +19,7 @@
1919
import java.io.Serializable;
2020
import java.util.List;
2121
import java.util.Map;
22+
import java.util.Optional;
2223
import javax.inject.Inject;
2324
import javax.inject.Named;
2425
import javax.inject.Provider;
@@ -573,6 +574,62 @@ public void testBeanAutowiredWithFactoryBean() {
573574
bf.destroySingletons();
574575
}
575576

577+
@Test
578+
public void testOptionalFieldInjectionWithBeanAvailable() {
579+
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
580+
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
581+
bpp.setBeanFactory(bf);
582+
bf.addBeanPostProcessor(bpp);
583+
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(OptionalFieldInjectionBean.class));
584+
bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class));
585+
586+
OptionalFieldInjectionBean bean = (OptionalFieldInjectionBean) bf.getBean("annotatedBean");
587+
assertTrue(bean.getTestBean().isPresent());
588+
assertSame(bf.getBean("testBean"), bean.getTestBean().get());
589+
bf.destroySingletons();
590+
}
591+
592+
@Test
593+
public void testOptionalFieldInjectionWithBeanNotAvailable() {
594+
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
595+
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
596+
bpp.setBeanFactory(bf);
597+
bf.addBeanPostProcessor(bpp);
598+
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(OptionalFieldInjectionBean.class));
599+
600+
OptionalFieldInjectionBean bean = (OptionalFieldInjectionBean) bf.getBean("annotatedBean");
601+
assertFalse(bean.getTestBean().isPresent());
602+
bf.destroySingletons();
603+
}
604+
605+
@Test
606+
public void testOptionalMethodInjectionWithBeanAvailable() {
607+
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
608+
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
609+
bpp.setBeanFactory(bf);
610+
bf.addBeanPostProcessor(bpp);
611+
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(OptionalMethodInjectionBean.class));
612+
bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class));
613+
614+
OptionalMethodInjectionBean bean = (OptionalMethodInjectionBean) bf.getBean("annotatedBean");
615+
assertTrue(bean.getTestBean().isPresent());
616+
assertSame(bf.getBean("testBean"), bean.getTestBean().get());
617+
bf.destroySingletons();
618+
}
619+
620+
@Test
621+
public void testOptionalMethodInjectionWithBeanNotAvailable() {
622+
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
623+
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
624+
bpp.setBeanFactory(bf);
625+
bf.addBeanPostProcessor(bpp);
626+
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(OptionalMethodInjectionBean.class));
627+
628+
OptionalMethodInjectionBean bean = (OptionalMethodInjectionBean) bf.getBean("annotatedBean");
629+
assertFalse(bean.getTestBean().isPresent());
630+
bf.destroySingletons();
631+
}
632+
576633

577634
public static class ResourceInjectionBean {
578635

@@ -1099,4 +1156,30 @@ public boolean isSingleton() {
10991156
}
11001157
}
11011158

1159+
1160+
public static class OptionalFieldInjectionBean {
1161+
1162+
@Inject
1163+
private Optional<TestBean> testBean;
1164+
1165+
public Optional<TestBean> getTestBean() {
1166+
return this.testBean;
1167+
}
1168+
}
1169+
1170+
1171+
public static class OptionalMethodInjectionBean {
1172+
1173+
private Optional<TestBean> testBean;
1174+
1175+
@Inject
1176+
public void setTestBean(Optional<TestBean> testBeanFactory) {
1177+
this.testBean = testBeanFactory;
1178+
}
1179+
1180+
public Optional<TestBean> getTestBean() {
1181+
return this.testBean;
1182+
}
1183+
}
1184+
11021185
}

0 commit comments

Comments
 (0)