Getting null values when using ConfigurationProperties on Spring boot - java

I'm new to the Java world and Spring boot, and I'm trying to access some configuration values located in a YAML file through the ConfigurationProperties annotation.
But whenever I try to access a configuration value anywhere in a service, I get a null value.
Here's the application.yml file:
my_config:
test: "plop"
Here's the ValidationProperties configuration class:
#Configuration
#ConfigurationProperties(prefix = "my_config")
public class ValidationProperties {
#NotNull
private String test;
public void setTest(String test) {
this.test = test;
}
public String getTest() {
return this.test;
}
}
A validator service that uses it:
#Service
public class MyValidator implements ConstraintValidator<MyConstraint, MyEntity> {
#Autowired
private ValidationProperties validationProperties;
#Value("${my_config.test}")
private String test;
#Override
public boolean isValid(MyEntity entity, ConstraintValidatorContext context) {
System.out.println(this.test); // null value, why?
}
}
I also added the #EnableConfigurationProperties annotation in my main class.
I'm not sure which annotation is supposed to do what, but I'm obviously missing something here. Also, if I try to access the value from the getter of the configuration file, I get an exception:
System.out.println(this.validationProperties.getTest());
will get me HV000028: Unexpected exception during isValid call.

Try adding #EnableConfigurationProperties(ValidationProperties.class)
on your main application class or any other #Configuration class.
Also putting #Component annotation on ValidationProperties should work.
No need to inject value via #Value annotation, just access it via getter of injected validationProperties object

I got this error running a JUnit test on a SpringBoot app, what I was missing is the annotation #SpringBootTest

Related

Spring Boot Cucumber tests could not resolve placeholder 'random.uuid'

I want a Spring Boot property to have an impossible to guess random value by default (for security reasons), so I am trying to use a random UUID as the default value, using code like this:
#Service
public class UserServiceImpl implements UserService {
...
#Autowired
public UserServiceImpl(#NonNull final PasswordEncoder passwordEncoder,
#NonNull final UserRepository userRepository,
#NonNull #Value("${administrator.password:${random.uuid}}") final String administratorPassword) {
...
}
But my Cucumber Spring Boot tests are complaining about the ${random.uuid} thus:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userServiceImpl' defined in file [.../UserServiceImpl.class]: Unexpected exception during bean creation; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'random.uuid' in value "administrator.password:${random.uuid}"
What do I have to do to get my application to use a random property value?
The problem might be related to test slicing. If I run a clean Spring boot project's test with:
#SpringBootTest
class DemoApplicationTests {
#Value("${nonexistingValue:${random.uuid}}")
private String someVal;
#Test
public void someTest() {
assertThat(someVal).contains("-");
}
}
The test passes. However, if I change #SpringBootTest to #ExtendWith({SpringExtension.class}) or #RunWith(SpringRunner.class), the test fails. ${random.uuid} and similar expressions should be available in a normal runtime environment.
Because of this slicing, it seems that the RandomValuePropertySource is not available. A rather inelegant work-around is to explicitly add it to the context, using a PropertySourcesPlaceholderConfigurer bean created in your test context:
#Configuration
public class CucumberBeansConfiguration {
#Bean
public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
final var configurer = new PropertySourcesPlaceholderConfigurer();
final var sources = new MutablePropertySources();
sources.addFirst(new RandomValuePropertySource());
configurer.setPropertySources(sources);
return configurer;
}
}
${random.uuid} can only be used if RandomValuePropertySource is available.
As workaround, you can define a "dummy default value" and detect this in your code:
#Autowired
public UserServiceImpl(#NonNull final PasswordEncoder passwordEncoder,
#NonNull final UserRepository userRepository,
#NonNull #Value("${administrator.password:no-default-vale-provided}") final String administratorPassword) {
this.adminPassword = "no-default-value-provided".equals(administratorPassword)
? UUID.randomUUID().toString()
: administratorPassword;
}
If you have [`RandomValuePropertySource`](https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/env/RandomValuePropertySource.html) available, then the SpEL expression `${random.uuid}` should indeed resolve to a random UUID. `${…}` is a property placeholder and `#{$…}` is SpEL syntax.
You need to change your annotation:
#NonNull #Value("#{${administrator.password} ?: ${random.uuid}}") final String administratorPassword
Cucumber uses Springs TestContextManager. By annotating your context configuration with #CucumberContextConfiguration Cucumber knows which class use to start the test context.
import com.example.app;
import org.springframework.boot.test.context.SpringBootTest;
import io.cucumber.spring.CucumberContextConfiguration;
#CucumberContextConfiguration
#SpringBootTest
public class CucumberSpringConfiguration {
}
Do make sure the CucumberSpringConfiguration is on the cucumber.glue path.

