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 b43c0f87f7..68096d9df2 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 @@ -36,6 +36,7 @@ import java.util.IdentityHashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import javax.inject.Provider; @@ -108,6 +109,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto private static Class javaxInjectProviderClass = null; + private static Class javaUtilOptionalClass = null; + static { try { javaxInjectProviderClass = @@ -116,6 +119,13 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto catch (ClassNotFoundException ex) { // JSR-330 API not available - Provider interface simply not supported then. } + try { + javaUtilOptionalClass = + ClassUtils.forName("java.util.Optional", DefaultListableBeanFactory.class.getClassLoader()); + } + catch (ClassNotFoundException ex) { + // Java 8 not available - Optional references simply not supported then. + } } @@ -854,6 +864,9 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto else if (descriptor.getDependencyType().equals(javaxInjectProviderClass)) { return new DependencyProviderFactory().createDependencyProvider(descriptor, beanName); } + else if (descriptor.getDependencyType().equals(javaUtilOptionalClass)) { + return new OptionalDependencyFactory().createOptionalDependency(descriptor, beanName); + } else { Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(descriptor, beanName); if (result == null) { @@ -1319,4 +1332,22 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto } } + + /** + * Separate inner class for avoiding a hard dependency on the {@code javax.inject} API. + */ + private class OptionalDependencyFactory { + + public Object createOptionalDependency(DependencyDescriptor descriptor, String beanName) { + DependencyDescriptor descriptorToUse = new DependencyDescriptor(descriptor) { + @Override + public boolean isRequired() { + return false; + } + }; + descriptorToUse.increaseNestingLevel(); + return Optional.ofNullable(doResolveDependency(descriptorToUse, beanName, null, null)); + } + } + } diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/InjectAnnotationBeanPostProcessorTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/InjectAnnotationBeanPostProcessorTests.java index a18a25e05a..4836140087 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/InjectAnnotationBeanPostProcessorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/InjectAnnotationBeanPostProcessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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. @@ -19,6 +19,7 @@ package org.springframework.beans.factory.annotation; import java.io.Serializable; import java.util.List; import java.util.Map; +import java.util.Optional; import javax.inject.Inject; import javax.inject.Named; import javax.inject.Provider; @@ -573,6 +574,62 @@ public class InjectAnnotationBeanPostProcessorTests { bf.destroySingletons(); } + @Test + public void testOptionalFieldInjectionWithBeanAvailable() { + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); + bpp.setBeanFactory(bf); + bf.addBeanPostProcessor(bpp); + bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(OptionalFieldInjectionBean.class)); + bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class)); + + OptionalFieldInjectionBean bean = (OptionalFieldInjectionBean) bf.getBean("annotatedBean"); + assertTrue(bean.getTestBean().isPresent()); + assertSame(bf.getBean("testBean"), bean.getTestBean().get()); + bf.destroySingletons(); + } + + @Test + public void testOptionalFieldInjectionWithBeanNotAvailable() { + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); + bpp.setBeanFactory(bf); + bf.addBeanPostProcessor(bpp); + bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(OptionalFieldInjectionBean.class)); + + OptionalFieldInjectionBean bean = (OptionalFieldInjectionBean) bf.getBean("annotatedBean"); + assertFalse(bean.getTestBean().isPresent()); + bf.destroySingletons(); + } + + @Test + public void testOptionalMethodInjectionWithBeanAvailable() { + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); + bpp.setBeanFactory(bf); + bf.addBeanPostProcessor(bpp); + bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(OptionalMethodInjectionBean.class)); + bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class)); + + OptionalMethodInjectionBean bean = (OptionalMethodInjectionBean) bf.getBean("annotatedBean"); + assertTrue(bean.getTestBean().isPresent()); + assertSame(bf.getBean("testBean"), bean.getTestBean().get()); + bf.destroySingletons(); + } + + @Test + public void testOptionalMethodInjectionWithBeanNotAvailable() { + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); + bpp.setBeanFactory(bf); + bf.addBeanPostProcessor(bpp); + bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(OptionalMethodInjectionBean.class)); + + OptionalMethodInjectionBean bean = (OptionalMethodInjectionBean) bf.getBean("annotatedBean"); + assertFalse(bean.getTestBean().isPresent()); + bf.destroySingletons(); + } + public static class ResourceInjectionBean { @@ -1099,4 +1156,30 @@ public class InjectAnnotationBeanPostProcessorTests { } } + + public static class OptionalFieldInjectionBean { + + @Inject + private Optional testBean; + + public Optional getTestBean() { + return this.testBean; + } + } + + + public static class OptionalMethodInjectionBean { + + private Optional testBean; + + @Inject + public void setTestBean(Optional testBeanFactory) { + this.testBean = testBeanFactory; + } + + public Optional getTestBean() { + return this.testBean; + } + } + }