How does spring boot #Value("${somevalue}") annotation work? - spring

I have some #Value annotation in spring-boot project. To Simplify, I have few classes: a restcontroller, service (annotated with #Service) and a pojo.
In each of these classes, I declared a variable as below:
#Value("${somevalue}")
private String somevalueVariable
In the controller class, the value is getting populated as defined in the application.properties. So no problem here.
In the service class, the value is showing up as null. This is my issue, how should i fix it to get the value from the application.properties
In the pojo, the value is showing up as null, I am thinking this is expected behaviour as spring does not manage this class.

Try this:
import org.springframework.beans.factory.annotation.Value;
#Value("#{${somevalue}}")
private String somevalueVariable

ideally service class should have #Service anotation over it, either you missed that or this class is not scanned by spring context, so please add ComponentScan anotation for service class package over main class to scan classes uner this package -
#SpringBootApplication
#ComponentScan({"com.in28minutes.springboot"})
public class Application

It uses Spring Expression Language (SpEL):
https://docs.spring.io/spring-integration/docs/5.3.0.RELEASE/reference/html/spel.html
Also there is 2 #Value : org.springframework.beans.factory.annotation.Value and lombok.Value;
Make sure you are using the right one.
To get value from property try this:
#Value("${systemValue}")
private String systemValue;
For more information I find this useful:
https://www.baeldung.com/spring-value-annotation

Related

Spring #AliasFor not working for #Profile annotation

Environment: Kotlin 1.5.30, SpringBoot 2.5.4(Spring 5.3.9)
Background & Issue
I'm trying to create a composed annotation to simplify similar template annotation codes. The annotation class is like this:
#Profile("default") //NOTE 1: use a placeholder, see the investigations below
annotation class ProfileAware(
#get: AliasFor(annotation = Profile::class, attribute = "value")
val profiles: Array<String>,
//properties delegated for other annotations
)
expected usage:
#Component
#ProfileAware(profiles = ["dev"])
class TheBean {
init {
LoggerFactory.getLogger("TheBean").info("TheBean: I'm registered")
}
}
in application.yaml:
spring:
profiles:
active: dev
But after the application starts, TheBean is not registered as expected.
Investigation & Try
First I've search in github spring repository, and found this: Regression: custom composed #Profile annotation without runtime retention no longer supported with component scanning. So I tried to add #Retention(AnnotationRetention.RUNTIME) on #ProfileAware, but no effect.
Tried to remove ("default") value from meta annotation (and, whether add the default value to profiles attribute or not), but got java.lang.IllegalArgumentException: Must specify at least one profile.
Tried to remove #Profile("default") from meta annotation, but got AnnotationConfigurationException: #AliasFor declaration on attribute 'profile' in annotation ... which is not meta-present.
(Important) Try to use #Profile("dev") directly on bean instead of ProfileAware , works as expected.
(Important) Try to change the value on meta annotaion as "dev", it works, but obviously it is hardcoded and not match my need.
Is there something I did wrong? Or is it possible to create composed annotation with dynamic #Profile value?
Thanks for your reading and help.
#Profile is looked up by org.springframework.context.annotation.ProfileCondition whose matches(...) method uses org.springframework.core.type.AnnotatedTypeMetadata.getAllAnnotationAttributes(String) to look up the #Profile annotations, and the Javadoc for that method states the following (bold emphasis added by me).
Retrieve all attributes of all annotations of the given type, if any (i.e. if defined on the underlying element, as direct annotation or meta-annotation). Note that this variant does not take attribute overrides into account.
Thus, you currently cannot use #Profile with #AliasFor for annotation attribute overrides.

Does every instantiated class in spring load default variables from the application.properties file?

Spring annotations question. I am using a default value like so
#Service
public class MyClient{
#Value("${apikey}")
private String apikey;
public MyClient(){}
}
I imagine that every instantiation of this class should initialize the value of apikey to the default value apikey from the application.properties file. Is this not so? I ask because I am finding apikey to be null after I've instantiated MyClient object.
Your class is annotated #Service and so it is a spring-managed bean. By default all spring beans are singleton so exists in only one instance.
If you try to create this bean manually auto wiring of #value won't work because you bypass the spring initialization.
Btw: it is a bad idea to mix self-managed and spring-managed bean from the same class.

Should we use #Component annotation on the class just to document/indicate that it's a Bean?

Even when we create the Beans in a Spring configuration class, I feel it is still useful to use #Component annotation on the class just to document/indicate that it's a Bean. Is it a good idea? Can there be any other issue that Spring will find the same bean defined in two different ways?
It is bad practice to create two beans from one class...
#Component
class SimpleComponent {
}
#Bean
public SimpleComponent simpleComponentBean(){
new SimpleComponent();
}
By default if we use #Component spring creates bean with name of class, but starts with small letter simpleComponent, if we use #Bean it takes method name and create bean with name simpleComponentBean and we have duplicated beans...
If we have method name the same, as component class name, spring replace one of them and we can Inject unexpected bean.
PS: intellij idea enterprise - correct indicate about all beans.

