Spring Boot: Autowiring app credentials within custom autoconfiguration bean returns null - spring

I am working with a custom AWS Simple System Management client just to avoid the original from using the default AWS authentication chain, so I placed my class in /META-INF/spring.factories and excluded the original from being autconfigured in bootstrap.yml . What I'm facing right now is to get the credentials from application.yml and pass them to my new conf, but when I try to autowire them all I get is null. I wonder if there is any way to achieve it
Here is the code:
package es.example;
import lombok.*;
import org.springframework.boot.context.properties.*;
#ConfigurationProperties(prefix = "aws.credentials")
#Data
public class CustomAWSSSMAuthProperties {
private String accessKey;
private String secretKey;
}
package es.example;
import com.amazonaws.services.simplesystemsmanagement.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.boot.context.properties.*;
#EnableConfigurationProperties(CustomAWSSSMAuthProperties.class)
public class CustomAWSSSMClient extends AWSSimpleSystemsManagementClient {
#Autowired
private CustomAWSSSMAuthProperties customProperties;
public CustomAWSSSMClient() {
String accessKey = customProperties.getAccessKey();
String secretKey = customProperties.getSecretKey();
}
}
/META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
es.example.CustomAWSSSMClient
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
es.example.CustomAWSSSMClient
bootstrap.yml
spring:
cloud:
config:
uri: ${SPRING_CONFIG_URI:http://localhost:8888}
autoconfigure.exclude: com.amazonaws.services.simplesystemsmanagement.AWSSimpleSystemsManagementClient
Many thanks

#ConfigurationProperties does not create a bean like #Configuration or #Component does. In your case CustomAWSSSMAuthProperties type bean/object will not be instantiated in the Spring context.
Generally #ConfigurationProperties is used with #Configuration or #Bean to bind the some properties to the bean.
You can annotate CustomAWSSSMAuthProperties with #Configuration to fix the issue.

Related

Spring Kotlin #ConfigurationProperties for data class defined in dependency

I've got a library that has a configuration class (no spring configuration class) defined as a data class. I want a Bean of that configuration which can be configured via application.properties. The problem is that I don't know how to tell Spring to create ConfigurationProperties according to that external data class. I am not the author of the configuration class so I can't annotate the class itself. #ConfigurationProperties in conjunction with #Bean does not work as the properties are immutable. Is this even possible?
Maybe change scan packages to inlcude the packages that do you want.
#SpringBootApplication( scanBasePackages = )
take a look this:
Configuration using annotation #SpringBootApplication
If I understand correctly, do you need a way to turn a third-party object into a bean with properties from your application.properties file ?
Given an application.properties file:
third-party-config.params.simpleParam=foo
third-party-config.params.nested.nestedOne=bar1
third-party-config.params.nested.nestedTwo=bar2
Create a class to receive your params from properties file
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.context.annotation.Configuration
#Configuration
#ConfigurationProperties(prefix = "third-party-config")
data class ThirdPartConfig(val params: Map<String, Any>)
Here is an example of the object that you want to use
class ThirdPartyObject(private val simpleParam: String, private val nested: Map<String, String>) {
fun printParams() =
"This is the simple param: $simpleParam and the others nested ${nested["nestedOne"]} and ${nested["nestedTwo"]}"
}
Create the configuration class with a method that turns your third-party object into an injectable bean.
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
#Configuration
class ThirdPartObjectConfig(private val thirdPartConfig: ThirdPartConfig) {
#Bean
fun thirdPartyObject(): ThirdPartyObject {
return ThirdPartObject(
simpleParam = thirdPartConfig.params["simpleParam"].toString(),
nested = getMapFromAny(
thirdPartConfig.params["nested"]
?: throw IllegalStateException("'nested' parameter must be declared in the app propertie file")
)
)
}
private fun getMapFromAny(unknownType: Any): Map<String, String> {
val asMap = unknownType as Map<*, *>
return mapOf(
"nestedOne" to asMap["nestedOne"].toString(),
"nestedTwo" to asMap["nestedTwo"].toString()
)
}
}
So now you can inject your third-party object as a bean with custom configurated params from your application.properties files
#SpringBootApplication
class StackoverflowAnswerApplication(private val thirdPartObject: ThirdPartObject): CommandLineRunner {
override fun run(vararg args: String?) {
println("Running --> ${thirdPartObject.printParams()}")
}
}

Autowire Bean and application.yml file in another jar file

I am experimenting with spring boot multi module projects for my understanding.
My Over All Goal is :
1.Build Spring boot project as independent jar and utilise it on another project.
2.Autowire Bean properties inside jar as per new project. Make it independent.
Things I have done so far.
Project providerModule1
Declare a Service(MyService).
#Component
public class MyService {
#Autowired
ServiceProperties serviceProperties;
public String getInfoFromProperties() {
return serviceProperties.toString();
}
}
Create a bean called ServiceProperties that will be used in to MyService.
package com.demo.multimodule.providerModule1.util;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import lombok.Data;
#Component
#Data
public class ServiceProperties {
#Value("${default.userName}")
private String name;
#Value("${default.email}")
private String email;
#Value("${default.age}")
private String age;
}
Load the bean ServiceProperties by reading propeties from yml file.
default:
userName: userName1
email: default#email.com
age: 18
Build the Project ProviderModule1 using maven plugin
Project ParentProjectApplication
5. Load the maven dependency.
<dependency>
<groupId>com.demo.multimodule</groupId>
<artifactId>providerModule1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<classifier>app-to-import</classifier>
</dependency>
Autowire the MyService from project providerModule1
package com.multimodule.demo.parentProject.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.demo.multimodule.providerModule1.service.MyService;
#Service
public class HomeService {
#Autowired
private MyService myService;
public String getHomeInfo() {
return "Home Info from Home service : "+myService.getInfoFromProperties();
}
}
Initially myservice was not getting loaded .Hence I added this step to main application class:
#SpringBootApplication(scanBasePackages= {"com.demo.multimodule.providerModule1.service",
"com.demo.multimodule.providerModule1.util"})
public class ParentProjectApplication {
}
Question 1 : Is this the only way using which I can autowire bean from a jar. If there is another way let me know.
I executed the Project ParentProjectApplication and it seem to work as expected.
Question 2 : Is it possible to autowire new yml property from Project ParentProjectApplication and make ServiceProperties bean of project ProviderModule1 utilise it.
you can try spring.factory to create beans.
refer the link below
https://docs.spring.io/spring-boot/docs/2.0.0.M3/reference/html/boot-features-developing-auto-configuration.html

