diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java index 5d8d82b14c..fd38f364cc 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.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. @@ -263,18 +263,20 @@ public class DependencyDescriptor implements Serializable { if (this.field != null) { if (this.nestingLevel > 1) { Type type = this.field.getGenericType(); - if (type instanceof ParameterizedType) { - Type[] args = ((ParameterizedType) type).getActualTypeArguments(); - Type arg = args[args.length - 1]; + for (int i = 2; i <= this.nestingLevel; i++) { + if (type instanceof ParameterizedType) { + Type[] args = ((ParameterizedType) type).getActualTypeArguments(); + type = args[args.length - 1]; + } + } + if (type instanceof Class) { + return (Class) type; + } + else if (type instanceof ParameterizedType) { + Type arg = ((ParameterizedType) type).getRawType(); if (arg instanceof Class) { return (Class) arg; } - else if (arg instanceof ParameterizedType) { - arg = ((ParameterizedType) arg).getRawType(); - if (arg instanceof Class) { - return (Class) arg; - } - } } return Object.class; } 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 68096d9df2..ef1a6d267e 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 @@ -64,6 +64,7 @@ import org.springframework.beans.factory.config.DependencyDescriptor; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.annotation.OrderProviderComparator; import org.springframework.core.annotation.OrderUtils; +import org.springframework.lang.UsesJava8; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; @@ -107,18 +108,11 @@ import org.springframework.util.StringUtils; public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable { - private static Class javaxInjectProviderClass = null; - private static Class javaUtilOptionalClass = null; + private static Class javaxInjectProviderClass = null; + static { - try { - javaxInjectProviderClass = - ClassUtils.forName("javax.inject.Provider", DefaultListableBeanFactory.class.getClassLoader()); - } - catch (ClassNotFoundException ex) { - // JSR-330 API not available - Provider interface simply not supported then. - } try { javaUtilOptionalClass = ClassUtils.forName("java.util.Optional", DefaultListableBeanFactory.class.getClassLoader()); @@ -126,6 +120,13 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto catch (ClassNotFoundException ex) { // Java 8 not available - Optional references simply not supported then. } + try { + javaxInjectProviderClass = + ClassUtils.forName("javax.inject.Provider", DefaultListableBeanFactory.class.getClassLoader()); + } + catch (ClassNotFoundException ex) { + // JSR-330 API not available - Provider interface simply not supported then. + } } @@ -858,15 +859,15 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto Set autowiredBeanNames, TypeConverter typeConverter) throws BeansException { descriptor.initParameterNameDiscovery(getParameterNameDiscoverer()); - if (descriptor.getDependencyType().equals(ObjectFactory.class)) { + if (descriptor.getDependencyType().equals(javaUtilOptionalClass)) { + return new OptionalDependencyFactory().createOptionalDependency(descriptor, beanName); + } + else if (descriptor.getDependencyType().equals(ObjectFactory.class)) { return new DependencyObjectFactory(descriptor, beanName); } 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) { @@ -1284,6 +1285,25 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto } + /** + * Separate inner class for avoiding a hard dependency on the {@code javax.inject} API. + */ + @UsesJava8 + 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)); + } + } + + /** * Serializable ObjectFactory for lazy resolution of a dependency. */ @@ -1291,17 +1311,25 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto private final DependencyDescriptor descriptor; + private final boolean optional; + private final String beanName; public DependencyObjectFactory(DependencyDescriptor descriptor, String beanName) { this.descriptor = new DependencyDescriptor(descriptor); this.descriptor.increaseNestingLevel(); + this.optional = this.descriptor.getDependencyType().equals(javaUtilOptionalClass); this.beanName = beanName; } @Override public Object getObject() throws BeansException { - return doResolveDependency(this.descriptor, this.beanName, null, null); + if (this.optional) { + return new OptionalDependencyFactory().createOptionalDependency(this.descriptor, this.beanName); + } + else { + return doResolveDependency(this.descriptor, this.beanName, null, null); + } } } @@ -1332,22 +1360,4 @@ 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 4836140087..3e210ece59 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 @@ -630,6 +630,62 @@ public class InjectAnnotationBeanPostProcessorTests { bf.destroySingletons(); } + @Test + public void testProviderOfOptionalFieldInjectionWithBeanAvailable() { + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); + bpp.setBeanFactory(bf); + bf.addBeanPostProcessor(bpp); + bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ProviderOfOptionalFieldInjectionBean.class)); + bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class)); + + ProviderOfOptionalFieldInjectionBean bean = (ProviderOfOptionalFieldInjectionBean) bf.getBean("annotatedBean"); + assertTrue(bean.getTestBean().isPresent()); + assertSame(bf.getBean("testBean"), bean.getTestBean().get()); + bf.destroySingletons(); + } + + @Test + public void testProviderOfOptionalFieldInjectionWithBeanNotAvailable() { + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); + bpp.setBeanFactory(bf); + bf.addBeanPostProcessor(bpp); + bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ProviderOfOptionalFieldInjectionBean.class)); + + ProviderOfOptionalFieldInjectionBean bean = (ProviderOfOptionalFieldInjectionBean) bf.getBean("annotatedBean"); + assertFalse(bean.getTestBean().isPresent()); + bf.destroySingletons(); + } + + @Test + public void testProviderOfOptionalMethodInjectionWithBeanAvailable() { + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); + bpp.setBeanFactory(bf); + bf.addBeanPostProcessor(bpp); + bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ProviderOfOptionalMethodInjectionBean.class)); + bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class)); + + ProviderOfOptionalMethodInjectionBean bean = (ProviderOfOptionalMethodInjectionBean) bf.getBean("annotatedBean"); + assertTrue(bean.getTestBean().isPresent()); + assertSame(bf.getBean("testBean"), bean.getTestBean().get()); + bf.destroySingletons(); + } + + @Test + public void testProviderOfOptionalMethodInjectionWithBeanNotAvailable() { + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); + bpp.setBeanFactory(bf); + bf.addBeanPostProcessor(bpp); + bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ProviderOfOptionalMethodInjectionBean.class)); + + ProviderOfOptionalMethodInjectionBean bean = (ProviderOfOptionalMethodInjectionBean) bf.getBean("annotatedBean"); + assertFalse(bean.getTestBean().isPresent()); + bf.destroySingletons(); + } + public static class ResourceInjectionBean { @@ -1182,4 +1238,30 @@ public class InjectAnnotationBeanPostProcessorTests { } } + + public static class ProviderOfOptionalFieldInjectionBean { + + @Inject + private Provider> testBean; + + public Optional getTestBean() { + return this.testBean.get(); + } + } + + + public static class ProviderOfOptionalMethodInjectionBean { + + private Provider> testBean; + + @Inject + public void setTestBean(Provider> testBeanFactory) { + this.testBean = testBeanFactory; + } + + public Optional getTestBean() { + return this.testBean.get(); + } + } + } diff --git a/spring-core/src/main/java/org/springframework/core/MethodParameter.java b/spring-core/src/main/java/org/springframework/core/MethodParameter.java index 0431def134..c3b1576e68 100644 --- a/spring-core/src/main/java/org/springframework/core/MethodParameter.java +++ b/spring-core/src/main/java/org/springframework/core/MethodParameter.java @@ -260,19 +260,21 @@ public class MethodParameter { public Class getNestedParameterType() { if (this.nestingLevel > 1) { Type type = getGenericParameterType(); - if (type instanceof ParameterizedType) { - Integer index = getTypeIndexForCurrentLevel(); - Type[] args = ((ParameterizedType) type).getActualTypeArguments(); - Type arg = args[index != null ? index : args.length - 1]; + for (int i = 2; i <= this.nestingLevel; i++) { + if (type instanceof ParameterizedType) { + Type[] args = ((ParameterizedType) type).getActualTypeArguments(); + Integer index = getTypeIndexForLevel(i); + type = args[index != null ? index : args.length - 1]; + } + } + if (type instanceof Class) { + return (Class) type; + } + else if (type instanceof ParameterizedType) { + Type arg = ((ParameterizedType) type).getRawType(); if (arg instanceof Class) { return (Class) arg; } - else if (arg instanceof ParameterizedType) { - arg = ((ParameterizedType) arg).getRawType(); - if (arg instanceof Class) { - return (Class) arg; - } - } } return Object.class; }