FindBugs null issue with Spring #Autowired in Eclipse - java

I am using the findbugs Eclipse plugin (3.0.1.20150306-5afe4d1), spring (4.2.2.RELEASE), and eclipse (Mars.1 (4.5.1)) together and I am receiving the following FindBugs bug in Eclipse.
Non-null field env is not initialized by new
org.test.app.config.AppConfiguration() [Scary(8), Normal confidence]
I am using using the default constructor and using autowiring to initialize the env variable. I also have a PostConstruct annotation which gets called after everything is wired and accesses the env variable to make sure it was initialized correctly.
How can I make this error disappear without turning off the FindBugs plugin and still using the #Autowired annotation?
package org.test.app.config;
import java.util.Arrays;
import javax.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
#Configuration
#ComponentScan(basePackages = { "org.test.app" })
#PropertySource("classpath:/${spring.profiles.active:local}.properties")
public class AppConfiguration {
private static final Logger log = LoggerFactory.getLogger(AppConfiguration.class);
#Autowired
private Environment env;
/**
* Dump profile info.
*/
#PostConstruct
public void details() {
log.debug("** App application context, active profile(s)={}", Arrays.toString(env.getActiveProfiles()));
}
}
Update
I tried using a constructor per #spoonybard896 suggestion, but it did not work. I received the following error.
java.lang.NoSuchMethodException: org.test.app.config. AppConfiguration $$EnhancerBySpringCGLIB$$cbece1d7.<init>()
[STDOUT] at java.lang.Class.getConstructor0(Class.java:3082) ~[na:1.8.0_60]
[STDOUT] at java.lang.Class.getDeclaredConstructor(Class.java:2178) ~[na:1.8.0_60]
[STDOUT] at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiat‌​e(SimpleInstantiationStrategy.java:80) ~[na:na]

How about using a non-default constructor instead?
private final Environment env;
#Autowired
public AppConfiguration(final Environment env) {
this.env = env;
}
EDIT
The above approach would work for a #Controller instance, but will not work for #Configuration. After doing some quick research it turns out that:
#Configuration is meta-annotated with #Component, therefore #Configuration classes are candidates for component scanning (typically using Spring XML's <context:component-scan/> element) and therefore may also take advantage of #Autowired/#Inject at the field and method level (but not at the constructor level).
I'm thinking that unless there is some kind of addon for FindBugs that understands Spring annotations (I do not know of one) then you may just need to apply a filter to the FindBugs plugin and have it ignore that particular error in that particular file(or in any Configuration class in general). In Eclipse check out Preferences -> Java -> Findbugs -> Filter Files, and check out this link which describes a similar problem and resolution, but just make sure to filter out only the error you want. The goal would not be turn FindBugs off, but instead have it just ignore this one case.
EDIT 2
Adding an annotation to the class will suppress the FindBugs error for this file only.
#SuppressFBWarnings(
value="NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR",
justification="Overriding the check on the env variable because Spring will automatically initialize the variable after the constructor is called and before any public methods are called.")

Related

Spring override non-application.properties file in Tests

I use an external pure Java library that uses a file that is called updater.properties.
Im currently writing some Spring-Boot Integration Tests with #SpringBootTest.
The problem I currently have is that i don’t find any option to pass/use a different updater.properties file with Spring. Is there a solution for this? Or does the library need to be updated for this to work?
The updater.properties is in my resource folder and being parsed internally by the library.
Would be happy for any help!
#SaltukKezer, yes you can. First step make a config class like
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
#Configuration
#PropertySource("classpath:/updater.properties")
public class UpdaterProperties{
#Autowired
Environment env;
public String getMyPropertyValue() {
return env.getProperty("your.property.name");
}
}
Then second step, just get an instance of this class and call the getters

Parameter 0 of constructor in required a bean of type 'java.lang.String' that could not be found

