Spring : Add properties file externally

2019-08-22 12:20发布

I am working on a Spring-MVC application in which we are preparing to setup application on different servers. As each server can have it's own database related configuration, we are hoping to use an external properties file(outside the war file) which can be read while project is starting. How do I go about this approach? For making it work, I have already moved application initialization code to Java, this way, static XML reading which we had before won't be required. But, we are unsure how to create and add a properties file dynamically which atleast has these 3 values(JDBC URL containing DB name, username, password). All tables, other data will be created automatically.

Here is the class where app is initialized :

WebConfig.java :

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = {"com.ourapp.spring"})
@EnableTransactionManagement
@EnableCaching
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
    }


    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    }

    @Bean
    public ReloadableResourceBundleMessageSource messageSource(){
        ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
        messageSource.setBasename("../resources/locale/messages.properties");
        messageSource.setDefaultEncoding("UTF-8");
        return messageSource;
    }

    @Bean
    public LocaleChangeInterceptor localeInterceptor(){
        LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor();
        interceptor.setParamName("lang");
        return interceptor;
    }

    @Bean
    public MappingJackson2HttpMessageConverter converter() {
        return new MappingJackson2HttpMessageConverter();
    }

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/css/**").addResourceLocations("/css/");
        registry.addResourceHandler("/img/**").addResourceLocations("/img/");
        registry.addResourceHandler("/js/**").addResourceLocations("/js/");
    }

    @Bean
    public InternalResourceViewResolver getInternalResourceViewResolver() {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("../webapp/WEB-INF/views/");
        resolver.setSuffix(".jsp");
        return resolver;
    }

    @Bean
    public DoNotTruncateMyUrls doNotTruncate(){
       return new DoNotTruncateMyUrls();
    }

    @Bean
    public MultipartResolver multipartResolver() {
        return new StandardServletMultipartResolver();
    }

    @Bean
    @Qualifier("primary_tx")
    public HibernateTransactionManager getPrimaryTransactionManager() throws IOException {
        HibernateTransactionManager txName= new HibernateTransactionManager();
        txName.setSessionFactory(sessionFactory().getObject());
        return txName;
    }

    @Bean
    @Qualifier("extended_tx")
    public HibernateTransactionManager txName() throws IOException {
        HibernateTransactionManager txName= new HibernateTransactionManager();
        txName.setSessionFactory(getExtendedSessionFactory().getObject());
        return txName;
    }

    @Bean
    @Qualifier("sessionFactory_origin")
    public LocalSessionFactoryBean sessionFactory() {
        LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
        sessionFactory.setDataSource(new DataSourceConfig().primaryDataSource());
        sessionFactory.setPackagesToScan("com.ourapp.spring");
        return sessionFactory;
    }

    @Bean
    @Qualifier("sessionFactory_extended")
    public LocalSessionFactoryBean getExtendedSessionFactory() {
        LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
        sessionFactory.setDataSource(new DataSourceConfig_Extended().secondaryDataSource());
        sessionFactory.setPackagesToScan("com.ourapp.spring");
        return sessionFactory;
    }

}

Thank you. :-)

3条回答
甜甜的少女心
2楼-- · 2019-08-22 12:30

You can do this using PropertySourcesPlaceholderConfigurer

Create a bean like this

@Bean
public static PropertySourcesPlaceholderConfigurer devPropertyPlaceholderConfigurer() throws IOException {
   PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
   configurer.setLocations(new PathMatchingResourcePatternResolver().getResources("file:pathtToFile"));
   configurer.setIgnoreUnresolvablePlaceholders(true);
   return configurer;
}

For example if you write file:/tmp/dev/*.properties . It will load all the properties file under /tmp/dev.

If you want to customize it based on differnt environment(dev, testing, production). then you can use @Profile and create multiple beans.

@Bean
@Profile("dev")
public static PropertySourcesPlaceholderConfigurer devPropertyPlaceholderConfigurer() throws IOException {
   ..... // give dev properties file path
}


@Bean
@Profile("testing")
public static PropertySourcesPlaceholderConfigurer testPropertyPlaceholderConfigurer() throws IOException {
.....// give test properties file path
}

@Bean
@Profile("prod")
public static PropertySourcesPlaceholderConfigurer prodPropertyPlaceholderConfigurer() throws IOException {
.....// give prod properties file path
}
查看更多
叛逆
3楼-- · 2019-08-22 12:45

Probably what you are looking for is Profiles( @Profile or @Conditional )

Step1: Create a profile. The following is the example for prod profile. Similarly, you can create one for dev and qa

import javax.activation.DataSource;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.jndi.JndiObjectFactoryBean;

@Configuration
@Profile("prod")
@PropertySource("classpath:/com/<SomePath>/app.properties")
public class ProductionProfileConfig {

    @Autowired Environment env;

    @Bean
    public DataSource dataSource() {
        JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
        jndiObjectFactoryBean.setJndiName(env.getProperty("dbName"));
        jndiObjectFactoryBean.setResourceRef(true);
        jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);
        return (DataSource) jndiObjectFactoryBean.getObject();
    }
}

Step2 Activate profiles

Spring honors two separate properties when determining which profiles are active: spring.profiles.active and spring.profiles.default. If spring.profiles.active is set, then its value determines which profiles are active. But if spring .profiles.active isn’t set, then Spring looks to spring.profiles.default. If neither spring.profiles.active nor spring.profiles.default is set, then there are no active profiles, and only those beans that aren’t defined as being in a profile are created

UPDATE

Use PropertyConfigurator.configure(Loader.getResource(<your-file-path>)); if the file is located outside the packaged war. Later you can simply inject values using @Value or SPel

查看更多
SAY GOODBYE
4楼-- · 2019-08-22 12:48

Write a config manager, which will create configuration objects based on the name of properties file. For example,

Configuration config = ConfigurationManager.getConfig("dbConfig");

so, now your config object will contain all properties related to db. When you instantiate config object read all properties into this object.

Let's say your properties file contains following fields:

user.name = "Tom" user.password = "pass"

Next time when you need "user.name", you would just do config.getString("user.name")

查看更多
登录 后发表回答