Autowiring a bean based on a property - java

I have two beans, beanA and beanB, in my Spring config. Both of these beans implement the same interface. I have a class with an autowired field of the interface type (i.e. it will be populated with an instance of beanA or beanB).
Initially there was only one bean, so I simply used the #Autowired annotation and the field was populated. However, now there's two potential beans that could be autowired. I want to autowire the bean based on the existence of a property in one of my .properties resources. Is there any elegant way to do this?
The solution I'm using now is to use the #Qualifier annotation on the autowired field to specify beanA and then make a check to see if the property exists in code. If it does, I reassign the field to an instance of beanB. It's a very clunky way of doing it, so I'm looking for a better option.

Apart from the newer feature of bean profiles, you can also take advantage of FactoryBean which instantiate a bean at the time of access. The idea is to inject the FactoryBean with the bean types (e.g. fqcn.BeanA or fqcn.BeanB). Then factory bean will return the bean factory to instantiate the correct type of the bean that you may need. The configuration of FactoryBean then can take advantage of properties coming from a resource bundle.

Bean profiles could be great fit for this - based on the "active" profile let one or the other bean be created.
Somewhat of an older article, but is still a good reference to profiles in Spring 3.1- http://blog.springsource.com/2011/02/11/spring-framework-3-1-m1-released/

Spring Profile can help
Configuration Changes
web.xml
<context-param>
<param-name>spring.profiles.active</param-name>
<param-value>a</param-value>
</context-param>
spring-beans.xml with profiles. Profile value can behave as the
property value for decision making.
<bean id="A" profile="a"/>
<bean id="B" profile="b"/>

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)?

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.

Singleton in Java and Spring [duplicate]

