Get value from application.properties without Main application - java

I' m working in a Spring Boot project, consisted in several applications booted with embedded Tomcat and a unique application used only as library for running applications. In that library application I need to retrieve a value from application.properties file for a variable. Being it a library application, it has not a Main class, so the variable will be always null.
My try was these:
public class AuthAdapter implements IAuthAdapter {
#Value("${signin.key}")
private String key;
#Override
public String getSigningKey() {
return key;
}
}
When the getSigninKey() is invoked by an application that uses it, the variable key is null.
How can I do to fill the variable key with the value present on appliaction.properties file, considering the situation explained before?

You need to annotate your AuthAdapter class with #Service/#Component/#Repository/#Configuration based on your requirement. By doing so, spring boot will inject the application.properties to your class during the spring context initialization.

#Value Spring annotation
This annotation can be used for injecting values into fields in Spring-managed beans
So make AuthAdapter as spring bean in some config class
#Configuration
public class ApplicationConfig {
#Bean
public AuthAdapter getAuthAdapter() {
return new AuthAdapter():
}
}
with property in application.yml or application.properties
application.yml
signin:
key: value
Or if AuthAdapter is annotated with any stereotype annotations here, Just enable that class in ComponentScan
#SpringBootApplication
#ComponentScan({ "com.project1", "com.library.AuthAdapter" })
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class);
}
}

If the variable key==null, this means that Spring did not inject it.
This could come from sevreral reasons:
the property "signin.key" is not present in the application.properties
SpringBoot did not start => make sure that in your application there is a main annoted with #springbootapplication
The AuthAdapter is not instanciate by Spring:
=> The AuthAdapter shall not be instanciate like:
authAdapter = new AuthAdapter();
Instead, it must be instanciate and injected by Spring itself like:
#Autowired
private authAdapter AuthAdapter;
For that the AuthAdapter must be annoted with a springBoot annotation:
#Service/#Component/#Repository/#Configuration.
Finally, the package containing the AuthAdapter class must be in the component scan perimeter to tell Spring to instanciate it.

Make sure that the "signin.key" is present in the application.properties and that the is present in the jar generated

Related

Spring Boot: #TestConfiguration Not Overriding Bean During Integration Test

