From 0113ea91a3ba17dadab7d6321feb0dcf2d31ba99 Mon Sep 17 00:00:00 2001 From: Chris Beams Date: Thu, 24 Nov 2011 21:39:58 +0000 Subject: [PATCH] Support by-type lookup/injection of primitive types Allowing beans of primitive type to be looked up via getBean(Class), or to be injected using @Autowired or @Injected or @Resource. Prior to these changes, an attempt to lookup or inject a bean of, for example, type boolean would fail because all spring beans are Objects, regardless of initial type due to the way that ObjectFactory works. Now these attempts to lookup or inject primitive types work, thanks to simple changes in AbstractBeanFactory using ClassUtils#isAssignable methods instead of the built-in Class#isAssignableFrom. The former takes into account primitives and their object wrapper types, whereas the latter does not. The need to declare, look up or inject primitive-typed beans is probably low -- how often does one need a bean of type boolean or int after all?. Prior to the introduction of @Bean methods in Spring 3.0, it was not possible in practice to register primitive beans, so this issue never came up. Now that one can declare primitive-typed beans, it does make sense that we properly support by-type lookup and injection without forcing the user to work with object wrappers. Issue: SPR-8874 --- .../factory/support/AbstractBeanFactory.java | 6 +- ...PrimitiveBeanLookupAndAutowiringTests.java | 107 ++++++++++++++++++ 2 files changed, 110 insertions(+), 3 deletions(-) create mode 100644 org.springframework.context/src/test/java/org/springframework/context/annotation/PrimitiveBeanLookupAndAutowiringTests.java diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java index 47e7ad7823..145259abe1 100644 --- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java +++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java @@ -469,15 +469,15 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp if (beanInstance instanceof FactoryBean) { if (!BeanFactoryUtils.isFactoryDereference(name)) { Class type = getTypeForFactoryBean((FactoryBean) beanInstance); - return (type != null && typeToMatch.isAssignableFrom(type)); + return (type != null && ClassUtils.isAssignable(typeToMatch, type)); } else { - return typeToMatch.isAssignableFrom(beanInstance.getClass()) ; + return ClassUtils.isAssignableValue(typeToMatch, beanInstance); } } else { return !BeanFactoryUtils.isFactoryDereference(name) && - typeToMatch.isAssignableFrom(beanInstance.getClass()); + ClassUtils.isAssignableValue(typeToMatch, beanInstance); } } else if (containsSingleton(beanName) && !containsBeanDefinition(beanName)) { diff --git a/org.springframework.context/src/test/java/org/springframework/context/annotation/PrimitiveBeanLookupAndAutowiringTests.java b/org.springframework.context/src/test/java/org/springframework/context/annotation/PrimitiveBeanLookupAndAutowiringTests.java new file mode 100644 index 0000000000..10c0316165 --- /dev/null +++ b/org.springframework.context/src/test/java/org/springframework/context/annotation/PrimitiveBeanLookupAndAutowiringTests.java @@ -0,0 +1,107 @@ +/* + * Copyright 2002-2011 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.context.annotation; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertThat; + +import javax.annotation.Resource; + +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Tests changes introduced for SPR-8874, allowing beans of primitive types to be looked + * up via getBean(Class), or to be injected using @Autowired or @Injected or @Resource. + * Prior to these changes, an attempt to lookup or inject a bean of type boolean would + * fail because all spring beans are Objects, regardless of initial type due to the way + * that ObjectFactory works. + * + * Now these attempts to lookup or inject primitive types work, thanks to simple changes + * in AbstractBeanFactory using ClassUtils#isAssignable methods instead of the built-in + * Class#isAssignableFrom. The former takes into account primitives and their object + * wrapper types, whereas the latter does not. + * + * @author Chris Beams + * @since 3.1 + */ +public class PrimitiveBeanLookupAndAutowiringTests { + + @Test + public void primitiveLookupByName() { + ApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class); + boolean b = ctx.getBean("b", boolean.class); + assertThat(b, equalTo(true)); + int i = ctx.getBean("i", int.class); + assertThat(i, equalTo(42)); + } + + @Test + public void primitiveLookupByType() { + ApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class); + boolean b = ctx.getBean(boolean.class); + assertThat(b, equalTo(true)); + int i = ctx.getBean(int.class); + assertThat(i, equalTo(42)); + } + + @Test + public void primitiveAutowiredInjection() { + ApplicationContext ctx = + new AnnotationConfigApplicationContext(Config.class, AutowiredComponent.class); + assertThat(ctx.getBean(AutowiredComponent.class).b, equalTo(true)); + assertThat(ctx.getBean(AutowiredComponent.class).i, equalTo(42)); + } + + @Test + public void primitiveResourceInjection() { + ApplicationContext ctx = + new AnnotationConfigApplicationContext(Config.class, ResourceComponent.class); + assertThat(ctx.getBean(ResourceComponent.class).b, equalTo(true)); + assertThat(ctx.getBean(ResourceComponent.class).i, equalTo(42)); + } + + + @Configuration + static class Config { + @Bean + public boolean b() { + return true; + } + + @Bean + public int i() { + return 42; + } + } + + + static class AutowiredComponent { + @Autowired boolean b; + @Autowired int i; + } + + + static class ResourceComponent { + @Resource boolean b; + @Autowired int i; + } +}