How does #Value work in SpEL? - java

I have a class A with a date field annotated with #Value to take as default value the current date.
When I create a bean in an XML file, this field appears to be initialized with the current date, as expected.
When I create a new object with new A() in main of a test class, the field is null.
My question is how does #Value work. In the documentation, it said it gives a default value to a field, but does it work only for beans? How is it processed?

When creating the bean using the XML file, Spring creates the bean and applies the injection of configured fields, via XML configuration or decorated by annotations e.g. #Autowired, #Value, etc...
When creating the bean using new YourClass, then you're manually creating an instance of the class, thus not being handled by Spring.
If you want Spring to handle new beans created on demand i.e. using new keyword, you can refer to Spring injecting or autowiring datasource bean to class, point 3 and the explanation at the bottom using #Configurable.

Related

Why does #Value as parameter of constructor fill the property correctly?

My question is: Why does this piece of code correctly set the constructor parameter property port:
private final RedisServer redisServer;
public RedisTestConfiguration(#Value("${cache.port}") final int port) {
this.redisServer = new RedisServer(port);
}
As of my understanding, #Value("${cache.port}") is resolved by a BeanPostProcessor called AutowiredAnnotationBeanPostProcessor. Spring bean lifecycle works in a way that the constructor method is called before any BeanPostProcess, see picture below. Note The constructor is called before BeanPostProcessor.postProcessBeforeInitialization().
How come this is still working?
Regards,
Bart
This behavior is supported by PropertySourcesPlaceholderConfigurer
Ref: https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-factory-placeholderconfigurer
PropertySourcesPlaceholderConfigurer is a BeanFactoryPostProcessor. It collects #Value fields and updates bean definition in spring container. PropertySourcesPlaceholderConfigurer should be created in the container before any bean is initialized.
Bean metadata is described here in the documentation:
https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-factory-metadata
So, the flow is the following:
1. Bean definition readers collect beans declarations in xml files or from java classes. Example, XmlBeanDefinitionReader.
Ref: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/xml/XmlBeanDefinitionReader.html
Bean Factory Post Processors update bean definitions. Example, PropertySourcesPlaceholderConfigurer.
Spring container looks at the bean definition and creates beans(calls constructor) according to bean definition values.
Ref: https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-factory-class
So, the problem is that
As of my understanding, #Value("${cache.port}") is resolved by a BeanPostProcessor is not correct. #Value is managed by BeanFactoryPostProcessor, not BeanPostProcessor
Actually, documentation states:
A default lenient embedded value resolver is provided by Spring. It will try to resolve the property value and if it cannot be resolved, the property name (for example ${catalog.name}) will be injected as the value. If you want to maintain strict control over nonexistent values, you should declare a PropertySourcesPlaceholderConfigurer bean, as the following example shows:...
Spring has some kind of default property resolver, that is another BeanFactoryPostProcessor. But it is possible to override it with PropertySourcesPlaceholderConfigurer

Constructor bean injection with custom annotation

I would like to inject a bean (lets call it clientStub) into my service bean.
There are two requirements for that:
In order to create the clientStub bean, I need to access the #Client annotation, that carries some important information that are used to lookup the relevant configuration in the properties.
It must support constructor based injection (and #Bean method parameter injection)
Expected usage:
#Autowired
public MyService(
#Client("invoice-manager") InvoiceManagerClientStub clientStub,
SomeOtherBean bean1, ...) {
or
#Bean
MyService myService(
#Client("invoice-manager") InvoiceManagerClientStub clientStub,
SomeOtherBean bean1, ...) {
So I would like to do the same as the #Value annotation e.g. derive the value from the annotation on the parameter (plus some internal lookups).
Unfortunately, the usual BeanFactorys don't seem to be aware of the target's annotations.
Alternatives considered
Using an BeanPostProcessor to inject the values into annotated fields. Well, this is what I'm currently doing, but it doesn't feel right to have an immutable class with a single setter for injection.
Injecting it to a field and then exposing it as a bean or manually invoking the constructor is even worse.
Creating a proxy instance of the injected class. This isn't possible since the injected classes are generated + final and not part of my library.
Derive the configuration from the bean name: Not sure how to implement this and how to explain the user what the configuration parameters should be named in their properties files. I also would like to avoid bean name conflicts.
Non-Goals
Overwriting any core beans. I would like to distribute the extension as a library (spring-boot based), so any excessive replacement of spring internal beans should be avoided.
TLDR
How do I tell spring to resolve the parameter using my annotation's value (resolver)?

spring placeholder - replace value from a bean

I want to choose a property file based which is decided by calling a method in another bean. Example
#Component
#PropertySource("classpath:app-${app.getName('value')}.properties")
class AppConfig{}
class App is a bean in my spring boot program.
Is there a way to do it?
TLDR: I want to provide a value in placeholder by calling a method from other bean.

Restricting injection from a bean definition, not from the autowiring end (bean not injectable implictly)

Is there any annotation/trick in Spring that marks a #Configuration #Bean as injectable only on strictly qualified #Autowireds?
I'd like my bean to be qualified in a way that only those that specifically call for its #Qualifer can inject it. It's indeed a mechanism for controlling where is it going to be able to be autowired, with no ambiguity nor arbitrary decisions depending on available beans in the context.
So my bean would never be autowired as a side-effect out-of-my control without me actively marking the injecting as expecting it
You can control bean creation with #Conditional annotation.
Also if you need real control of using bean you can create some annotation like #ConroledByQualifer and use this annotation instead of standard spring component annotation , or extension for one (like service , repository .... ) .
So spring can't process it for autowired as one don't know how to handling it.
You need add custom BeanPostProcessor that will work with #ConroledByQualifer - create it and inject. So your custom bean will be processed only by BeanPostProcessor for #ConroledByQualifer and not custom spring BeanPostProcessors.

#Value place holder not resolving values in certain spring beans

I have a situation and im trying to figure out why it is so. The scenario is I have a class A that implements an operation exposed by a webservice. For each request for the operation a new instance of A is created. The entire thing is manged by Spring internally and I am not sure how exactly the Class A in instantiated by the webservice.
Now i have a class variable whose value i want to inject during runtime through
#Value("${someValue}")
String someVariable
someValue is defined a system property. But each of the beans created has someVariable as NULL.
I tried the same thing in another class annotated with #Configuration in the same package and it is resolving the variable fine.
So, Im thinking if the ability to inject through #Value depends on the way a bean is created like #Component, #Configuration-#Bean, XML definiti
Not really. Actually You can inject value with #Value in both the component scan bean and xml definition bean. But pay attention to this, It depends on which xml file that you put the Placeholder in, it just valids in the per-container. For example, you put it in applicationContext, then the bean create in xxx-servlet.xml can not inject value with #Value.

Categories