Do I have any known way to inject beans in spring IoC container based on some condition. E.g. I have two beans:
<bean id="aaa" class="My"/>
<bean id="bbb" class="My"/>
... and want inject it in another bean based on following rule:
Inject "aaa" if "aaa" isn't null or inject "bbb" in other case
Thanks
You can use JavaConfig - there you can use java code to implement this logic. I've never used it, but taking an example from the docs:
#Configuration
public class ServiceConfig {
private #Resource(name="aaa") Aaa aaa;
private #Resource(name="bbb") Aaa bbb;
public #Bean TransferService transferService() {
TransferService service = new TransferServiceImpl();
if (aaa != null) {
service.setProperty(aaa);
} else {
service.setProperty(bbb);
}
return service;
}
}
Another option is to use a FactoryBean to encapsulate that logic - the factory bean can lookup a bean in the context, and if found - return it. If not found - lookup another bean.
If you are on spring 3.0 this can be achieved using SpEL - Expression langauge support.
Related
There is a spring project A which is completely annotation based.
I need to override some beans conditionally in project B which is a legacy application using Spring 4.1.3 and uses xml based config.
There is FooConfig which is configuring beans using #ComponentScan. This config is a third party code for me. i.e I do not have access for this
#ComponentScan(basePackages = {"com.foo.bean"})
#Configuration
public class FooConfig {
}
I have created a BarConfig at my end, which imports this FooConfig and overrides some beans based on a condition. This is achieved using #Conditional
#Configuration
#Import(FooConfig.class)
public class BarConfig {
#Bean(name="helloService")
#Conditional(IsSpanishCondition.class)
public HelloService getHelloService() {
return new HelloService() {
#Override
public String getGreeting(String name) {
return "Hola "+name;
}
};
}
}
And I have included BarConfig in my application-context.xml
<context:annotation-config/>
<bean class="com.foo.config.BarConfig"/>
While this approach works flawlessly in Spring 5.1.2.RELEASE, it does not work in Spring 4.1.3.RELEASE
00:14:20.617 [main] INFO org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader - Skipping bean definition for [BeanMethod:name=getHelloService,declaringClass=com.foo.config.BarConfig]: a definition for bean 'helloService' already exists. This top-level bean definition is considered as an override.
Also, I have observed the same issue in Spring 4 in a completely annotation based context as well. i.e. it is not because of xml and annotation config mix but due to the Spring versions used here
Questions
What changed in Spring 5?
Is there any rule of thumb while working with a Spring application that uses both xml and annotation config especially when it comes to overriding the beans?
Also FTR, these are the solutions that worked
1.Overriding the beans using BeanPostProcessor
2.Using profiles. But this wouldn't work for complicated conditions.
#Profile("ENGLISH")
#Configuration
#Import(FooConfig.class)
public class EnglishConfig {
}
#Profile("SPANISH")
#Configuration
public class SpanishConfig {
#Bean(name="helloService")
public HelloService getHelloService() {
return new HelloService() {
#Override
public String getGreeting(String name) {
return "Hola "+name;
}
};
}
}
The issue here is that you are trying to override a xml bean from a #Configuration class, now I'm not 100% sure, but in spring 4 a xml bean still had precedence in choosing a bean, so the #Configuration beans would not get permission to overwrite the xml bean. Which was resolved in spring 5.
Your approach to use BeanPostProcessor is i guess the only viable solution for this.
I'm thinking maybe you could use a different bean name, implement your own behaviour and use #Qualifier annotation to choose which bean will get selected?
I have an interface Animal having two implementations Cat and Dog. These two implementations are spring #Component. How do I conditionally wire these two based on a value? I understand that I may have to change the scope of MyTestController from singleton to request.
#RestController
public class MyTestController {
#Autowired
Animal animal;// how to wire bean of Cat or Dog based on animalName
#PostMapping("/get-animal")
public #ResponseBody Animal getAnimal(#RequestParam(value = "animalName") String animalName) {
return animal;
}
}
Since both MyTestController is a bean the autowiring / initialisation happens before you actually start using the class instance itself. What I mean is that by the time you actually trigger REST requests on your controller, the injected animal bean should be already there!
More specifically if you have two classes that implement the same interface (Animal) without further specification (active Profiles, or #Primary annotation) Spring won't be able to decide which implementation to inject while creating the MyTestController,
What you want to do is return beans from your ApplicationContext based on a parameter / class name. This would look something like this:
#Autowired
private ApplicationContext context;
/* ... */
if(animalName.equals("dog") {
context.getBean(Dog.class) //returning the dog bean
} else if(animalName.equals("cat") {
context.getBean(Cat.class) //returning the cat bean
}
Edit IMO the question is a bit confusing. You ask for wiring the bean based on a value, but this value only comes at runtime. Hence my answer. However If you want to wire based on some variable at initialisation of your bean I would suggest taking a look at the following sources:
Profiles - With profiles you can tell spring which instance to inject in which configuration. (E.g.: production/development/test configs and for each you want to inject different beans)
Primary - One of your bean takes precedence over the others while injecting it.
Qualifier
Finally I would note that such an inversion on the IoC is considered as a bad practice. (See here)
Well, you're missing the whole point. Don't autowire simple DTO, but autowire AnimalFactory or some kind of AnimalRepository (or better - Service) where you can create or retrieve animals based on animal type.
It would look something like that:
#Component
public class AnimalFactory {
public Animal createAnimal(String animalType) {
switch (animalType) {
case "DOG":
return new Dog();
case "CAT":
return new Cat();
}
throw new IllegalArgumentException("AnimalType is invalid");
}
}
Animal Spring Data JPA Repository:
#Component
public class AnimalRepository implements JpaRepository<Animal, Long> {
public Optional<Animal> findByAnimalName(String animalName);
}
I'm looking into using Spring Boot for a new application but I'm having trouble figuring out the best approach to create the application beans.
At a high-level, this would be a web application that can have one or more beans of the same type - each with different property values. If I need to add a new bean of the same type, I should only have to configure it. Typically, if I was using Spring MVC, I would just define each bean in application context and load in the property values via a context file. Spring Boot prefers to do away with xml config, but I'm not sure how to translate the bean definitions into a Spring Boot solution. How do I still take advantage of IoC using Spring Boot.
Actually this has nothing to do with Spring Boot. As you mentioned, it supports both Java and XML bean configurations.
You can easily create multiple beans out of the same class using Java configuration.
XML config like:
<bean id="first" class="com.foo.MyClass" />
<bean id="second" class="com.foo.MyClass" />
translates into:
#Configuration
class MyConfiguration {
#Bean
MyClass first() {
return new MyClass();
}
#Bean
MyClass second() {
return new MyClass();
}
}
with Maciej Walkowiak's answer, it is also recomended to write it like this:
#Configuration
class MyConfiguration {
#Bean
#Qualifier("first")
MyClass first() {
return new MyClass();
}
#Bean
#Qualifier("second")
MyClass second() {
return new MyClass();
}
}
then later when you autowire you can use:
#Autowired
#Qualifier("second")
private MyClass myClass;
I have recently learned concept of autowiring in spring. When I was trying to understand in which particular scenarios spring autowiring can be useful
I came up with the below two reasons from one of the questions asked in our stakoverflow forum.
1.I wanted to read values from a property file and inject them into a bean. Only way I could figure out how to do this at start up of my app was to
wire the bean in XML (and inject the properties.) I ended up using the "byName" attribute (because the bean was also marked as #Component) and then
used #Autowired #Qualifier("nameIChose") when injecting the bean into another class. It's the only bean I've written that I wire with XML.
2.I've found autowiring useful in cases where I've had a factory bean making another bean (whose implementation class name was described in a system
property,so I couldn't define the all wiring in XML). I usually prefer to make my wiring explicit though;
Can any body please give me some code snippet example of the above situations that would make my understanding of autowiring more clearer?
Here is an example of injecting properties into a bean.
Using field injection:
#Component
public class YourBean {
#Value("${your.property.name}")
private String yourProperty;
}
Using constructor injection:
#Component
public class YourBean2 {
private String yourProperty;
#Autowired
public YourBeans2(#Value("${your.property.name}") String yourProperty) {
this.yourProperty = yourProperty;
}
}
The following is a super simple example of autowiring various beans
#Component
public class Foo {
public void doSomething() {
}
}
#Component
public class Bar {
private Foo foo;
#Autowired
public Bar(Foo foo) {
this.foo = foo;
}
public void doSomethingElse() {
foo.doSomething();
}
}
In the previous example, no XML configuration of Foo and Bar needs to be done, Spring automatically picks up the beans because of their #Component annotation (assuming of course that component scanning has been enabled)
Has anyone tried to auto-wire different beans into a Spring-managed bean based on a condition? For e.g. if some condition is met, inject class A, else B? I saw in one of the Google search results that it is possible with SpEL (Spring Expression Language), but could not locate a working example.
There are multiple ways to achieve this. Mostly this depends on the conditioning you want to perform.
Factory bean
You can implement simple factory bean to do the conditional wiring. Such factory bean can contain complex conditioning logic:
public MyBeanFactoryBean implements FactoryBean<MyBean> {
// Using app context instead of bean references so that the unused
// dependency can be left uninitialized if it is lazily initialized
#Autowired
private ApplicationContext applicationContext;
public MyBean getObject() {
MyBean myBean = new MyBean();
if (true /* some condition */) {
myBean.setDependency(applicationContext.getBean(DependencyX.class));
} else {
myBean.setDependency(applicationContext.getBean(DependencyY.class));
}
return myBean;
}
// Implementation of isSingleton => false and getObjectType
}
Maybe a bit better approach is if you use factory bean to create the dependency bean in case you want to have only one such bean in your application context:
public MyDependencyFactoryBean implements FactoryBean<MyDependency> {
public MyDependency getObject() {
if (true /* some condition */) {
return new MyDependencyX();
} else {
return new MyDependencyY();
}
}
// Implementation of isSingleton => false and getObjectType
}
SpEL
With SpEL there are many possibilities. Most common are system property based conditions:
<bean class="com.example.MyBean">
<property name="dependency" value="#{systemProperties['foo'] == 'bar' ? dependencyX : dependencyY}" />
</bean>
Property placeholder
You can have property placeholder resolve your bean reference. The dependency name can be part of the application configuration.
<bean class="com.example.MyBean">
<property name="dependency" ref="${dependencyName}" />
</bean>
Spring profiles
Usually the condition you want to evaluate means that a whole set of beans should or should not be registered. Spring profiles can be used for this:
<!-- Default dependency which is referred by myBean -->
<bean id="dependency" class="com.example.DependencyX" />
<beans profile="myProfile">
<!-- Override `dependency` definition if myProfile is active -->
<bean id="dependency" class="com.example.DependencyY" />
</beans>
Other methods can mark the bean definition as lazy-init="true", but the definition will be still registered inside application context (and making your life harder when using unqualified autowiring). You can also use profiles with #Component based beans via #Profile annotation.
Check ApplicationContextInitialier (or this example) to see how you can activate profiles programatically (i.e. based on your condition).
Java config
This is why Java based config is being so popular as you can do:
#Bean
public MyBean myBean() {
MyBean myBean = new MyBean();
if (true /* some condition */) {
myBean.setDependency(dependencyX());
} else {
myBean.setDependency(dependencyY());
}
return myBean;
}
Of course you can use more or less all configuration methods in the java based config as well (via #Profile, #Value or #Qualifier + #Autowired).
Post processor
Spring offers numerous hook points and SPIs, where you can participate in the application context life-cycle. This section requires a bit more knowledge of Spring's inner workings.
BeanFactoryPostProcessors can read and alter bean definitions (e.g. property placeholder ${} resolution is implemented this way).
BeanPostProcessors can process bean instances. It is possible to check freshly created bean and play with it (e.g. #Scheduled annotation processing is implemented this way).
MergedBeanDefinitionPostProcessor is extension of bean post processor and can alter the bean definition just before it is being instantiated (#Autowired annotation processing is implemented this way).
UPDATE Oct 2015
Spring 4 has added a new method how to do conditional bean registration via #Conditional annotation. That is worth checking as well.
Of course there are numerous other ways with Spring Boot alone via its #ConditionalOn*.
Also note that both #Import and #ComponentScan (and their XML counterparts) undergo property resolution (i.e. you can use ${}).
I had a case where I needed to inject different beans depending on property: "my.property". In my case this solution was successful:
<property name="name" ref="#{ ${my.property:false}==true ? 'bean1' : 'bean2' }"/>
I needed to add the apostrophes around bean names in order to make it work.
In your #Configuration class declare a bean to be conditionally created:
#Bean
#Conditional(CustomFeatureCondition.class)
public Stuff stuff() {
return new Stuff ();
}
In the place of using just #Autowire it with required = false option:
#Component
#Setter(onMethod_ = #Autowired(required = false))
public class AnotherStuff {
private Stuff stuff;
// do stuff here
}
This way you'll get Stuff bean if it exists in the context and stuff = null if it doesn't.
I suppose the simpest way:
#Autowired #Lazy
protected A a;
#Autowired #Lazy
protected B b;
void do(){
if(...) { // any condition
// use a
} else {
// use b
}
}
In case you do not declare nessassary bean, Spring throws at runtime NoSuchBeanDefinitionException