Can't achieve dependency injection oustide a controller in Spring Booot

I am new at spring MVC framework and i am currently working in a web application that uses a session scoped bean to control some data flow.
I can access these beans in my application context using #Autowired annotation without any problem in the controllers. The problem comes when I use a class in service layer that does not have any request mapping (#RequestMapping, #GetMapping nor #PostMapping) annotation.
When I try to access the application context directly or using #Autowired or even the #Resource annotation the bean has a null value.
I have a configuration class as follow:
#Configuration
#EnableAspectJAutoProxy
#EnableJpaRepositories(repositoryFactoryBeanClass = EnversRevisionRepositoryFactoryBean.class, basePackages = "com.quantumx.nitididea.NITIDideaweb.repository")
public class AppConfig implements WebMvcConfigurer {
#Bean (name = "lastTemplate")
#SessionScope
public LastTemplate getlastTemplate() {
return new LastTemplate();
}
//Some extra code
}
The POJO class is defined as :
public class LastTemplate {
private Integer lastId;
public LastTemplate(){
}
public Integer getLastId() {
return lastId;
}
public void setLastId(Integer lastId) {
this.lastId = lastId;
}
}
The I have a Test class that is annotated as service and does not have any request mapping annotated method:
//#Controller
#Service
public class Test {
// #Autowired
// private ApplicationContext context;
// #Autowired
#Resource(name = "lastTemplate")
public LastTemplate lastTemplate;
// #Autowired
// public void setLastTemplate(LastTemplate lastTemplate) {
// this.lastTemplate = lastTemplate;
// }
public Test() {
}
// #RequestMapping("/test")
public String testing() {
// TemplateForma last = (TemplateForma) context.getBean("lastInsertedTemplate");
// System.out.println(last);
System.out.println(lastTemplate);
// System.out.println(context.containsBean("lastTemplate"));
// System.out.println(context.getBean("lastTemplate"));
System.out.println("Testing complete");
return "Exit from testing method";
// return "/Messages/Success";
}
}
As you can see, there is a lot of commented code to show all the ways i have been trying to access my application context, using an Application context dependency, autowiring, declaring a resource and trying with a request mapping. The bean is null if no controller annotation and request mapping method is used and throws a java null pointer exception when I use the context getBean() methods.
Finally I just test my class in a controller that i have in my app:
#RequestMapping("/all")
public String showAll(Model model) {
Test test = new Test();
test.testing();
return "/Administrator/test";
}
Worth to mention that I also tried to change the scope of the bean to a Application scope and singleton, but it not worked. How can access my application context in a service class without mapping a request via controller?
Worth to mention that I also tried to change the scope of the bean to a Application scope and singleton, but it not worked
It should have worked in this case.
How can access my application context in a service class without mapping a request via controller?
Try one of these :-
#Autowired private ApplicationContext appContext;
OR
Implement ApplicationContextAware interface in the class where you want to access it.
Edit:
If you still want to access ApplicationContext from non spring managed class. Here is the link to article which shows how it can be achieved.
This page gives an example to get spring application context object with in non spring managed classes as well
What worked for me is that session scoped bean had to be removed in the application configuration declaration and moved to the POJO definition as follows:
#Component
#SessionScope
public class LastTemplate {
private Integer lastId;
public LastTemplate(){
}
public Integer getLastId() {
return lastId;
}
public void setLastId(Integer lastId) {
this.lastId = lastId;
}
}
The I just call the bean using #Autowired annotation.

Getting null for Autowired bean created using Java config