I have a Bean defined in a class decorated with #Configuration:
#Configuration
public class MyBeanConfig {
#Bean
public String configPath() {
return "../production/environment/path";
}
}
I have a class decorated with #TestConfiguration that should override this Bean:
#TestConfiguration
public class MyTestConfiguration {
#Bean
#Primary
public String configPath() {
return "/test/environment/path";
}
}
The configPath bean is used to set the path to an external file containing a registration code that must be read during startup. It is used in an #Component class:
#Component
public class MyParsingComponent {
private String CONFIG_PATH;
#Autowired
public void setCONFIG_PATH(String configPath) {
this.CONFIG_PATH = configPath;
}
}
While trying to debug this I set a breakpoint inside each method as well as the constructor of the test config class. The #TestConfiguration's constructor breakpoint is hit, so i know that my test configuration class instantiates, however the configPath method of that class is never hit. Instead, the configPath method of the normal #Configuration class is hit and the #Autowired String in MyParsingComponent is always ../production/environment/path rather than the expected /test/environment/path.
Not sure why this is happening. Any thoughts would be greatly appreciated.
As documented in the Detecting Test Configuration section of the Spring Boot reference manual, any beans configured in a top-level class annotated with #TestConfiguration will not be picked up via component scanning. So you have to explicitly register your #TestConfiguration class.
You can do that either via #Import(MyTestConfiguration.class) or #ContextConfiguration(classes = MyTestConfiguration.class) on your test class.
On the other hand, if your class annotated with #TestConfiguration were a static nested class within your test class, it would be registered automatically.
Make sure that the method name of your #Bean factory method does not match any existing bean name. I had issues with method names like config() or (in my case)
prometheusConfig() which collided with existing bean names. Spring skips those factory methods silently and simply does not call them / does not instantiate the beans.
If you want to override a bean definition in your test, use the bean name explicitly as string parameter in your #Bean("beanName") annotation.
Test configuration has to be explicitly imported in the test via #Import({MyTestConfiguration.class}).
The name of the #Bean methods in #Configuration and #TestConfiguration have to be different. At least it makes difference in Spring Boot v2.2.
Also make sure spring.main.allow-bean-definition-overriding=true otherwise the bean could not be overriden.
For me worked this code:
#TestConfiguration // 1. necessary
public class TestMessagesConfig {
#Bean
#Primary // 2. necessary
public MessageSource testMessageSource() { // 3. different method name than in production code e.g. add test prefix
}
}
I struggled with a related problem, whereby even though I was using an inner static class, my test bean was not being registered.
It turns out, You still need to add your inner static class to the #ContextConfiguration class array, otherwise the beans inside the #TestConfiguration doesn't get picked up.
public interface Foo {
String execute();
}
public class FooService {
private final Foo foo;
FooService(Foo foo) {
this.foo = foo;
}
public String execute() {
return foo.execute();
}
}
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = {FooService.class, FooTest.FooTestConfig.class})
public class FooTest {
#Autowired
FooService fooService;
#Test
void test() {
Assertions.assertEquals("MY_TEST_BEAN", fooService.execute());
}
#TestConfiguration
static class FooTestConfig {
#Bean
public Foo getFooBean() {
return () -> "MY_TEST_BEAN";
}
}
}
I came across a similar issue recently and got it sorted out by annotating my testing bean with #Primary as well as #Bean. Not sure why it's required, which seems not documented in the Spring doc. The version of my SpringBoot is 2.0.3.
In my case it was an issue with #RunWith(SpringRunner.class), I'm not exactly sure why it wasn't working, I was following this - Testing in Spring Boot
But after replacing that with #ExtendWith(SpringExtension.class) the inner static #TestConfiguration class created the beans as expected.
Maybe a version mismatch - I'm using Spring Boot 2.7.2.
In my case replacing #Import(TestConfig.class) with #ContextConfiguration(classes=TestConfig.class) did the trick. For some reason, some of the beans from TestConfig but 1 wasn't until I replaced #Import with #ContextConfiguration.
This was also mentioned in some comments that were hidden because they had no upvotes.
I found it odd how several answers stated that the names of the #Beans have to be different from each other. How would that make one override the other?
There wasn't one specific answer that worked for me, but I've solved the issue by combining some of their advices.
Here's what worked for me.
Main configuration class:
#Configuration
public class SpringConfiguration {
#Bean
BeanInterface myBean() {
return new BeanImplementation();
}
#Bean
OtherClass otherBean() {
return new OtherClass();
}
}
Test configuration class:
#TestConfiguration
public class TestSpringConfiguration {
#Bean
#Primary
BeanInterface myBean() {
return new TestBeanImplementation();
}
}
Test class:
#SpringBootTest(classes = TestSpringConfiguration.class,
properties = "spring.main.allow-bean-definition-overriding=true")
public class Tests {
#Test
public void test() {
// do stuff
}
}
In this way, the "myBean" bean instance is the one defined in the TestSpringConfiguration class, while "otherBean" is the one defined in the SpringConfiguration class, since it's not overridden.
If I gave two different names to the "myBean" beans, the "real" one would still be initialized and, in my case, would give an error during tests, since it needs something that's only available at runtime in its proper environment.
Once I gave both the same name, Spring would throw an error saying that they were conflicting. Hence why I had to specify the property spring.main.allow-bean-definition-overriding=true in the #SpringBootTest annotation of the test class.
By the way, if you're NOT using Spring Boot, I guess these alternative annotations could work for you:
#ExtendWith(value = SpringExtension.class)
#ContextConfiguration(loader = AnnotationConfigContextLoader.class, // <- not sure about this one
classes = { SpringConfiguration.class, TestSpringConfiguration.class })
public class Tests {
#Test
public void test() {
// do stuff
}
}
Then, you would still have to set the property spring.main.allow-bean-definition-overriding=true in the test application.yml or application.properties file, or in some other way via code on startup.
Note: I'm not 100% sure that you would need the loader = AnnotationConfigContextLoader.class thing. Try without it, first. I needed it in a project of mine which had Spring without Boot, but I can't remember whether it's a standard thing to set or I needed it for some specific reason.

Access Spring Boot Actuator build.version property with #Value