I am working on spring batch with spring boot 2.X application, actually its existing code i am checked out from git. While running the application it fails due to below error only for me and same code is working for others.
s.c.a.AnnotationConfigApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'inputItemReader' defined in file [C:\Users\XYZ\git\main\batch\CBatchProcessing\target\classes\com\main\batchprocessing\batch\reader\InputItemReader.class]: Unsatisfied dependency expressed through **constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'java.lang.String' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations**: {}
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2018-10-16 23:23:37.411 ERROR 2384 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
**Parameter 0 of constructor in com.main.batchprocessing.batch.reader.InputItemReader required a bean of type 'java.lang.String' that could not be found.**
Action:
Consider defining a bean of type 'java.lang.String' in your configuration.
I have checked below
All Spring components are correctly annotated with #Component, #Service, #Controller,#Repository, etc...
#ComponentScan & #EnableAutoCOnfiguration is also provided.
Tried giving "java.lang.String" in declarations.
Code:
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.StepExecutionListener;
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.mapping.JsonLineMapper;
import
org.springframework.batch.item.file.separator.JsonRecordSeparatorPolicy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.FileSystemResource;
import org.springframework.stereotype.Component;
#Component
public class InputItemReader extends FlatFileItemReader<Map<String,
Object>> implements StepExecutionListener {
#Autowired
private InputFileHeaderValidator inputFileHeaderValidator;
#Autowired
private FileAuditService fileAuditService;
private final Logger log =
LoggerFactory.getLogger(InputItemReader.class);
private java.lang.String inputFilePath;
public InputItemReader(String inputFilePath) {
setLineMapper(new JsonLineMapper());
setRecordSeparatorPolicy(new JsonRecordSeparatorPolicy());
setResource(new FileSystemResource(inputFilePath));
this.inputFilePath = inputFilePath;
}
}
Since you do not provide the public default constructor and you added your own non-default constructor the instantiation will fail. I would suggest you to define the input file path as property like #Value("${inputFilePath}").
If you need further initialization in your bean define a void method and annotate it with #PostConstruct and do the initialization within.
Add a public default constructor in your class. For example.
public User() {
}
Make sure you are using spring-boot-starter-data-jpa
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
You defined something like this:
#Component
public class InputItemReader{
public InputItemReader(String input){
...
}
}
The name of your class suggest that your object is not a bean, just a simple object. You should try to use it in classic way:
new InputItemReader(myString);
or to have a static method to process the input String.
Explanation: Spring IoC container will try to instantiate a new InputItemReader object like this :
new InputItemReader( -- WHAT TO PUT HERE? --)
and will fail to call your constructor, because it will not know what you do actually expect and input string.
UPDATE:
Your problem can be solved by removing #Component annotation and defining the bean in a configuration like this:
#Bean
public InputItemReader inputItemReader(InputFileHeaderValidator inputFileHeaderValidator, FileAuditService fileAuditService){
InputItemReader inputItemReader = new InputItemReader("--HERE SHOULD BE ACTUAL PATH---");
// set the required service, a cleaner approach would be to send them via constructor
inputItemReader.setFilteAuditService(fileAuditService);
inputItemReader.setInputFileHeaderValidator(inputFileHeaderValidator);
return inputItemReader;
}
For me, it was because of using #AllArgsConstructor annotation of the lombok. My code was like this:
#Service
#AllArgsConstructor
public class SampleService {
#Value("${search.page.size}")
private Integer pageSize;
private final SampleRepository sampleRepository;
And then, I removed #AllArgsConstructor and added #RequiredArgsConstructor annotation. The problem was solved.
#Service
#RequiredArgsConstructor
public class SampleService {
#Value("${search.page.size}")
private Integer pageSize;
private final BatchRepository batchRepository;
I had the same error but the error was generated by Feign Client. If you have this error using feign client you must add #EnableFeignClients on your main class:
#SpringCloudApplication
#EnableFeignClients
public class Application {
...
}
I also had the same error:
***************************
APPLICATION FAILED TO START
***************************
Description:
Field repository in com.example.controller.CampaignController required a bean of type 'com.example.data.CustomerRepository' that could not be found.
Action:
Consider defining a bean of type 'com.example.data.CustomerRepository' in your configuration.de here
I solved this issue by adding #EnableMongoRepositories annotation in the main class:
#SpringBootApplication
#EnableMongoRepositories(basePackageClasses = CustomerRepository.class)
public class CampaignAPI {
public static void main(String[] args) {
SpringApplication.run(CampaignAPI.class, args);
}
}
I was also having the same problem, for me following solution worked perfectly fine:
I had annotated my class as #AllArgsConstructor by importing import lombok.AllArgsConstructor
I had just removed this annotation and the code starts working.
Hope this may help someone.
The issue in my case was a redundant #Autowired,
I initially added a dependency using #Autowired, by eventually commented it out, however I forgot to comment the annotation, due to which the method next to #Autowired was considered as some sort of setter.
On removing the redundant annotation it is working fine.
Even after following the above solutions, if the problem still exists then please check your import statements.
In my case it was the wrong import of #service annotation.
in my case I missed the #Service annotation so once I placed it works.
in my case, the issue was way different.
#SpringBootApplication
#EnableNeo4jRepositories("com.digital.api.repositories") // <-- This was non-existent.
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Look at the #EnableNeo4jRepositories annotation. The package defined, was non-existent.
Try defining that package, and be careful that the Repository interfaces are based there.
Otherwise, Spring will not find the repositories classes that it should load up!
I was facing the same issue and it got fixed by removing Constructors from my Model class. Adding sample snippet below:
Map<String, ServiceDefinition> serviceDefinitionMapper = new HashMap<>();
A def;
B serviceCharacter;
#Autowired
public Scan(Map<String, ServiceDefinition> serviceDefinitionMapper, A def,
B serviceCharacter) {
super();
this.serviceDefinitionMapper = serviceDefinitionMapper;
this.def = def;
this.serviceCharacter = serviceCharacter;
}
Please Note: Do not keep any Constructor/#AllArgsConstructor in your Model class unless it is very much needed to be decalred.
In my case annotating fields with lombok #NonNull was causing the trouble.
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
#Service
#RequiredArgsConstructor
#Transactional
#Slf4j
public class UserServiceImp implements UserService, UserDetailsService {
....
}
I had this issue recently and it turned out I hadn't added a `#Component' annotation to the service file of my project. The effect was the class wasn't instantiated as a spring bean.
I faced same issue but with my own created interface repository that could not be found. The answer for me was that Spring 2.6.3 has bugs with Java 8. After i switched java to 11 version everything goes correct.
More info about this kind of problems i found here https://github.com/spring-projects/spring-boot/issues/6987
in my case i had this problem but it was because of i added an annotation of #Component in my Exception code which i shouldn't because we don't want it to be scanned really we're just using it to handle an exception in our service or i don't know try to delete the annotation in your built-in exception.
worked on my machine
I had this problem while testing with JUnit. It was that I forgot to create #MockBean annotation, that's why the bean was missing.
Not related to the above issue but if you have the same error of bean creation in a spring boot application and you are getting the same above error please check whether you have added the class path in #ComponentScan( of the main spring booth application launching class.
In my case , I defined a service's interface and forgot to implement it :) check this as well
For it to work I had to add this #RequiredArgsConstructor annotation to my class, and then added this #Value("${inputFilePath}") annotation to access the variable. See below:
#Component
#RequiredArgsConstructor
public class InputItemReader extends FlatFileItemReader<Map<String,
Object>> implements StepExecutionListener {
#Value("${inputFilePath}")
private inputFilePath;
//...
}
I met this error when trying to use record.
#Component
public record User(String Name, int Age){}
It turns out record needn't #Component. Fixed by simply removing it.
In my case, removing the #EnableJpaRepositories annotation from my config fixed the issue. The repositories are still wired correctly.
Before:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories
#EnableJpaAuditing
public class PersistenceConfig {
After:
#Configuration
#EnableTransactionManagement
#EnableJpaAuditing
public class PersistenceConfig {

Spring 3 and Junit 4 NullPointerException

I have a problem in setting up a unit test based on Spring 3 and Junit 4
My code is fairly basic at the moment as can be seen below
#ContextConfiguration
#RunWith(SpringJUnit4ClassRunner.class)
public class PSTemplateDAOTests {
private static final Logger logger = Logger.getLogger(
PSTemplateDAOTests.class.getName());
#Autowired
ApplicationContext ctx;
#Test
public void testone ()
{
logger.info("Run testone");
}
#Test
public void testtwo ()
{
logger.info("Run testtwo");
}
}
However when I come to run this I encounter a NPE as below :-
java.lang.NullPointerException
at org.springframework.test.context.ContextLoaderUtils.resolveContextLoaderClass(ContextLoaderUtils.java:153)
at org.springframework.test.context.ContextLoaderUtils.resolveContextLoader(ContextLoaderUtils.java:84)
at org.springframework.test.context.ContextLoaderUtils.buildMergedContextConfiguration(ContextLoaderUtils.java:298)
at org.springframework.test.context.TestContext.<init>(TestContext.java:100)
at org.springframework.test.context.TestContextManager.<init>(TestContextManager.java:117)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTestContextManager(SpringJUnit4ClassRunner.java:119)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.<init>(SpringJUnit4ClassRunner.java:108)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
at java.lang.reflect.Constructor.newInstance(Unknown Source)
at org.junit.internal.builders.AnnotatedBuilder.buildRunner(AnnotatedBuilder.java:31)
at org.junit.internal.builders.AnnotatedBuilder.runnerForClass(AnnotatedBuilder.java:24)
at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:57)
at org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass(AllDefaultPossibilitiesBuilder.java:29)
at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:57)
at org.junit.internal.requests.ClassRequest.getRunner(ClassRequest.java:24)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.<init>(JUnit4TestReference.java:33)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestClassReference.<init>(JUnit4TestClassReference.java:25)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.createTest(JUnit4TestLoader.java:48)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.loadTests(JUnit4TestLoader.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:452)
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)
If I comment out the #ContextConfiguration annotation the test(s) will run however then I encounter the following :-
May 20, 2015 8:28:29 AM org.springframework.test.context.TestContext <init>
INFO: #ContextConfiguration not found for class [class uk.gov.moj.ps.dao.PSTemplateDAOTests]
May 20, 2015 8:28:29 AM org.springframework.test.context.TestContextManager retrieveTestExecutionListeners
INFO: #TestExecutionListeners is not present for class [class uk.gov.moj.ps.dao.PSTemplateDAOTests]: using defaults.
2015-05-20 08:28:29 INFO PSTemplateDAOTests:34 - Run testtwo
2015-05-20 08:28:29 INFO PSTemplateDAOTests:27 - Run testone
Which makes sense as there is no ContextConfiguration element to allow Spring to set up the environment.
I have also tried different ContextConfiguration set ups with both normal files and location elements, neither setting gets past the initial NPE error.
I'm using Eclipse Kepler and jre 1.7 although I have also switched round jre's without any success.
Any assistance would be much appreciated.
Ok - I realise I wasn't clear enough in my description, it is my first question on SO, anyway some further clarification.
I am aware that the contextconfiguration is generally configured with a path to a file via location or just a simple path, I have tried both of these and also the default value with no path.
The whole crux of the matter is that the #Contextconfiguration triggers a NPE regardless of what is supplied to it, in fact spring doesn't even seem to initialise correctly.
I hope this makes things clearer
Most examples from the Spring Framework Reference Manual explicitely defines the location of the XML file, or #Configuration classes used to populate the application context.
You can use a plain #ContextConfiguration annotation without any configuration, but the default may not be what you expect :
If you omit both the locations and value attributes from the #ContextConfiguration annotation, the TestContext framework will attempt to detect a default XML resource location. Specifically, GenericXmlContextLoader detects a default location based on the name of the test class. If your class is named com.example.MyTest, GenericXmlContextLoader loads your application context from "classpath:/com/example/MyTest-context.xml".
IMHO, you should explicitely pass the configuration of the application context to #ContextConfiguration. Examples from same doc :
// use test.config.xml from classpath
#ContextConfiguration("/test-config.xml")
public class XmlApplicationContextTests {
// class body...
}
// use TestConfig as a #Configuration class
#ContextConfiguration(classes = TestConfig.class)
public class ConfigClassApplicationContextTests {
// class body...
}
I don't think you can autowire ApplicationContext like you are trying, and I'm not sure why you would want to, especially in a test.
But if you really want to, try the code below:
import java.util.logging.Logger;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.Assert;
#ContextConfiguration
#RunWith(SpringJUnit4ClassRunner.class)
public class PSTemplateDAOTests implements ApplicationContextAware {
private static final Logger logger = Logger.getLogger(PSTemplateDAOTests.class.getName());
private ApplicationContext ctx;
#Test
public void assertContextPreset() {
Assert.notNull(ctx);
}
#Test
public void testone() {
logger.info("Run testone");
}
#Test
public void testtwo() {
logger.info("Run testtwo");
}
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
ctx = applicationContext;
}
}
Like others have said, you also seem to be missing some application context configuration. You should either provide a location as a parameter to ContextConfiguration or create a context file named PSTemplateDAOTests-context.xml in the same package as the test.

