Support for Java 8's java.util.Optional at injection points

Issue: SPR-11833
master
Juergen Hoeller 10 years ago
parent 6aa9c40552
commit 7d03daf8cb
  1. 31
      spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java
  2. 85
      spring-beans/src/test/java/org/springframework/beans/factory/annotation/InjectAnnotationBeanPostProcessorTests.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));
}
}
}

@ -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> testBean;
public Optional<TestBean> getTestBean() {
return this.testBean;
}
}
public static class OptionalMethodInjectionBean {
private Optional<TestBean> testBean;
@Inject
public void setTestBean(Optional<TestBean> testBeanFactory) {
this.testBean = testBeanFactory;
}
public Optional<TestBean> getTestBean() {
return this.testBean;
}
}
}

Loading…
Cancel
Save