22

I have a Spring-Boot-Application as a multimodule-Project in maven. The structure is as follows:

Parent-Project
|--MainApplication
|--Module1
|--ModuleN

In the MainApplication project there is the main() method class annotated with @SpringBootApplication and so on. This project has, as always, an application.properties file which is loaded automatically. So I can access the values with the @Value annotation

@Value("${myapp.api-key}")
private String apiKey;

Within my Module1 I want to use a properties file as well (called module1.properties), where the modules configuration is stored. This File will only be accessed and used in the module. But I cannot get it loaded. I tried it with @Configuration and @PropertySource but no luck.

@Configuration
@PropertySource(value = "classpath:module1.properties")
public class ConfigClass {

How can I load a properties file with Spring-Boot and access the values easily? Could not find a valid solution.

My Configuration

@Configuration
@PropertySource(value = "classpath:tmdb.properties")
public class TMDbConfig {

    @Value("${moviedb.tmdb.api-key}")
    private String apiKey;

    public String getApiKey() {
        return apiKey;
    }

    @Bean
    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }
}

Calling the Config

@Component
public class TMDbWarper {

@Autowired
private TMDbConfig tmdbConfig;

private TmdbApi tmdbApi;

public TMDbWarper(){
    tmdbApi = new TmdbApi(tmdbConfig.getApiKey());
}

I'm getting an NullPointerException in the constructor when I autowire the warper.

Karol Dowbecki
  • 43,645
  • 9
  • 78
  • 111
Daniel
  • 1,027
  • 1
  • 8
  • 23
  • 1
    can you follow this post? It has code example too. https://www.javacodegeeks.com/2016/11/spring-boot-multi-module-projects-adding-module-specific-property-files.html – Shahzeb Khan Oct 17 '17 at 07:45
  • is property file copied to jar ? try to unzip your jar and check if file is there – jmhostalet Oct 17 '17 at 07:59
  • Well without more information, it is only a wild guess, as it is stated in the JavaDoc: In order to resolve ${...} placeholders in definitions or @Value annotations using properties from a PropertySource, one must register a PropertySourcesPlaceholderConfigurer. – mrkernelpanic Oct 17 '17 at 08:11
  • I updated my Post. There you can see the configuration and how i try to access the value. I unzipped my war package. Within the war ther is the jar of my module and within the module-jar the is my propertyfile. – Daniel Oct 17 '17 at 15:33
  • tmdbConfig was not instantiated? – Oleksii Kyslytsyn Apr 17 '18 at 13:30

5 Answers5

7

For field injection:

Fields are injected right after construction of a bean, before any config methods are invoked. Such a config field does not have to be public. Refer Autowired annotation for complete usage. Use constructor injection in this case like below:

@Component
public class TMDbWarper {

    private TMDbConfig tmdbConfig;

    private TmdbApi tmdbApi;

    @Autowired
    public TMDbWarper(final TMDbConfig tmdbConfig){
            this.tmdbConfig = tmdbConfig;
            tmdbApi = new TmdbApi(tmdbConfig.getApiKey());
    }

(or)

Use @PostConstruct to initialise like below:

@Component
public class TMDbWarper {

    @Autowired
    private TMDbConfig tmdbConfig;

    private TmdbApi tmdbApi;

    @PostConstruct
    public void init() {
        // any initialisation method
        tmdbConfig.getConfig();
    }
Subash J
  • 2,028
  • 3
  • 13
  • 27
5

Autowiring is performed just after the creation of the object(after calling the constructor via reflection). So NullPointerException is expected in your constructor as tmdbConfig field would be null during invocation of constructor

You may fix this by using the @PostConstruct callback method as shown below:

@Component
public class TMDbWarper {

    @Autowired
    private TMDbConfig tmdbConfig;

    private TmdbApi tmdbApi;

    public TMDbWarper() {

    }

    @PostConstruct
    public void init() {
        tmdbApi = new TmdbApi(tmdbConfig.getApiKey());
    }

    public TmdbApi getTmdbApi() {
        return this.tmdbApi;
    }
}

Rest of your configuration seems correct to me.

Hope this helps.

skadya
  • 4,330
  • 19
  • 27
3

Here is a Spring Boot multi-module example where you can get properties in different module. Let's say I have main application module, dataparse-module, datasave-module.

StartApp.java in application module:

@SpringBootApplication
public class StartApp {

    public static void main(String[] args) {

        SpringApplication.run(StartApp.class, args);
    }
}

Configuration in dataparse-module. ParseConfig.java:

@Configuration
public class ParseConfig {
        @Bean
        public XmlParseService xmlParseService() {
            return new XmlParseService();
        }
}

XmlParseService.java:

@Service
public class XmlParseService {...}

Configuration in datasave-module. SaveConfig.java:

@Configuration
@EnableConfigurationProperties(ServiceProperties.class)
@Import(ParseConfig.class)//get beans from dataparse-module - in this case XmlParseService
public class SaveConfig {

    @Bean
    public SaveXmlService saveXmlService() {
        return new SaveXmlService();

    }

}

ServiceProperties.java:

@ConfigurationProperties("datasave")
public class ServiceProperties {

    private String message;

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

}

application.properties in datasave-module in resource/config folder:

datasave.message=Multi-module Maven project!

threads.xml.number=5

file.location.on.disk=D:\temp\registry

Then in datasave-module you can use all your properties either through @Value.

SaveXmlService.java:

@Service
public class SaveXmlService {

    @Autowired
    XmlParseService xmlParseService;

    @Value("${file.location.on.disk: none}")
    private String fileLocation;

    @Value("${threads.xml.number: 3}")
    private int numberOfXmlThreads;
    
    ...
}

Or through ServiceProperties:

Service.java:

@Component
public class Service {

    @Autowired
    ServiceProperties serviceProperties;

    public String message() {

        return serviceProperties.getMessage();

    }
}
Community
  • 1
  • 1
Kirill Ch
  • 5,496
  • 4
  • 44
  • 65
1

I had this situation before, I noticed that the properties file was not copied to the jar.

I made the following to get it working:

  1. In the resources folder, I have created a unique package, then stored my application.properties file inside it. e.g: com/company/project

  2. In the configuration file e.g: TMDBConfig.java I have referenced the full path of my .properties file:

    @Configuration
    @PropertySource("classpath:/com/company/project/application.properties")
    public class AwsConfig
    

Build and run, it will work like magic.

Stephen Rauch
  • 47,830
  • 31
  • 106
  • 135
Abdulhafeth Sartawi
  • 1,086
  • 1
  • 11
  • 20
0

You could autowire and use the Enviornment bean to read the property

@Configuration
@PropertySource(value = "classpath:tmdb.properties")
public class TMDbConfig {

  @Autowired
  private Environment env;

  public String getApiKey() {
    return env.getRequiredProperty("moviedb.tmdb.api-key");
  }

}

This should guarantee that property is read from the context when you invoke the getApiKey() method regardless of when the @Value expression is resolved by PropertySourcesPlaceholderConfigurer.

Karol Dowbecki
  • 43,645
  • 9
  • 78
  • 111