THis should be easy, but doesn't work.
I have spring-boot-actuator activated in my Spring Boot (2.0.1-RELEASE) application.
The Actuator-Endpoint /actuator/info works as expected and also shows the correct version info. The file build-info.properties is present.
When trying to access the version property with (e.g. in my Spring-Controller-Class):
#Value("${build.version}) private String version;
The action fails with the error Could not resolve placeholder 'build.version' in value "${build.version}".
ANy suggestions?
With spring expression language it is pretty simple and clean.
#Value("#{buildProperties.get('version')}") // not 'build.version'
private String myAppBuildVersion;
Or better, Autowire the buildProperties bean directly to your components so you can play with it as you want.
#Autowired
private BuildProperties buildProperties;
NOTE: The autoconfiguration strips off the build. prefix. So your
SpEL expressions should use version as key. Not build.version.
I needed to add the file build-info.properties as #PropertSource
#SpringBootApplication()
#PropertySource("classpath:META-INF/build-info.properties")
public class MyApp implements WebMvcConfigurer {
[...]
}
Then you can use the build-info in annotations
#SomeAnnotation(value = "${build.version}")
public class someClass { ... }
In your #Service, put the #Value in the constructor, as a parameter of the constructor. Don't use a standalone value and try to reference it.
like this:
#Service
public class myService {
public myService(#Value("${my.param}") String myParam) {
client.withParam(myParam).build();
}
}
Where your application.properties has a value like:
my.param=http://google.com
I have tried other implementations and they do not work. for example,
#Service
public class myService {
#Value("${my.param}")
String myParam;
public myService() {
client.withParam(myParam).build();
}
}
Does not work.
In this case, the service will be initialized, but the string will be null. I can't explain it. Something to do with param construction and bean initialization timing, I guess.

What difference does #EnableConfigurationProperties make if a bean is already annotated with #ConfigurationProperties?