Spring Java Config: configure already existing bean

I want to configure in my #Configuration class bean which is already created by other library's autoconfiguration. I just need to change some fields in that class after it's being initialized.
But I can't find a proper way how to provide code block in #Configuration class and not using #Bean annotation. Is there an ideomatic way to do so in spring?
One way to do this:
#Configuration
class TestConfig {
#Autowired
private SomeBean someBean;
#PostConstruct
private void initSomeBean() {
// someBean.setProperty("qwe");
}
}
#PostConstruct annotation defines init-method, which is getting called after SomeBean is autowired. In this method you can adjust your bean
Do you want to import one Config in AnotherConfig on? It can be done via annotation placed on AnotherConfig:
import org.springframework.context.annotation.Import;
...
#Import(value = {Config.class})
public class AnotherConfig ... {}

Using Spring boot, I am unable to refer a class present in another jar

I am trying to build a spring boot web application. I want to refer a class from another jar. The class name is SalaryHandler.
I have done the following configuration in the class having
#SpringBootApplication annotation:
#Bean
public SalaryHandler iSalary() {
return new SalaryHandler();
}
In the class, where it is required, I have used autowiring annotation like this:
package hello;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import com.salary.SalaryHandler;
//#Service - not working
//#Component - not working
public class SalaryDelegatorImpl implements SalaryDelegator {
#Autowired
private SalaryHandler iSalary;
#Override
public void show() {
iSalary.testSalary();
}
}
The code is compiling fine, but when this iSalary object is used to call its method, nullpointer exception is thrown.
Just to note that SalaryHandler is present inside another jar and is not using any spring annotation, its code is as below:
package com.salary;
public class SalaryHandler implements ISalary {
public void testSalary() {
System.out.println("Salary test successful...");
}
}
you need to attempt Autowire with #Component. In order to get this to work, you'll have to annotate a method in your #Configuration class. Something like this should allow you to autowire the class:
#Configuration
#ComponentScan("com.package.where.my.class.is")
public class ConfigClass{
#Bean
public JPADataService jpaDataService(){
return new JPADataService();
}
}
I am able to fix this. The problem was somewhere inside code, I was calling SalaryDelegatorImpl using new operator(from inside a factory class), so that was not being managed by Spring. As a result, the #Autowired on SalaryHandler, was not working.
I changed my factory to be spring managed, and then it worked fine.
Thanks everyone for the support.