How it works together these 2 Spring Java configuration classes?

I am studying for the Spring Core certification and I have the followind doubt with an exercice related to the beans configuration using the Java configuration way.
So I have the following RewardsConfig class that configure my beans (this class is into the application folder src/main/java):
package config;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import rewards.RewardNetwork;
import rewards.internal.RewardNetworkImpl;
import rewards.internal.account.AccountRepository;
import rewards.internal.account.JdbcAccountRepository;
import rewards.internal.restaurant.JdbcRestaurantRepository;
import rewards.internal.restaurant.RestaurantRepository;
import rewards.internal.reward.JdbcRewardRepository;
import rewards.internal.reward.RewardRepository;
#Configuration
public class RewardsConfig {
#Autowired
DataSource dataSource;
#Bean
public RewardNetwork rewardNetwork(){
return new RewardNetworkImpl(accountRepository(), restaurantRepository(), rewardRepository());
}
#Bean
public AccountRepository accountRepository(){
JdbcAccountRepository repository = new JdbcAccountRepository();
repository.setDataSource(dataSource);
return repository;
}
#Bean
public RestaurantRepository restaurantRepository(){
JdbcRestaurantRepository repository = new JdbcRestaurantRepository();
repository.setDataSource(dataSource);
return repository;
}
#Bean
public RewardRepository rewardRepository(){
JdbcRewardRepository repository = new JdbcRewardRepository();
repository.setDataSource(dataSource);
return repository;
}
}
As you can see I declare 4 methods that are used to create 4 beans and that specify the dependency that occurs among these beans.
So I have a RewardNetwork bean that is implemented by RewardNetworkImpl class that depends from the following 3 beans: AccountRepository, RestaurantRepository and RewardRepository.
Is it the correct interpretation of the Java configuration is Spring?
Can I say for example that RewardNetwork is the declared bean and that RewardNetworkImpl its the current implementation of this bean?
All the 3beans (AccountRepository, RestaurantRepository and RewardRepository) depends by another bean dataSource that, as you can see in the previous code snippet, is declared as #Autowired:
#Autowired
DataSource dataSource;
This bean is not declared in this configuration class because it changes according to the environment (test, developt, production).
So, in my case it is declared into the unit test folder src/test/java:
package rewards;
import javax.sql.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
#Configuration
public class TestInfrastructureConfig {
/**
* Creates an in-memory "rewards" database populated
* with test data for fast testing
*/
#Bean
public DataSource dataSource(){
return
(new EmbeddedDatabaseBuilder())
.addScript("classpath:rewards/testdb/schema.sql")
.addScript("classpath:rewards/testdb/test-data.sql")
.build();
}
}
So the dataSource bean define a datasource that is valid only for the test environment (used when I perform a unit test).
Now my doubt is: I have 2 different configuration classes and the dataSource bean is not definied into the RewardsConfig configuration class that contains the 3 beans that use it. Why I can't not use the #Import annotation to use it into RewardsConfig?
Something like it:
#Import(TestInfrastructureConfig.class)
How it work exactly?
Tnx
You don't have to import beans to make them available for autowiring. #Import is used to add extra configuration classes.
You really don't want to hard-import a test configuration class, because then your production code is referring to test-only code (and, in this case, always activating it). Instead, think of your configuration class more like an abstract class: declare autowired beans, but don't worry about how they get there. The downstream (runtime) configuration will supply them, and you don't need to know how. Maybe you're supplying an in-memory H2 for testing and using Spring Cloud Connectors for actual runs, doesn't matter.