How to override a Spring #Autowire annotation and set a field to null?

I am a Spring neophyte who is working on a large Spring-based project that has extensive coupling between Spring beans. I am trying to write some integration tests that exercise subsets of the total application functionality. To do so, I'd like to override some of the autowiring.
For example, suppose I have a class
public class MyDataServiceImpl implements MyDataService {
#Qualifier("notNeededForMyDataServiceTest")
#Autowired
private NotNeededForMyDataServiceTest notNeededForMyDataServiceTest;
//...
}
and a context file with:
<bean id="myDataService"
class="MyDataServiceImpl">
</bean>
In my test, I have no need to use the notNeededForMyDataServiceTest field. Is there some way I can override the #Autowired annotation and set notNeededForMyDataServiceTest to null, perhaps in the XML file? I don't want to modify any of the Java classes, but I do want to avoid the (problematic) configuration of notNeededForMyDataServiceTest.
I tried doing:
<bean id="myDataService"
class="MyDataServiceImpl">
<property name="notNeededForMyDataServiceTest"><null/></property>
</bean>
That doesn't work. IntelliJ informs me "Cannot resolve property 'notNeededForMyDataServiceTest'", apparently because there are no getters and setters for that field.
I'm using Spring Framework 3.1.3.
The following configuration should work, I took the liberty of mixing in Java configuration
#Configuration
//This will load your beans from whichever xml file you are using
#ImportResource("classpath:/path/beans.xml")
public class TestConfigLoader{
// This will declare the unused bean and inject MyDataServiceImpl with null.
public #Bean(name="notNeededForMyDataServiceTest") NotNeededForMyDataServiceTest getNotNeededForMyDataServiceTest(){
return null;
}
... any other configuration beans if required.
}
And annotate your test class like so:
// In your test class applicationContext will be loaded from TestConfigLoader
#ContextConfiguration(classes = {TestConfigLoader.class})
public class MyTest {
// class body...
}
These could help:
Context configuration with annotated classes
Testing with #Configuration Classes and Profiles
Spring TestContext Framework
and profiles:
beans profile="..."
Introducing #Profile
You could create different beans definition in the XML configuration and then activate them using the -Dspring.profiles.active="profile1,profile2" env.
You're using the #Autowired mechanism wrong. The qualifier is not a property that you need to set. That's actually the name of a bean, so that the container will be able to choose one particular instance in case multiple beans of the same type are defined in the same context.
So the container will look for a bean of type NotNeededForMyDataServiceTest and the name (which would actually be the bean id in XML): notNeededForMyDataServiceTest.
What I think you want is to instruct the container to not inject anything in that field if no bean of type NotNeededForMyDataServiceTest is defined in the application context. That could be achieved simply by setting the required attribute of the annotation to false:
#Autowired(required = false)
NotNeededForMyDataServiceTest someOptionalDependency;
The only drawback of this approach would be that the container will never complain at runtime if there's nothing to inject in that field (and perhaps you would want this sanity check when your code runs in production).
If you don't want to make that dependency optional (or you can't edit that code for some reason), you'll need to provide a mock / null value for that field by setting that explicitly in your context. One option to do that would be to use Java configuration instead of XML (like in #Abe's answer) and another approach would be to make use of a factory bean which returns null (like in this question).

How to make Spring #autowired annotation to pick a certain type without using #Qualifier?

Is there a way to specify the default type for Spring #autowired annotation? For example, I have bean ValidatorA and bean ValidatorB that extends ValidatorA.
The ValidatorA is used in some other places and is injected using #autowired.
Is there a way to force Spring to inject ValidatorB instead of ValidatorA without changing the existing code?
The only solution I found at How to override the behavior of Spring #Autowired suggests to exclude ValidatorA from the context.
Is there any other way?
For example, with Guice I would just bind the ValidatorA.class to ValidatorB.class and that will do exactly what I need.
I have no problem to add #Qualifier to ValidatorB but ValidatorA should stay unchanged.
Guice provides ability to define "Providers", is there something similar in Spring? Maybe with #Configuration annotation?
I think you could use primary indicator there. If your bean is defined through annotation, add #Primary to ValidatorB definition. If you are using xml config add primary="true" to <bean> definition.
Is there a way to force Spring to inject ValidatorB instead of ValidatorA without changing the existing code?
Without changing the code, I doubt it very much. You could change the app context or alter the code something like this :
#Component
#Qualifier("B")
public class ValidatorB extends ValidatorA {
}
and then inject like so (there are other syntactic options as well)
#Resource
#Qualifier("B")
private ValidatorA validatorB;

Resources