Spring Boot #Value Properties

I have a Spring Boot application and in one of the classes, I try to reference a property from the application.properties file using #Value. But, the property does not get resolved. I have looked at similar posts and tried following the suggestions, but that didn't help. The class is:
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class PrintProperty {
#Value("${file.directory}")
private String fileDirectory;
public void print() {
System.out.println(fileDirectory);
}
}
I have the property file.directory in application.properties. I have other fields as well.
I had the same problem like you. Here's my error code.
#Component
public class GetExprsAndEnvId {
#Value("hello")
private String Mysecret;
public GetExprsAndEnvId() {
System.out.println("construct");
}
public void print(){
System.out.println(this.Mysecret);
}
public String getMysecret() {
return Mysecret;
}
public void setMysecret(String mysecret) {
Mysecret = mysecret;
}
}
This is no problem like this, but
we need to use it like this:
#Autowired
private GetExprsAndEnvId getExprsAndEnvId;
not like this:
getExprsAndEnvId = new GetExprsAndEnvId();
Here, the field annotated with #Value is null because Spring doesn't know about the copy of GetExprsAndEnvId that is created with new and didn't know to how to inject values in it.
Make sure your application.properties file is under src/main/resources/application.properties. Is one way to go. Then add #PostConstruct as follows
Sample Application.properties
file.directory = somePlaceOverHere
Sample Java Class
#ComponentScan
public class PrintProperty {
#Value("${file.directory}")
private String fileDirectory;
#PostConstruct
public void print() {
System.out.println(fileDirectory);
}
}
Code above will print out "somePlaceOverhere"
I´d like to mention, that I used spring boot version 1.4.0 and since this version you can only write:
#Component
public class MongoConnection {
#Value("${spring.data.mongodb.host}")
private String mongoHost;
#Value("${spring.data.mongodb.port}")
private int mongoPort;
#Value("${spring.data.mongodb.database}")
private String mongoDB;
}
Then inject class whenever you want.
EDIT:
From nowadays I would use #ConfigurationProperties because you are able to inject property values in your POJOs. Keep hierarchical sort above your properties. Moreover, you can put validations above POJOs attributes and so on. Take a look at the link
To read the values from application.properties we need to just annotate our main class with #SpringBootApplication and the class where you are reading with #Component or variety of it. Below is the sample where I have read the values from application.properties and it is working fine when web service is invoked. If you deploy the same code as is and try to access from http://localhost:8080/hello you will get the value you have stored in application.properties for the key message.
package com.example;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#SpringBootApplication
#RestController
public class DemoApplication {
#Value("${message}")
private String message;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#RequestMapping("/hello")
String home() {
return message;
}
}
Try and let me know
You haven't included package declarations in the OP but it is possible that neither #SpringBootApplication nor #ComponentScan are scanning for your #Component.
The #ComponentScan Javadoc states:
Either basePackageClasses or basePackages (or its alias value) may be
specified to define specific packages to scan. If specific packages
are not defined, scanning will occur from the package of the class
that declares this annotation.
ISTR wasting a lot of time on this before and found it easiest to simply move my application class to the highest package in my app's package tree.
More recently I encountered a gotcha were the property was being read before the value insertion had been done. Jesse's answer helped as #PostConstruct seems to be the earliest you can read the inserted values, and of course you should let Spring call this.
I had the similar issue and the above examples doesn't help me to read properties. I have posted the complete class which will help you to read properties values from application.properties file in SpringBoot application in the below link.
Spring Boot - Environment #Autowired throws NullPointerException
Your problem is that you need a static PropertySourcesPlaceholderConfigurer Bean definition in your configuration. I say static with emphasis, because I had a non-static one and it didn't work.
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
I had the same issue get value for my property in my service class. I resolved it by using #ConfigurationProperties instead of #Value.
create a class like this:
import org.springframework.boot.context.properties.ConfigurationProperties;
#ConfigurationProperties(prefix = "file")
public class FileProperties {
private String directory;
public String getDirectory() {
return directory;
}
public void setDirectory(String dir) {
this.directory = dir;
}
}
add the following to your BootApplication class:
#EnableConfigurationProperties({
FileProperties.class
})
Inject FileProperties to your PrintProperty class, then you can get hold of the property through the getter method.

Resources