Properties and env always null

I have a hard time loading properties from my properties file. I'm trying to load a property into my configuration class which is listed below.
package dk.fitfit.budget.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.env.Environment;
#Configuration
#PropertySource("classpath:application.properties")
public class ApplicationConfig {
private static final Logger logger = LoggerFactory.getLogger(ApplicationConfig.class);
#Autowired
private Environment env;
#Value("${snot:test}")
private String snot;
public ApplicationConfig() {
logger.info("Application config loaded!"); // Displays as expected
logger.info("snot: {}", snot); // snot: null
logger.info("env: {}", env); // env: null
}
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
If I change the name of the file (application.properties) or "#PropertySource("classpath:application.properties")" I get an error about the file not being present. So clearly it's being loaded (to some extend anyway).
I was at least expecting to see the default string "test" injected into the variable snot. But not even that is done. I'm not able to autowire Environment either... not sure if there is a relation.
The content of my application.properties is as below.
snot=snog
My application.properties file is placed in src/main/resources/.
Anyone got a clue about what I'm doing wrong?
The purpose of a constructor is to initialize a class instance. You have to construct and initialize the class instance before Spring can do anything to it. As such, you cannot expect the env and snot fields to have anything other than the default null value within the constructor.
Spring (and all dependency-injection tools) can't inject fields before the instance is created with new, which is one reason why constructor injection should be preferred to field injection. Generally, you can use #Value on constructor parameters, but there are certain issues with #Configuration classes that make constructor injection impractical.
However, there's a solution meant for exactly this situation: #PostConstruct. This method will be called after the DI framework has done all its magic to the bean:
#PostConstruct
public void log() {
logger.info("Application config loaded!"); // Displays as expected
logger.info("snot: {}", snot); // snot: null
logger.info("env: {}", env); // env: null
}

Categories

Resources