Could you please explain why Spring is creating two objects for the configuration of beans shown below, since by default spring default scope is singleton?
The Spring configuration is here:
<bean id="customer" class="jp.ne.goo.beans.Customer">
<property name="custno" value="100"></property>
<property name="custName" value="rajasekhar"> </property>
</bean>
<bean id="customer2" class="jp.ne.goo.beans.Customer">
<property name="custno" value="200"></property>
<property name="custName" value="siva"></property>
</bean>
Spring's default scope is singleton. It's just that your idea of what it means to be a singleton doesn't match how Spring defines singletons.
If you tell Spring to make two separate beans with different ids and the same class, then you get two separate beans, each with singleton scope. All singleton scope means is that when you reference something with the same id, you get the same bean instance back.
Here is how the Spring documentation defines singleton scope:
Only one shared instance of a singleton bean is managed, and all requests for beans with an id or ids matching that bean definition result in that one specific bean instance being returned by the Spring container.
Singleton scope means using the same id retrieves the same bean, that is all. Testing that no two ids referenced the same class would get in the way of using maps as beans, and would be complicated by proxying things with BeanFactories.
For Spring to police this would involve a lot of work for little benefit. Instead it trusts the users to know what they're doing.
If you want a bean’s singleton-ness preserved across multiple names, that is do-able. You can have more than one name refer to the same bean, that is done by using an alias:
In a bean definition itself, you can supply more than one name for the bean, by using a combination of up to one name specified by the id attribute, and any number of other names in the name attribute. These names can be equivalent aliases to the same bean, and are useful for some situations, such as allowing each component in an application to refer to a common dependency by using a bean name that is specific to that component itself.
Specifying all aliases where the bean is actually defined is not always adequate, however. It is sometimes desirable to introduce an alias for a bean that is defined elsewhere. This is commonly the case in large systems where configuration is split amongst each subsystem, each subsystem having its own set of object definitions. In XML-based configuration metadata, you can use the element to accomplish this.
So if you add a name in the bean configuration:
<bean id="customer" name="customer2"
class="jp.ne.goo.beans.Customer">
</bean>
or create an alias for a bean defined elsewhere:
<alias name="customer" alias="customer2"/>
then "customer" and "customer2" will refer to the same bean instance.
Spring default scope is singleton and it will create one object for all instances unless you explicitly specify the scope to be prototype. You have not posted spring configuration. Please post it, it will give a better idea.
In Spring Singleton refers to One bean per Spring container where as in Java Singleton refers to one object per class loader.
So Spring singleton is not same as java singleton. Don't get confused between these two.
You're confusing two different concepts.
The word singleton in spring is used for a bean scope, meaning that the bean will be created only once for the whole application.
Singleton usual meaning refers to the GOF pattern. It is an object oriented pattern guarantying that only one instance of a class will exists (at least in the scope of the classLoader).
You are declaring two beans of the same class. That isn't the same.
#Component("springTestClass")
public class SpringTestClass{
private int randomNumber = 0;
public SpringTestClass(){
randomNumber = new Random().nextInt(2000);
}
public int getRandomNumber(){
return this.randomNumber;
}
}
And try to access this bean in two places the number will be the same. But what you have done was creating two separate beans.
If you want to check if this works try:
public class Main{
public static void main(String[] args){
ApplicationContext ctx = ....;
SpringTestClass testObject1 = (SpringTestClass)ctx.getBean("springTestClass");
SpringTestClass testObject2 = (SpringTestClass)ctx.getBean("springTestClass");
System.out.println(testObject1.getRandomNumber() == testObject2.getRandomNumber());
}
}
This code should return true if it is same instance;
But in SpringTestClass you can add #Scope("prototype") annotation.
The output will be false
Like others have mentioned, two beans should be created from the code you posted. Singletons are defined as follows (from the Spring documentation: Singleton Scope)
Only one shared instance of a singleton bean is managed, and all requests for beans with an id or ids matching that bean definition result in that one specific bean instance being returned by the Spring container.
To add clarity to this, the meaning behind "shared instance" is explained in the paragraph that follows the one above:
all subsequent requests and references for that named bean return the cached object
When a singleton bean is created, only one bean object is instantiated and cached. This refers only to the bean, not to whatever class the bean may be an instance of. For example,
<bean id="myBean" class="myPackage.myClass" />
<bean id="myOtherBean1 class="myPackage.myOtherClass1">
<property name="beanReference1" ref="myBean" />
</bean>
<bean id="myOtherBean2 class="myPackage.myOtherClass2">
<property name="beanReference2" ref="myBean" />
</bean>
In this made up configuration, "myOtherBean1" and "myOtherBean2" have references to the same "myBean" bean therefore the same "myPackage.myClass" instance. If you changed the code to add a second "myPackage.myClass" bean, it would be distinct from "myBean".
To fully understand this, also refer to the other Spring scope: the prototype. From the Spring documentation for Prototype Scope:
The non-singleton, prototype scope of bean deployment results in the creation of a new bean instance every time a request for that specific bean is made.
This means that if we were to use the same Spring XML as above, "myOtherBean1" and "myOtherBean2" would each receive their own distinct copies of "myBean" which is still just an instance of "myPackage.myClass".
Spring Singleton Bean Does not work like Java Singleton.
If we write
ApplicationContext ctx = new ClassPathXmlApplicationContext("MyConfig.xml");
Customer obj1= (Customer) ctx.getBean("customer");
Customer obj2 = (Customer) ctx.getBean("customer2");
System.out.println(obj1 == obj2);
System.out.println(obj1+ "::" + obj2);
If we see the output it will return 2 different Instance.
According to Spring Docs
Bean is singleton only one shared Instance will be managed, and all request beans with an ID or ID matching that bean definition. Here 2 different ID is available.
Spring container as managing Key value pair, Key as ID/Name and value is bean.
The following example shows a #Bean annotated method being called
twice:
#Configuration
public class AppConfig {
#Bean
public ClientService clientService1() {
ClientServiceImpl clientService = new ClientServiceImpl();
clientService.setClientDao(clientDao());
return clientService;
}
#Bean
public ClientService clientService2() {
ClientServiceImpl clientService = new ClientServiceImpl();
clientService.setClientDao(clientDao());
return clientService;
}
#Bean
public ClientDao clientDao() {
return new ClientDaoImpl();
}
}
clientDao() has been called once in clientService1() and once in
clientService2(). Since this method creates a new instance of
ClientDaoImpl and returns it, you would normally expect having 2
instances (one for each service). That definitely would be
problematic: in Spring, instantiated beans have a singleton scope by
default. This is where the magic comes in: All #Configuration classes
are subclassed at startup-time with CGLIB. In the subclass, the child
method checks the container first for any cached (scoped) beans before
it calls the parent method and creates a new instance. Note that as of
Spring 3.2, it is no longer necessary to add CGLIB to your classpath
because CGLIB classes have been repackaged under
org.springframework.cglib and included directly within the spring-core
JAR.
spring default scope is singleton. Once the bean will be created and same bean used throughout its lifecycle.

Spring bean loading order

I've two bean definitions in my application-context.xml (BeanClassA, BeanClassB). If BeanClassA has an #Autowired annotation to BeanClassB, Should the BeanClassB be declared BEFORE BeanClassA in the bean definitions in application-context.xml? More generically is there an inherent order in which the beans would be loaded..
No. Unless you have some complex circular dependencies, Spring will be able to publish all injections before making any beans available to you. The order of declarations in XML is not important.

Categories