5

My goal I want to achieve is to inject enum to Spring managed bean.

I do not want my bean be configured in XML (there no reason to do so except this not-working enum).

I have simple maven project:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>net.betlista</groupId>
    <artifactId>test.spring.enum-injection</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>3.1.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>3.1.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.5.4</version>
        </dependency>

        <!-- testing -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>3.1.3.RELEASE</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <properties>
        <junit.version>4.11</junit.version>
    </properties>
</project>

I created simple test

import net.betlista.spring.enum_injection.EnumInjectionComponent;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestClass {

    @Autowired
    EnumInjectionComponent component;

    @Test
    public void test() {
        Assert.assertNotNull(component.roundingMode);
    }

}

and my bean is

package net.betlista.spring.enum_injection;

import java.math.RoundingMode;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class EnumInjectionComponent {

    @Autowired
    public RoundingMode roundingMode;

}

When I'm trying to run my test Ipm getting

java.lang.IllegalStateException: Failed to load ApplicationContext
    at org.springframework.test.context.TestContext.getApplicationContext(TestContext.java:157)
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:109)
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:75)
    at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:321)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:211)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:288)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:290)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'enumInjectionComponent': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: public java.math.RoundingMode net.betlista.spring.enum_injection.EnumInjectionComponent.roundingMode; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [java.math.RoundingMode] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:287)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1106)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:517)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:294)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:225)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:291)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:605)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:925)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:472)
    at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:103)
    at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:1)
    at org.springframework.test.context.support.DelegatingSmartContextLoader.loadContext(DelegatingSmartContextLoader.java:228)
    at org.springframework.test.context.TestContext.loadApplicationContext(TestContext.java:124)
    at org.springframework.test.context.TestContext.getApplicationContext(TestContext.java:148)
    ... 24 more
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: public java.math.RoundingMode net.betlista.spring.enum_injection.EnumInjectionComponent.roundingMode; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [java.math.RoundingMode] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:513)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:92)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:284)
    ... 39 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [java.math.RoundingMode] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:948)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:817)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:731)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:485)
    ... 41 more

But in log I can see:

INFO: Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@236527f: defining beans [org.springframework.context.config.internalBeanConfigurerAspect,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,enumInjectionComponent,rounding,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor]; root of factory hierarchy

So I have rounding bean in context.

What I missed?

edit: I just found, the problem is with Spring version 3.1.x only, in 3.2.x it works, any workaround available?

edit2: here is definition of beans

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
    ">

    <context:spring-configured />
    <context:annotation-config />
    <context:component-scan base-package="net.betlista"/>

    <bean id="rounding" class="java.math.RoundingMode" factory-method="valueOf">
        <constructor-arg value="UP" />
    </bean>

</beans>

edit3:

as workaround I used Dev Blanked's workaround to specify the list, and use @Value in my bean as

@Value("#{roundingModes[0]}")
public RoundingMode roundingMode;

if there is a better (nicer) solution I'd like to see it ;-)

Charles
  • 50,943
  • 13
  • 104
  • 142
Betlista
  • 10,327
  • 13
  • 69
  • 110
  • Can you please clarify the question? Are you trying to inject a `RoundingMode` enum value you have defined somewhere? Trying to inject the Enum itself doesn't make sense. – Peter Mularien Mar 15 '13 at 18:03
  • No need to `pom.xml` file here, paste the bean xml file instead. – Abimaran Kugathasan Mar 15 '13 at 18:08
  • context config added @PeterMularien why it doesn't make sense? I want to have rounding configurable... – Betlista Mar 15 '13 at 18:15
  • 2
    Possible duplicate of [this question](http://stackoverflow.com/questions/516771/how-assign-beans-property-an-enum-value-in-spring-config-file) – codelark Mar 15 '13 at 18:22
  • 1
    @Betlista: The question is clearer now that you posted the Spring configuration - thanks! Have you tried moving the `` declaration for the enum up in the context configuration, before the ``? – Peter Mularien Mar 15 '13 at 19:38
  • @codelark gave you a reference to the right answer:http://stackoverflow.com/a/11443028/655756 – n1ckolas Mar 15 '13 at 20:50
  • I saw that question before I tried it and according to log above, `rounding` bean is correctly in context, AFAIK there is no problem with rounding bean initialization, just with injection to component... – Betlista Mar 15 '13 at 21:07
  • @Charles why did you remove spring-3.1 tag when there is written in question that it works fine in 3.2, but it's not working in 3.1 ? – Betlista Mar 19 '13 at 10:51
  • 1
    @Betlista, as noted in the edit description, the existing [tag:spring-3] tag very expressly covers *all* 3.x versions of Spring. We don't need a new version-specific tag here. – Charles Mar 19 '13 at 17:36
  • @Charles Hm :-/ Probably I'm missing something, because as spring-3 covers ALL 3.x version and this problem is NOT in 3.2.x version, this tagging is not correct. On the other hand Spring 3.1 is Spring-3 that's why I used this tag too, for example when someone has Spring-3 in favorite tags... – Betlista Mar 19 '13 at 17:42
  • Moving `` before `` solved my problem. Thanks @PeterMularien – Filipiz Aug 18 '14 at 22:24

2 Answers2

1

It should be possible to provide the bare enum name and specify the value-type. below is an example where the rounding mode has been injected to a list. should be the same for a property as well.

<util:list id="roundingModes" value-type="java.math.RoundingMode">
    <value>UP</value>
</util:list>
Betlista
  • 10,327
  • 13
  • 69
  • 110
Dev Blanked
  • 8,555
  • 3
  • 26
  • 32
  • 1
    As far as there is not better solution now I'm accepting this woraround (description how I used it is added in question - edit3) – Betlista Mar 19 '13 at 10:52
  • value-type can be specified for a property in a bean as well. So if there is a bean having a property called 'roundingMode' it should be possible to inject the value directly by using the value-type – Dev Blanked Mar 19 '13 at 12:01
  • I think I do not understand, because my bean is not in XML configuration - component scan is used... – Betlista Mar 19 '13 at 15:35
1

Another approach one can use is to specify UP as String and in bean implement InitializingBean interface and create the enum in afterPropertiesSet() from that String.

@Override
public void afterPropertiesSet() throws Exception {
    roundingMode = RoundingMode.valueOf(roundingModeName);
}
Betlista
  • 10,327
  • 13
  • 69
  • 110