Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to wire a value to a field in Spring?

Tags:

java

spring

I defined a bean in Spring configuration file,

 <bean id="accountConfigFile" class="java.lang.String">
    <constructor-arg index="0" type="java.lang.String" value="/account.properties"/>
</bean>

I then wire this bean to a field in AccountHelper Class:

@Autowired
@Qualifier("accountConfigFile")
private static String configFilename;

However, when I tried to access it in the constructor, I got NullPointerException, because it is null:

public Class AccountHelper {

    private Properties properties;

    @Autowired
    @Qualifier("accountConfigFile")
    private static String configFilename;

    public AccountHelper() {
        properties = new Properties();
        InputStream is = null;

        try
        {
            is = getClass().getResourceAsStream(configFilename);
            properties.load(is);
            is.close();

        } catch (Exception e)
        {
                    ......
        }
    }
    ......

}

Can anyone help me figure out why this happens?

Many thanks!

like image 764
kevin Avatar asked Nov 04 '22 08:11

kevin


2 Answers

It seems that you are injecting a value into a static field, which is not supported by Spring. If you really want to do this, you can follow the steps in Spring: How to inject a value to static field?. But I think it is not a good idea to autowire a config file name in the AccountHelper class. It will be better to inject the properties object into the AccountHelper class since the properties can be constructed in the same Spring configuration file. But if your AccountHelp class just consumes the property nodes in the properties file, you can just inject the value which is read from the properties file into the class using setter. And the Spring config file will be like

<bean id="propertyConfigurer"  
      class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  
    <property name="location" value="/account.properties"/>  
</bean>
<bean class="your.package.AccountHelper">
    <property name="foobar" value="node.name.in.the.properties"/>
</bean>

And the AccountHelper class will be like

public AccountHelper {
    private String foobar;
    public void setFoobar(String foobar) {
        this.foobar = foobar;
    }
    ...other methods
}
like image 110
Wenhao Ji Avatar answered Nov 08 '22 06:11

Wenhao Ji


First of all, as said by @rAy, don't inject to static field. Honestly that's quite meaningless.

Back to your question (assume you changed the field to non-static). The whole problem is not (yet) related to Auto-wiring. As quite some answers already pointed out, in Spring, an instance of bean will be first created, and then field/setter injection happens afterwards. Therefore, your constructor run before you have injected the field it used.

There are a lot of ways to solve this issue. I am listing some of the most common options (I bet there are still more ways), try to understand them and choose one suit your design.

Constructor Injection

Instead of injecting to field, you can choose to inject through constructor. Have your AccountHelper look something like:

public Class AccountHelper {

    private String configFilename;

    public AccountHelper(String configFilename) {
        this.configFilename = configFilename;
        // do something base on configFilename
    }
}

You can use auto-wiring for the constructor argument, or declare explicitly in XML (from your question, it seems to me you know how to do it)

Initialization method

In Spring, there are tons of way to have an initialization method after all properties are set. So, you can do something like:

public Class AccountHelper implements InitializingBean{

    private String configFilename;
    // corresponding setters etc

    public AccountHelper() {
        // don't do initialization in ctor
    }

    @Override
    public void afterPropertiesSet() {
        // called after property injections finished
        // do something base on configFilename
    }
}

or you can even have a method in any name you like, and tell Spring you want to use it as initialization method, by annotating with @PostConstruct, or by init-method in XML, or by initMethodName in @Bean, etc.

Factory Bean

If you don't have much control on the way your AccountHelper is created, and the creation process may be difficult to perform by plain Spring config, you can always write a Factory Bean to create the bean, with the construction logic hand-crafted in the Factory Bean.


After you make the thing work without auto-wiring, adding auto-wiring is just a piece of cake.

like image 24
Adrian Shum Avatar answered Nov 08 '22 06:11

Adrian Shum