简体   繁体   中英

Spring context environment is null when adding placeholder configurer programmatically

I've seen similar questions and tried many variants, came up with what should work but still having a NullPointerException. This is a web application, here's my AppListener's contextInitialized():

    AnnotationConfigWebApplicationContext wac = new AnnotationConfigWebApplicationContext();
    wac.setServletContext(sc);
    wac.setParent(rootContext);
    propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
    propertySourcesPlaceholderConfigurer.setLocation(new PathResource(_configFile)); // yes it's dynamic
    wac.addBeanFactoryPostProcessor(propertySourcesPlaceholderConfigurer);
    wac.register(Configuration.class);
    sc.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, wac);
    //TODO check if works properly - security. didn't manage to keep it in the same config class
    wac.register(SecurityConfiguration.class);
    wac.refresh();

here's my config class (Configuration.class):

@Autowired //(used to be @Inject, no difference)
private Environment env;

@Bean
public MessageSource messageSource(){
    ReloadableResourceBundleMessageSource ms = new ReloadableResourceBundleMessageSource();
    ms.addBasenames(new String[]{
        env.getProperty("paths.appConfigDir") + "/i18n/message",
        env.getProperty("paths.defaultConfigDir") + "/i18n/message"
    });
    ms.setDefaultEncoding("UTF-8");
    return ms;
}

env is null, thus NPE.

What am I doing wrong?

What is a modern way to do have properties from a file loaded both into placeholders and environment, having properties file name evaluated at startup (basically, taken from another config file)?

What order should be my app context mehtods calls? (Guessing here's my mistake)

Add Let me additionally stress that the properties file name is a variable

Update The correct answer is marked below: don't do weird things or you'll face some gotchas.

Though the answer and the advice is correct, it didn't help me due to other reasons that me as an unexperienced Spring user had no idea that were worth included to the question. Basically, I've got an answer to my question, but I couldn't follow the advice, had to deep debug things and found out the following two items you may consider if you followed my route:

The config class was instantiated too early and thus lacked the Environment injected, because:

1) Don't call your configuration class "Configuration". During the initialization phase Something in Spring Web tries to get the bean called "configuration", sees this class and instantiates it.

2) Move messageSource bean to a parent context, as it is sought early in Spring Web initialization; it seems impossible to query the Environment in the messageSource bean method, there's no Environment yet.

Hope this helps.

Add below mentioned annotation in your Configuration class:

1. @PropertySource

Working code will be like:

 @Configuration @PropertySource("classpath:your-property-file.properties") public class Config { @Autowired private Environment env; @Bean(name="testSource") public MessageSource messageSource(){ ReloadableResourceBundleMessageSource ms = new ReloadableResourceBundleMessageSource(); ms.addBasenames(new String[]{ env.getProperty("paths.appConfigDir") + "/i18n/message", env.getProperty("paths.defaultConfigDir") + "/i18n/message" }); ms.setDefaultEncoding("UTF-8"); return ms; } } 

To modify the PropertySource instances used by the Environment use an ApplicationContextInitializer . This allows you to add PropertySource instances before the ApplicationContext is actually created.

public class YourApplicationContextInitializer implements ApplicationContextInitializer {

    public void initialize(ConfigurableApplicationContext context) {

        Resource resource = new PathResource(_configFile);
        ConfigurableEnvironment env = context.getEnvironment();
        MutablePropertySources mps = env.getPropertySources();
        mps.addFirst(new ResourcePropertySource("config-file", resource));
    }
}

This class will add your configured PathResource as the first PropertySource in the Environment and will also be used by the, presumably, already available PropertySourcesPlaceholderConfigurer .

Assuming that you have a WebApplicationInitializer which extends AbstractAnnotationConfigDispatcherServletInitializer implement the getRootApplicationContextInitializers and getServletApplicationContextInitializers to return an instance of this class.

public class YourWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
   // Your other init code here

    protected ApplicationContextInitializer<?>[] getServletApplicationContextInitializers() {
        return new ApplicationContextInitializer[] { new YourApplicationContextInitializer()};
    } 

    protected ApplicationContextInitializer<?>[] getRootApplicationContextInitializers() {
        return new ApplicationContextInitializer[] { new YourApplicationContextInitializer()};
    } 

}

The getRootApplicationContextInitializers will add ApplictionContexInitializer for the context loaded by the ContextLoaderListener the getServletApplicationContextInitializers will do the same for the DispatcherServlet .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM