I am trying to do conditional auto-wiring in Spring using annotation-based configuration. I have 2 different beans both implementing the same interface. I would like to do something like
if(some condition)
choose bean 1 to autowire
else
choose bean 2 to autowire
Is there a way to do this? I noticed the #Primary and #Qualifier annotations, but they will only choose one bean or the other to autowire and not based on some condition. Thanks.
Autowiring injects a bean into the target bean only once, when the target object is being initialized. Afterwards it remain unchanged.
Consider other approaches. For instance, inject both beans and implement a method that selects one of these beans depending on your conditions.
Related
I need to register a series of BeanDefinition(s) before every other Bean gets created. That's because those registered Bean(s) are needed for autowiring and ApplicationContext#getBean calls.
I cannot use #DependsOn, obviously.
Example:
final var beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(...);
beanDefinition.setLazyInit(true);
beanDefinition.setAbstract(false);
beanDefinition.setAutowireCandidate(true);
beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
registry.registerBeanDefinition("...", beanDefinition);
Which point/interface/lister can I use to obtain this? Keep in mind I need an instance of BeanDefinitionRegistry.
Adding explanation as required.
Those definitions are created from a list of Classes gathered by scanning the classpath. Those classes are not Spring Bean(s) natively, so I need to integrate them into my ApplicationContext. Those classes, however, accepts constructor arguments which are Spring Beans.
That's why I'm setting
beanDefinition.setAutowireCandidate(true);
beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
Those new registered Beans are there used by other Bean(s) (native Beans).
You are trying to make the solution too complex. If your only goal is to have non #Component annotated classes be detected by component scanning and have them used as Spring Beans simply define a custom includeFilter for the #COmponentScan.
You can use a filter of type ASPECTJ or REGEX to match a package or type.
#ComponentScan(includeFilter = #Filter(type=REGEX, expression="com.foo.bar.*))
Something like that will automatically detect your beans (assuming they are in a packaged being scanned) and create spring beans out of them. If they have a single constructor that will automatically be used to create an instance.
Register a new BeanFactoryPostProcessor or BeanDefinitionRegistryPostProcessor bean in your context. This bean will get invoked after bean definitions are scanned but before actual beans are constructed:
Extension to the standard BeanFactoryPostProcessor SPI, allowing for the registration of further bean definitions before regular BeanFactoryPostProcessor detection kicks in. In particular, BeanDefinitionRegistryPostProcessor may register further bean definitions which in turn define BeanFactoryPostProcessor instances.
I am trying to understand the various Spring bean configurations, and at the outset, trying the traditional XML way.
Now I came to know for dependency injection,the "basic" methods are setter injection and constructor injection.
Thus far, good.
I also came to know that one more method which Spring provided is by Autowire. In Autowire we have many options, like: byType, byName, constructor.
Now when I dig more about Autowire option , it seems that for byType and byName to work there needs to have setter method to be present while for Autowire by constructor there should be matching constructor.
Is this correct?
Now, if Autowire internally uses constructor or setter, then why do we have this option present at all?
Can anyone help me understand this?
I'm totally new to CDI.
I'm used at configuring beans in XML (Spring). In CDI, should I configure them in classes directly? I have tens of beans with the same implementation but different configuration.
CDI uses a mixture of annotations and xml configuration to configure which beans are active in the deployment. It's a big topic but I'll attempt to summarise:
On your bean implementations you can use the following standard annotations:
#Default
#Alterative
#Vetoed
#Specializes
#Default is assumed unless no other annotations are present
#Alternative beans are not active unless specified so in your META-INF/beans.xml
#Vetoed beans are never considered active
#Specializes beans will always take precedence over their superclasses.
In addition to those you can create your own qualifiers to more accurately select which bean you want for what purpose.
You would create a qualifier as an annotation like this:
#Qualifier
#Retention(RUNTIME)
#Target({ TYPE, FIELD, PARAMETER, METHOD })
public #interface MyQualifier {
}
Note the #Qualifier and #Retention(RUNTIME) annotations.
You can also add parameters to your customer qualifier.
I would recommend giving the Weld documentation a read, it's comprehensive and quite well written:
Weld manual
Yes, configuration happens within the code. There have been several attempts at doing XML based configuration, look for Seam Config.
For your tens of beans, you would typically use producer methods to create the individual implementations with their own configuration. CDI uses qualifiers, rather than bean ids to identify beans.
I'm wondering if I add a #Value annotation on a property, the class who contains this property cannot be used by another one with a different value, Example :
MyClassUtil.java had
#Value("${some.value}")
private int _myProperty;
And of course there is one module.properties who contain :
some.value=10
Another class ClassA.java wants to use this class with value 10. Ok, no problem.
But another class ClassB.java wants to use this class but with another value : 20. I cannot do this if I'm not mistaken.
Because before #Value era, I could declare two beans in the moduleContext.xml without any problem.
So, is #Value pushes you to do some strong coupling ?
You are right that the annotation configuration can not be instance specific. It is important to understand the concept of bean definitions in bean factory.
Manual bean definition:
Single <bean> element in your XML config leads to a single bean definition. Multiple <bean> mean multiple definitions (regardless of a bean type).
Single #Bean method within #Configuration class leads to a single bean definition. Multiple #Bean methods mean multiple definitions (regardless of a bean type).
However when using component scan, classes annotated with #Component-like annotations are auto-registered as a single bean definition. There is no way you can register bean multiple times via component scan.
Similarly, annotation configurations (#Value, #Autowired, etc.) are type-wide. Your bean instances are always augmented and processed with the same effect (e.g. injecting the same value). There is no way you can alter annotation processing behaviour from instance to instance.
Is this tight coupling? It is not in its general understanding - bean factory (Spring) is still free to inject whatever it thinks is suitable. However it is more of a service lookup pattern. This simplifies your life when working with domain specific singletons. And most beans in an application context tend to be singletons, many of them domain specific (controllers, services, DAOs). Framework singletons (non-project specific reusable classes) should never use annotation based configuration - in this scope, it is an unwanted tight coupling.
If you need different bean instances, you should not use annotation configuration and define your beans manually.
Is there any way to inject bean dependencies make configurable except from factory pattern?
I have 3 class implement same interface and have 3 bean definition. I want to change that beans using in other class? For excample is it possile to read bean name form conf file and use it as varible?
Yes, you can go with #Qualifier annotation. As you have 3 classes which implement same interface, name those classes with different names and use #Qualifier annotation.
Spring documentation says : autowiring by type may lead to multiple candidates, it is often necessary to have more control over the selection process. One way to accomplish this is with Spring's #Qualifier annotation.