Java Spring Boot #Autowired values are null - java

When I try to print the variable that I have autowired, it prints "null" instead of the value I set it to, "Example." I can't quite figure out what I'm missing
In my AppConfig class I have:
#Configuration
public class ApplicationConfiguration {
#Bean
public String tableName(){
return "Example";
}
}
In my other class, DAOMethods, that I want to autowire the variable in:
#Component
public class DAOMethods {
#Autowired
private String tableName;
public void print(){
System.out.println(tableName);
}
}

They exist in different packages; With AppConfig living in a config
folder and DAOMethods in client->dynamodb->util folder. Config and
Client are folders under the main->java folder
The added #Configuration annotation scans for the beans in the current and its subpackages. You need to explicitly tell the application to scan the required packages. So you can do:
#SpringBootApplication (scanBasePackages = {"config", "client"})
OR,
You need to keep the config and other classes which uses that config in same root package. You can put the config folder and client folder under same package, say com.application and then add the following in your main class:
#SpringBootApplication(scanBasePackages = "com.application")
Now run the application.

You problem have many solutions
F.e. you can create config-class with required set of parameters and next autowire it (Injecting values with #Value annotation from application configuration file is good practice):
#Component
public class CustomConfiguration {
#Value("${table.name}")
private String tableName;
#Value("${some.value}")
private Integer someValue;
public String getTableName() {
return tableName;
}
public Integer getsomeValue() {
return someValue;
}
}
And you application.properties will looks like:
some.value=1
table.name=Example
Or you can simply inject single value from configuration with #Value annotation
One of solutions is using bean name in #Value annotation:
#Configuration
public class ApplicationConfiguration {
#Bean
public String tableName(){
return "Example";
}
}
#Component
public class DAOMethods {
#Value(#{tableName})
private String tableName;
}
More examples you can see in this question: Autowire a string from Spring #Configuration class?

Related

Spring: Implement two instances of a #Service with two instances of #Configuration linked to each respective service

An existing utility exists that I am importing to my project. It is written similarly as these two classes
#Service
public class ServiceAccessorImpl implements ServiceAccessor {
#Autowired private ServiceConfiguration serviceConfiguration;
public Response executeCall(){
return callEndPoint(serviceConfiguration.getServiceEndPoint());
}
}
#Configuration
#Data //automatically adds getters and setters
#ConfigurationProperties(prefix="config")//pulls serviceEndPoint value from external config
//Assume external config has config.serviceEndPoint = "www.endpoint1.com"
public class ServiceConfiguration {
private String serviceEndPoint;
}
In a separate project below I am importing the above into my project. I would like to have two instances of the same service with two unique and respective configuration classes. so that service1 is linked to config1 and service2 is linked to config2. My reasoning is I want one instance that only pulls the endpoint from the external configuration and another instance that I can use to set the endpoint. I have tried using things like #Qualifier but I cant figure out how to link the correct config with the correct service. I have a feeling that this may not be possible because ServiceConfiguration is privately scoped within ServiceAccessorImpl and I have no access through setters or constructors.
Controller Endpoint. The below is psuedo code of how I would like to implement my design. Autowiring in a single instance and using either endpoint works for that endpoint but not for both as shown below.
#ComponentScan(basePackageClass = ServiceAccessorImpl.class)
public class ServiceAccessorController {
#Autowired private ServiceAccessor serviceAccessor1;
#Autowired private ServiceConfiguration serviceConfiguration1;
#Autowired private ServiceAccessor serviceAccessor2;
#Autowired private ServiceConfiguration serviceConfiguration2;
Response CallEndpoint1(){
//www.endpoint1.com is already set here from external config
return serviceAccessor1.executeCall();
}
Response CallEndpoint1(){
serviceConfiguration2.setServiceEndPoint("www.endpoint2.com")
return serviceAccessor2.executeCall();
}
}
Thank you in advance
If you need multiple instances of the same implementation, it's easier to not annotate it as a bean, and have a #Configuration provide the beans instead.
public class ServiceAccessorImpl implements ServiceAccessor {
private ServiceConfiguration serviceConfiguration;
public ServiceAccessorImpl(ServiceConfiguration configuration) {
this.serviceConfiguration = configuration;
}
public Response executeCall(){
return callEndPoint(serviceConfiguration.getServiceEndPoint());
}
}
// this one should just have #ConfigurationProperties, not #Configuration
#Data
#ConfigurationProperties(prefix="config")
public class ServiceConfiguration {
private String serviceEndPoint;
}
then in your service you can have a Configuration providing both instances:
#Configuration
public class BeansConfiguration {
#Qualifier("service1")
#Primary // optional, Spring will autowire this instance by default if no qualifier is given
#Bean
public service1(#Autowired ServiceConfiguration config) {
// use the default config bean
return new ServiceAccessorImpl(config);
}
#Qualifier("service2")
#Bean
public service2() {
return new ServiceAccessorImpl(new ServiceConfiguration("www.endpoint2.com"));
}
}
then you may consume both by using the qualifiers (note that you don't have to inject the configs here):
public class ServiceAccessorController {
#Autowired
private ServiceAccessor serviceAccessor1;
#Autowired
#Qualifier("service2")
private ServiceAccessor serviceAccessor2;
Response CallEndpoint1(){
return serviceAccessor1.executeCall();
}
Response CallEndpoint2(){
return serviceAccessor2.executeCall();
}
}

How to use values from application.properties in named properties of annotations on class header

It is known that some annotations have named properties, such as:
#MyAnnotation(name="myName", url="http://www.example.com")
Now, if I were to use this annotation in MyClass like this:
#MyAnnotation(name="myName", url="http://www.example.com")
public class MyClass {
...
}
but move the values "myName" and "http://www.example.com" into application.properties like this:
services.name=myName
services.url=http://www.example.com
then how can I access these properties from MyClass?
I was able to use the properties defined in application.properties as below:
#PropertySource("classpath:application.properties")
#MyAnnotation(name="${services.name}", url="${services.url}")
public class MyClass {
...
}
However, I am not sure if others can use application.properties values like this as well.
You can achieve binding using #ConfigurationProperties annotation.
#Configuration
#ConfigurationProperties(prefix = "services")
public class MyClass {
private String name;
private String url;
}
We use #Configuration so that Spring creates a Spring bean in the application context.
#ConfigurationProperties works best with hierarchical properties that all have the same prefix; therefore, we add a prefix of mail.
The Spring framework uses standard Java bean setters, so we must declare setters for each of the properties.
Note: If we don't use #Configuration in the POJO, then we need to add #EnableConfigurationProperties(ConfigProperties.class) in the main Spring application class to bind the properties into the POJO:
#SpringBootApplication
#EnableConfigurationProperties(MyClass.class)
public class EnableConfigurationDemoApplication {
public static void main(String[] args) {
SpringApplication.run(EnableConfigurationDemoApplication.class, args);
}
}
Spring will automatically bind any property defined in our property file that has the prefix services and the same name as one of the fields in the ConfigProperties class.
References :
https://www.baeldung.com/configuration-properties-in-spring-boot
You can simply use #Value annotation. For that your class must managed by Spring. In other words your class must be a Spring Bean.
Something like this,
#Service
public class MyClass {
#Value("${services.name}")
private String name;
#Value("${services.url}")
private String url;
}
Note: For more info here
You can also create custom configuration property class. Something like this,
#Configuration
#ConfigurationProperties("services")
public class MyClass {
private String name;
private String url;
}
Note: For more info here

Spring Boot - How to read properties from multiple custom yml

We are upgrading the spring boot version from 1.3.0.RELEASE to 2.3.12.RELEASE.
As per the old version, yml files were read using the following code snippet
#Configuration
#ConfigurationProperties(locations = "classpath:/config/myconf-source.yml")
public class MyConfigProperties {
private String configSource;
public String getConfigSource() {
return configSource;
}
public void setConfigSource(String configSource) {
this.configSource = configSource;
}
}
Config files in src/main/resources/config/
myconf-source.yml
news-source.yml
conf-mapping.yml
Content in myconf-source.yml
configSource: "TEST"
Corresponding Test Class
#ActiveProfiles("test")
#RunWith(SpringJUnit4ClassRunner.class)
#EnableAutoConfiguration
#SpringApplicationConfiguration(classes = SampleApplication.class)
#DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
#ConfigurationProperties(locations = "classpath:**/config/**")
public class MyConfigPropertiesTest {
#Autowired
private MyConfigProperties myConfigProperties;
#Test
public void testMyConfigProperties() {
String config = myConfigProperties.getConfigSource();
Assert.assertEquals(config, "TEST");
}
}
After changing to the new version, it throws an error Cannot resolve method 'locations'.
If I remove locations attribute how spring will know the class MyConfigProperties has to read myconf-source.yml
Also while running the test class, NullPointerException is thrown as myConfigProperties.getConfigSource(); becomes null.
I have tried various solutions posted but no luck,
Can anyone suggest how to make it work?
Thanks
#Configuration should be used if in that class you define beans with #Bean.
If not then remove it from there.
Also #Configuration does not make this class a bean to be autowired in the test that you require it to be.
If you want MyConfigProperties to be available for autowiring then you also need
#EnableConfigurationProperties(MyConfigProperties.class). This will make sure that this class is available as a spring bean in the application context.
So it would be
#PropertySource("classpath:/config/myconf-source.yml")
#ConfigurationProperties()
#EnableConfigurationProperties(MyConfigProperties.class)
public class MyConfigProperties {
private String configSource;
public String getConfigSource() {
return configSource;
}
public void setConfigSource(String configSource) {
this.configSource = configSource;
}
}
You can use #PropertySource annotation to read the yml file , you can read the below article :
https://www.baeldung.com/properties-with-spring

#TestPropertySource - Values not set / set as null from test properties file

My Junit is not picking up properties set in test properties file.
I get no error, but value returned from properties file is null
CLASS TO BE TESTED:
package com.abc.mysource.mypackage;
#Component
#ComponentScan
public class MyHelper {
#Autowired
#Qualifier("commonProperties")
private CommonProperties commonProperties;
public LocalDateTime method1ThatUsesCommonProperties(LocalDateTime startDateTime) throws Exception {
String customUserType = commonProperties.getUserType(); // Returns null if run as JUnit test
//Further processing
}
}
SUPPORTING COMPONENTS - BEANS & CONFIG CLASSES:
package com.abc.mysource.mypackage;
#Component
public class CommonProperties {
#Value("${myhelper.userType}")
private String userType;
public String getUserType() {
return userType;
}
public void setCalendarType(String userType) {
this.userType = userType;
}
}
CONFIG CLASS:
package com.abc.mysource.mypackage;
#Configuration
#ComponentScan(basePackages ="com.abc.mysource.mypackage.*")
#PropertySource("classpath:default.properties")
public class CommonConfig {}
default.properties under src/main/resources
myhelper.userType=PRIORITY
MY TEST CLASS:
package com.abc.mysource.mypackage.test;
#RunWith(SpringRunner.class)
#ContextConfiguration(classes=MyHelper.class)
#TestPropertySource("classpath:default-test.properties")
#EnableConfigurationProperties
public class MyHelperTest {
#MockBean(name="commonProperties")
private CommonProperties commonProperties;
#Autowired
private MyHelper myHelper;
#Test
public void testMethod1ThatUsesCommonProperties() {
myHelper.method1ThatUsesCommonProperties();
}
}
default-test.properties defined under /src/test/resources:
myhelper.userType=COMMON
NOTE:
I moved default-test.properties to /src/main/resources - commonProperties.getUserType() is null
I even used #TestPropertySource(properties = {"myhelper.userType=COMMON"}). Same result
NOTE 2:
I tried the solution on #TestPropertySource is not loading properties.
This solution requires me to create a duplicate bean called CommonProperties under src/test/java. But #MockBean fails when I do
#MockBean(name="commonProperties")
private CommonProperties commonProperties;
Please do not mark a duplicate.
NOTE 3:
Mine is a spring, not a spring boot application.
MockBeans are suited if you don't need specific state. Usually this bean is "isolated" and every method call of this bean will have the same result. It is "isolated" -> the service that uses #Value annotation will not apply on this bean.
What you need is a "normal" bean, properly constructed and initialized. Please use #Autowired annotation and define another bean if needed, using a test profile.

class required a single bean, but 2 were found:

I have the below interface:
public interface MailSender {
void sender(String to, String subject,String body);
}
With 2 imlementation of it:
public class SmtpkMailSender implements MailSender {
static Log log=LogFactory.getLog(MailSender.class);
public void sender(String to, String subject,String body){
log.info("SMTP To: "+to);
log.info("SMTP Subjecy: "+subject);
log.info("SMTP body: "+body);
}
}
and the second one is:
#Primary
public class MockMailSender implements MailSender {
static Log log=LogFactory.getLog(MailSender.class);
public void sender(String to, String subject,String body){
log.info("To: "+to);
log.info("Subject: "+subject);
log.info("body: "+body);
}
}
I used the dependency injection into the controller class which is as following:
#RestController
public class MailController {
#Autowired
private MailSender smtpkMailSender;
#RequestMapping("/send")
public String send(){
smtpkMailSender.sender("Person", "Important", "Take Care");
return "mail is sent";
}
}
At the end i have a configuration class which contains my Beans:
#Configuration
public class MailConfig {
#Bean
public SmtpkMailSender getSmtpkMailSender(){
return new SmtpkMailSender();
}
#Bean
public MockMailSender getMockMailSender(){
return new MockMailSender();
}
}
Unfortunatly, when i run my application it complains with:
Description:
Field smtpkMailSender in com.example.demo.MailController required a single bean, but 2 were found:
- getSmtpkMailSender: defined by method 'getSmtpkMailSender' in class path resource [com/example/mail/MailConfig.class]
- getMockMailSender: defined by method 'getMockMailSender' in class path resource [com/example/mail/MailConfig.class]
Action:
Consider marking one of the beans as #Primary, updating the consumer to accept multiple beans, or using #Qualifier to identify the bean that should be consumed
As you see, although i have specified the MockMailSender as Primary the spring still complains, and cannot identify it
Since you're using java config you should mark config method with #Primary annotation, and not a class:
#Configuration
public class MailConfig {
#Bean
public SmtpkMailSender getSmtpkMailSender(){
return new SmtpkMailSender();
}
#Bean
#Primary
public MockMailSender getMockMailSender(){
return new MockMailSender();
}
}
You can use #Qualifier annotation for specify which specific type of your implementation, you want to autowire.
#RestController
public class MailController {
#Autowired
#Qualifier("smtpkMailSender")
private MailSender smtpkMailSender;
#RequestMapping("/send")
public String send(){
smtpkMailSender.sender("Person", "Important", "Take Care");
return "mail is sent";
}
}
You may simply name your bean using
#Bean(name="mockMailSender")
public MockMailSender getMockMailSender() { }
and then decorate the autowired field with
#Autowired
#Qualifier("mockMailSender")
private MailSender smtpkMailSender;
Another solution for this error is to exclude the Bean that is not used (the duplicated bean), from the autoconfiguration stage within "application.yml" file, as explained in this thread: How to exclude AutoConfiguration classes in Spring Boot JUnit tests?
spring:
autoconfigure:
exclude:
- org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration
- org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration
This is applicable if the error appears after you added a new Maven/Gradle dependency, which duplicates an existing bean that you are using. For example, when both beans come from external JARs, so the source code cannot be updated with #Primary or #Qualifier annotations to solve the error.

Categories