I am creating a REST service in Spring boot. I am creating a bean in config class and trying to use in service class by auto wiring, but I am always getting null, I have tried in constructor injection as well but not working. Below is the code,
Main app
#SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
}
REST controller
#RestController
#RequestMapping("/v1")
public class RestController {
#Autowired
private Service service;
Service
#Service
public class ServiceImpl implements Service {
//This is the bean
#Autowired
private Record record;
public ServiceImpl() { //-----------------> tried injecting in constructor as well
System.out.println(record); //-------------------> null
}
Config class
#Configuration
public class AppConfig {
#Bean
public Record record() {
return new Record("test");
}
}
I noted whenever I remove the record() from config class I get below error
required a bean of type 'com.ns.service.Record' that could not be found
And after adding the method the error is not reported but null is returned, which indirectly means record() is considered as returning the required bean.
I can't find what I am doing wrong please advise.
Project folder structure
I think you're doing everything right conceptually
Spring creates an object first and only after that injects the values (technically done in bean post processors):
So try this:
#Service
public class ServiceImpl implements Service {
//This is the bean
#Autowired
private Record record;
public ServiceImpl() {
// here the record is null - not injected yet
System.out.println(record);
}
#PostConstruct
public void checkThisOut() {
// here print the record
}
You say you've tried constructor injection as well - it should work because spring has to inject something into the constructor of a bean (ServiceImpl) or fail. Please show the code snippet
One this that might be wrong in some level (although it doesn't sound like this from your description) is that you have to put all the #Configuration/#Service annotated classes in the package that is the same or underneath the package where you've created the main class annotated with #SpringBootApplication annotation. It instructs spring boot where to look for the beans.
So make sure your classes obey this rule...

How to use #ConstructorBinding and #PropertySource together with #ConfigurationProperties with in Spring Boot 2.2.4?

I am new to Spring Boot. Currently, I am trying to create a POJO class (SystemProperties.class) to read the value in a properties file (parameter.properties separate from application.properties but still under the same directory /src/main/resources. The issue happens when I am using the #ConstructorBinding in the class in order for it to be immutable.
#ConstructorBinding needs to be used with #EnableConfigurationProperties or #ConfigurationPropertiesScan.
#ConfigurationPropertiesScan will ignore #Configuration annotation which is needed when using #PropertySource to specify external
*.properties file.
A) SystemProperties.class
#Configuration
#PropertySource("classpath:parameter.properties")
#ConstructorBinding
#ConfigurationProperties(prefix = "abc")
public class SystemProperties {
private final String test;
public SystemProperties (
String test) {
this.test = test;
}
public String getTest() {
return test;
}
B) parameter.properties
abc.test=text1
I have tried to remove the #PropertySource annotation but the value cannot be retrieved unless it is from the application.properties. Any help is greatly appreciated!
The way to solve this is to split the class into two classes with two different concerns. With such solution, you retain the SystemProperties class you created and additionally add another class simply for loading the properties file parameters to make them available to your application.
The solution would be as follows:
#ConstructorBinding
#ConfigurationProperties(prefix = "abc")
public class SystemProperties {
private final String test;
public SystemProperties(
String test) {
this.test = test;
}
public String getTest() {
return test;
}
}
Notice that I have omitted the #Configuration and #PropertySource annotations.
#Configuration
#PropertySource("classpath:parameter.properties")
public class PropertySourceLoader {
}
Notice I have simply added this annotations on a new class, solely created to load the properties file.
Finally, you can add #ConfigurationPropertiesScan on your main application class to enable the property mapping mechanism.

Spring Boot read values from application properties

I'm not sure if I understand it correctly, but from what I got, is that I can use #Value annotations to read values from my application.properties.
As I figured out this works only for Beans.
I defined such a bean like this
#Service
public class DBConfigBean {
#Value("${spring.datasource.username}")
private String userName;
#Bean
public String getName() {
return this.userName;
}
}
When the application starts I'm able to retrieve the username, however - how can I access this value at runtime?
Whenever I do
DBConfigBean conf = new DBConfigBean()
conf.getName();
* EDIT *
Due to the comments I'm able to use this config DBConfigBean - but my initial problem still remains, when I want to use it in another class
#Configurable
public SomeOtherClass {
#Autowired
private DBConfigBean dbConfig; // IS NULL
public void DoStuff() {
// read the config value from dbConfig
}
}
How can I read the DBConfig in a some helper class which I can define as a bean
Thanks
As Eirini already mentioned you must inject your beans.
The #Value annotation only works on Spring beans.
There is another way of accessing configuration with #ConfigurationProperties.
There you define a class that holds the configuration.
The main advantage is, that this is typesafe and the configuration is in one place.
Read more about this:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-vs-value
You shouldn't instantiate your service with the new operator. You should inject it, for example
#Autowired
private DBConfigBean dbConfig;
and then dbConfig.getName();
Also you don't need any #Bean decorator in your getName() method
You just need to tell spring where to search for your annotated beans. So in your configuration you could add the following:
#ComponentScan(basePackages = {"a.package.containing.the.service",
"another.package.containing.the.service"})
EDIT
The #Value, #Autowired etc annotations can only work with beans, that spring is aware of.
Declare your SomeOtherClass as a bean and add the package config in your #Configuration class
#Bean
private SomeOtherClass someOtherClass;
and then
#Configuration
#ComponentScan(basePackages = {"a.package.containing.the.service"
"some.other.class.package"})
public class AppConfiguration {
//By the way you can also define beans like:
#Bean
public AwesomeService service() {
return new AwesomeService();
}
}
Wrap your DBConfig with #Component annotation and inject it using #Autowired :
#Autowired
private DBConfig dbConfig;
Just add below annotation to your DBConfigBean class:
#PropertySource(value = {"classpath:application.properties"})

Categories

Resources