The Spring Boot documentation says that to use the #ConfigurationProperties annotation
You also need to list the properties classes to register in the
#EnableConfigurationProperties annotation, as shown in the following
example:
and gives this code:
#Configuration
#EnableConfigurationProperties(AcmeProperties.class)
public class MyConfiguration {
}
But in the very next paragraph says:
Even if the preceding configuration creates a regular bean for
AcmeProperties, we recommend that #ConfigurationProperties only deal
with the environment and, in particular, does not inject other beans
from the context. Having said that, the #EnableConfigurationProperties
annotation is also automatically applied to your project so that any
existing bean annotated with #ConfigurationProperties is configured
from the Environment.
Suggesting that listing a #ConfigurationProperties bean under an #EnableConfigurationProperties annotation is not necessary.
So which is it? Experimentally, I've seen that if I annotate a bean with #ConfigurationProperties it gets properties injected to it as expected without needing to list it in #EnableConfigurationProperties, but if this is the case then why list anything that has a #ConfigurationProperties annotation under #EnableConfigurationProperties, as is shown in the documentation? Does it make any kind of difference?
As M. Deinum referred #EnableConfigurationProperties Is for enabling support of #ConfigurationProperties. If you take a look to the annotation Java Doc you can see:
Enable support for ConfigurationProperties annotated beans. ConfigurationProperties beans can be registered in the standard way (for example using Bean #Bean methods) or, for convenience, can be specified directly on this annotation. [...]
For example, let's say you have a class whose responsibility is to read and store information from your application.yml / application.properties that is required to make a connection to different databases. You annotate it with #ConfigurationProperties.
Then, you typically have a #Configuration annotated class that provides a DataSource #Bean to your application. You can use the #EnableConfigurationProperties to link it to the #ConfigurationProperties class and init your data sources accordingly.
Here is a small example:
application.yml
data-sources:
db1:
url: "jdbc:postgresql://localhost:5432}/db1"
username: test
password: test
db2:
url: "jdbc:postgresql://localhost:5432}/db2"
username: test
password: test
DataSourcesConfiguration
#ConfigurationProperties
public class DataSourcesConfiguration {
private Map<String, BasicDataSource> dataSources;
public void setDataSources(Map<String, BasicDataSource> dataSources) {
this.dataSources = dataSources;
}
Map<String, BasicDataSource > getDataSources() {
return dataSources;
}
}
DataSourceConnectionConfiguration
#Configuration
#EnableConfigurationProperties(DataSourcesConfiguration.class)
public class DatabaseConnectionConfiguration implements Provider<Connection> {
private DataSourcesConfiguration dataSourcesConfiguration;
public DatabaseConnectionConfiguration(DataSourcesConfiguration dataSourcesConfiguration) {
this.dataSourcesConfiguration = dataSourcesConfiguration;
}
#Bean
public DataSource dataSource() {
// Use dataSourcesConfiguration to create application data source. E.g., a AbstractRoutingDataSource..
}
}
It took me a while to reach to this post but would like to add here so that others may get benefited.
#ConfigurationProperties - Used to bind a class with an externalized property file. Very powerful and must be used to separate out bean classes with configuration entity class.
#Configuration - Creates a Spring bean of configuration stereotype.
#EnableConfigurationProperties - Creates a binding between a configuration entity class and Spring configuration stereotype so that after injection within a service properties can be retrieved easily.
If we look at the code below:
#Configuration #EnableConfigurationProperties #ConfigurationProperties(prefix="ar1")
public class ar1Settings { }
#Configuration tells Spring to treat this as a configuration class and register it as a Bean
#EnableConfigurationProperties tells Spring to treat this class as a consumer of application.yml/properties values
#ConfigurationProperties tells Spring what section this class represents.
My understanding is that if you don't need to specify the section of the property file, then #ConfigurationProperties can be omitted.
#EnableConfigurationProperties imports EnableConfigurationPropertiesRegistrar which enables support for #ConfigurationProperties annotated beans.
#ConfigurationProperties is an annotation for externalized configuration, it is to be applied to a bean configuration class or method annotated with #Bean eg
#ConfigurationProperties(prefix = "some-prefix")
public SomePrefix prefixBean() {
return new SomePrefix();
}
To load the properties and bind them to properties within the method or the class that match the prefix.
ps: some-prefix binds to SomePrefix because of spring's support for Relaxed binding.
Before springboot 2.2, You could do either of the following:
#Configuration
#ConfigurationProperties(prefix = "some-prefix")
public class ConfigProperties {
//...some code
}
or
#Configuration
#EnableConfigurationProperties(SomeClassToBeBounded.class)
public class ConfigProperties {
along with
#ConfigurationProperties(prefix = "some-prefix")
public class SomeClassToBeBounded{
//...some code
}
From springboot 2.2
You can do it in a much easier way:
#ConfigurationProperties(prefix = "some-prefix")
#ConfigurationPropertiesScan
public class ConfigProperties {
//...some code
}
With this, the classpath scanner enabled by #SpringBootApplication finds the ConfigProperties class, even though we didn't annotate this class with #Component.

Cannot #Autowire configuration

I am absolutely new to TestNG, Spring framework etc. and I'm trying to use the annotation #Value access to configuration file via the #Configuration annotation.
All I'm trying to achieve here is to make the console write out "hi" from the config file accessing the value via #Value. I must be obviously missing the whole point of the #Value annotation (or #Autowired or some other annotations) as all I'm gettting is java.lang.NullPointerException.
I have the following three files (reduced to the absolute minimum):
config.properties
a="hi"
TestConfiguration.java
#Configuration
#PropertySource("config.properties")
public class TestConfiguration {
#Value("${a}")
public String A;
}
TrialTest.java
public class TrialTest {
#Autowired
private TestConfiguration testConfiguration;
#Test
public void test() {
System.out.println(testConfiguration.A);
}
}
Thanks a lot.
Try annotate your test class with these:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes={TestConfiguration.class})
[Edit] sorry I didn't see that OP was using TestNG. The essential point is still that the problem is caused by Spring not being bootstrapped. In TestNG that can be done via extending AbstractTestNGSpringContextTests.
Make sure that in your config, you are declaring the PropertySourcesPlaceholderConfigurer bean which can resolve the #Value expressions. Declare this bean:
#Configuration
#PropertySource("config.properties")
public class TestConfiguration {
#Value("${a}")
public String A;
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer()
{
return new PropertySourcesPlaceholderConfigurer();
}
}
Note that you do not have to do anything with this bean, just by declaring it, it will allow the #Value annotation expressions to work as expected.
You can either redundantly declare this bean in every class that uses a #Value annotation, but that would be bad practice/style as it would keep overwriting the bean in each new declaration. Instead, place this bean at the top most config which imports other configs using #Value and you can recycle the PropertySourcesPlaceholderConfigurer bean from the one place.

Access different Implementation/Component with one of same bean in spring boot

#Configuration
#Profile("live")
public class LiveProfileConfig{
#Bean
public BaseInterface commonInterface(){
return new LiveComponent();
}
}
#Configuration
#Profile("test")
public class TestProfileConfig{
#Bean
public BaseInterface commonInterface(){
return new TestComponent();
}
}
Now, in Main Spring boot application, I set the "live" profile as following
#ContextConfiguration(classes = {LiveProfileConfig.class, TestProfileConfig.class},loader=AnnotationConfigContextLoader.class)
#ActiveProfiles(value="live")
Then call one of implmentation to use component according active profile,
#Service
public class AnotherImpl implements AnotherInterface {
#Autowired
BaseInterface commonInterface;
}
Now, I want to access one of component methods (i.e. LiveComponent / TestComponent) based on active profile.
It works in JUnit test but while I try to run it with Spring Boot Application it gives me below error.
required a single bean, but 2 were found, 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
Any help?

Categories

Resources