How to specify sub-dependency for AutoWired Beans - java

I have a Spring component defined like this:
#Component
public class SearchIndexImpl implements SearchIndex {
IndexUpdater indexUpdater;
#Autowired
public SearchIndexImpl(final IndexUpdater indexUpdater) {
Preconditions.checkNotNull(indexUpdater);
this.indexUpdater = indexUpdater;
}
}
along with two implementations of the IndexUpdater interface, like:
#Component
public class IndexDirectUpdater implements IndexUpdater, DisposableBean, InitializingBean {
}
#Component
public class IndexQueueUpdater implements IndexUpdater, DisposableBean, InitializingBean {
}
If I try to auto-wire SearchIndexImpl like this:
#Autowired
private SearchIndex searchIndex;
I get the following exception:
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'IndexUpdater' available: expected single matching bean but found 2: indexDirectUpdater,indexQueueUpdater
which is expected since Spring cannot tell which IndexUpdater implementation to auto-wire for the indexUpdater parameter in the constructor of SearchIndexImpl. How do I guide Spring to the bean that it should use? I understand I can use the #Qualifier annotation, but that will hard-code the index updater to one of the implementation, while I want the user to be able to specify what index updater to use. In XML, I can do something like:
<bean id="searchIndexWithDirectUpdater" class="SearchIndexImpl">
<constructor-arg index="0" ref="indexDirectUpdater"/>
</bean>
How do I do the same using Spring's Java annotations?

Use the #Qualifier annotation to specify the dependency to use :
public SearchIndexImpl(#Qualifier("indexDirectUpdater") IndexUpdater indexUpdater) {
Preconditions.checkNotNull(indexUpdater);
this.indexUpdater = indexUpdater;
}
Note that #Autowired is not needed to autowire the arg constructor of a bean since Spring 4.
To answer to your comment.
To let the class that will use the bean to define the dependency to use you could allow it to define the IndexUpdater instance to inject in the container such as :
// #Component not required any longer
public class IndexDirectUpdater implements IndexUpdater, DisposableBean, InitializingBean {
}
// #Component not required any longer
public class IndexQueueUpdater implements IndexUpdater, DisposableBean, InitializingBean {
}
Declare the bean in a #Configuration class :
#Configuration
public class MyConfiguration{
#Bean
public IndexUpdater getIndexUpdater(){
return new IndexDirectUpdater();
}
The SearchIndexImpl bean will now resolve the dependency thanks to IndexUpdater getIndexUpdater().
Here we use #Component for one bean and #Bean for its dependency.
But we could also allow a full control on the beans to instantiate by using only #Bean and by removing #Component on the 3 classes :
#Configuration
public class MyConfiguration{
#Bean
public IndexUpdater getIndexUpdater(){
return new IndexDirectUpdater();
}
#Bean
public SearchIndexImpl getSearchIndexFoo(){
return new SearchIndexImpl(getIndexUpdater());
}

Related

Why do we use a qualifier when we can have a name for the bean?

Why do we use qualifiers with #Bean when we can have different names for different beans of the same type (class)?
#Bean
#Qualifier("fooConfig")
public Baz method1() {
}
Isn't the following code more clean?
#Bean("fooConfig")
public Baz method1() {
}
If I create two beans of the same type with different names (using #Bean annotation), then can we inject them specifically using the #Qualifier annotation(can be added on field/constructor parameter/setter) in another bean?
#Bean("fooConfig")
public Baz method1(){
}
#Bean("barConfig")
public Baz method2(){
}
// constructor parameter of a different bean
final #Qualifier("fooConfig") Baz myConfig
If the above is true, then where do we use #Qualifier (with #Bean or #Component) instead of giving the bean a name as shown below?
#Bean
#Qualifier("fooConfig")
public Baz method1(){
}
#Bean
#Qualifier("barConfig")
public Baz method2(){
}
// constructor parameter of a different bean
final #Qualifier("fooConfig") Baz myConfig
Beans have names. They don't have qualifiers. #Qualifier is annotation, with which you tell Spring the name of Bean to be injected.
No.
Default Qualifier is the only implementation of the interface(example is below, 4th question) or the only method with a particular return type. You don't need to specify the #Qualifier in that case. Spring is smart enough to find itself.
For example:
#Configuration
public class MyConfiguration {
#Bean
public MyCustomComponent myComponent() {
return new MyCustomComponent();
}
}
If you will try to inject myComponent somewhere, Spring is smart enough to find the bean above. Becaude there is only one Bean with return type MyCustomComponent. But if there was a couple of methods, that would return MyCustomComponent, then you would have to tell Spring which one to inject with #Qualifier annotation.
SIDENOTE: #Bean annotation by default Spring uses the method name as a bean name. You can also assign other name like #Bean("otherComponent").
You have one Interface, and a couple of Classes implementing it. You inject bean of your interface. How can Spring know which Class should be used?
This is you interface:
public interface TestRepository{}
This is your implementation 1:
#Repository
public class Test1Repository implements TestRepository{}
Your implementation 2:
#Repository
public class Test2Repository implements TestRepository{}
Now you are injecting it like:
private final TestRepository testRepository;
public TestServiceImpl(TestRepository testRepository) {
this.testRepository= testRepository;
}
QUESTION! How is Spring supposed to know which class to inject? Test1 or Test2? That's why you tell it with #Qualifier which class.
private final TestRepository testRepository;
public TestServiceImpl(#Qualifier("test1Repository") TestRepository testRepository) {
this.testRepository= testRepository;
}
I Prefer different method to not using #Qualifier
Create common Interface
public interface CommonFooBar{
public String commonFoo();
public String commonBar();
}
Extends to each service
public interface FooService extends CommonFooBar {
}
public interface BarService extends CommonFooBar {
}
Then using it to your class
#Autowired
FooService fooService;
or
#Autowired
BarService barService;
so, we can defined the single responsibility to each interface and This kind of segregation is more readable to every junior.
I quite like a different way of working. Surely if you provide a unique name for your bean, then that is all you need?
Given the example below, its easy to see that Spring will name the beans based on the method name used to create the beans. In other words, if you give your beans sensible names, then the code should become self-explanatory. This also works when injecting beans into other classes.
The end result of this is:
Spring will name your beans based on the method used to create them.
If you import a bean, Spring will try to match on the bean name.
If you try to import a bean that does not match the name, Spring will attempt to match the class.
If your injected field name does not match the bean name and there are more than one instance of your bean, Spring will throw an exception on startup as it won't know which one to inject.
Lets not over-complicate Spring.
#Bean
mqConnectionFactory() {
ConnectionFactory connectionFactory = new MQXAConnectionFactory();
return connectionFactory;
}
#Bean
public ConnectionFactory pooledConnectionFactory(ConnectionFactory mqconnectionFactory) {
JmsPoolConnectionFactory connectionFactory = new JmsPoolConnectionFactory();
connectionFactory.setConnectionFactory(mqConnectionFactory);
return connectionFactory;
}
#Bean
public ConnectionFactory cachingConnectionFactory(ConnectionFactory mqConnectionFactory) {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setTargetConnectionFactory(mqConnectionFactory);
return connectionFactory;
}
#Bean
public JmsTemplate jmsTemplate(ConnectionFactory cachingConnectionFactory) {
JmsTemplate jmsTemplate = new JmsTemplate();
jmsTemplate.setConnectionFactory(cachingConnectionFactory);
return jmsTemplate;
}
#Bean
public DefaultMessageListenerContainer messageListenerContainer(ConnectionFactory pooledConnectionFactory) {
DefaultMessageListenerContainer container = new DefaultMessageListenerContainer();
container.setConnectionFactory(pooledConnectionFactory);
...
return container;
}

How do I extend a named Spring bean when using a #Qualifier specified bean injection point?

How do I extend a named bean when using a #Qualifier specified bean injection point?
I have project 1 consisting of 3 spring beans:
#Component("bean1")
public class Bean1 implements Bean {
}
#Component("bean2")
public class Bean2 implements Bean {
}
#Component("bean3")
public class Bean3 {
private Bean bean;
public void setBean(#Qualifier("bean1") final Bean bean) {
this.bean = bean;
}
}
This project is bundled into a jar and included as a dependency on my 2nd project:
#Component("bean1")
#Primary
public class Bean1Child extends Bean1 {
}
What I want to happen is for the bean, Bean1Child, to be injected into Bean3's setter method. Unfortunatly I am receiving an error.
org.springframework.context.annotation.ConflictingBeanDefinitionException:
Annotation-specified bean name 'bean1' for bean class [Bean1Child]
conflicts with existing, non-compatible bean definition of same name
and class [Bean1]
I needed to use #Qualifier so that Bean2 is not injected into Bean3 Using the #Primary annotation did not help. How can I have Bean1Child injected into Bean3 when running from my 2nd project?
If this is possible, you can change the way the beans are created by removing the #Component annotations:
In the first project, the BeanChild3 would be refactored to get the bean in the constructor
public class Bean3 {
private final Bean bean;
public Bean3(final Bean bean) {
this.bean = bean;
}
}
Then we can create the beans in a BeansConfig class
#Configuration
class BeansConfig {
#ConditionalOnMissingBean
#Bean("bean1")
Bean bean1(){
return new Bean1();
}
#Bean("bean2")
Bean bean2(){
return new Bean2();
}
#Bean("bean3")
Bean bean3(#Autowired #Qualifier("bean1") Bean){
return new Bean3(bean);
}
}
The #ConditionalOnMissingBean allows us to provide another bean with the same name to be used instead. If no such bean exists, then the default one would be used.
You will then need to create a beanChild1 bean in your second project and it should be picked up.
#Bean("bean1")
Bean bean1(){
return new Bean1Child();
}
You can easily achieve this using #ConditionalOnMissingBean feature.
Modify your Bean1 class as below
#Component("bean1")
#ConditionalOnMissingBean(type = "bean1")
public class Bean1 implements Bean {
}
modify Bean1Child class as below
#Component
#Qualifier("bean1")
#Primary
public class Bean1Child extends Bean1 {
}
How it works?
Spring will try to load a bean named "bean1". If it doesn't find any bean by the same name that is marked as #Primary it will fall back to Bean1 class and load it as "bean1".
Bean1Child has to be marked as primary because spring is going to find 2 beans by the same name. We need to tell which to load.
You have multiple beans of the same type and want to prevent Bean2 from injecting. In some project inject Bean1 and in others Bean1Child.
There are multiple options.
Override bean definition with #Bean
Make Bean1Child bean definition the same as Bean1 has using #Bean
#Configuration
public class Config {
#Primary
#Bean
public Bean1 bean1() { //return type must be Bean1
return new Bean1Child();
}
}
and set the property spring.main.allow-bean-definition-overriding=true
Create custom #Qualifier annotation
#Qualifier
#Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
#Retention(RetentionPolicy.RUNTIME)
#interface BeanType {
}
#BeanType
public #interface Bean1Type {
}
#Bean1Type
#Component("bean1")
public class Bean1 implements Bean {
}
#Component("bean2")
public class Bean2 implements Bean {
}
#Component("bean3")
public class Bean3 {
private final Bean bean;
public Bean3(#Bean1Type Bean bean) {
this.bean = bean;
}
}
#Bean1Type
#Primary
#Component("bean1Child")
public class Bean1Child extends Bean1 {
}
You cannot have two beans specified with the same qualified name, as the error indicates:
Annotation-specified bean name 'bean1' for bean class [Bean1Child]
conflicts with existing, non-compatible bean definition of same name and class [Bean1]
Giving a different qualifier name to Bean1Child should work.
#Component("bean1child")
#Primary
public class Bean1Child extends Bean1 {
}
and in Bean3 , public void setBean(#Qualifier("bean1child") final Bean bean) {

Eliminating Spring.xml from Spring Framework

In Spring Framework is it possible to eliminate the entire Spring.xml and use a configuration class with #Configuration and #Bean annotation for creating bean, and for all other purpose use a spring.xml?
Yes, you can have pure java configuration in Spring. You have to create a class and annotate it with #Configuration. We annotate methods with #Bean and instantiate the Spring bean and return it from that method.
#Configuration
public class SomeClass {
#Bean
public SomeBean someBean() {
return new SomeBean();
}
}
If you want to enable component scanning, then you can give #ComponentScan(basePackages="specify_your_package") under the #Configuration. Also the method name as someBean serves as bean id. Also if you have to inject a dependency, you can use constructor injection and do as following:
#Configuration
public class SomeClass {
#Bean
public SomeDependency someDependency() {
return new SomeDependency();
}
#Bean
public SomeBean someBean() {
return new SomeBean(someDependency());
}
}
Yes,most of (maybe all of)official guides uses absolutely no xml configuration file,just annotations.

Spring Bean Alias in JavaConfig

I have a #Service annotated class which provides core functionality which I can use in all my projects:
#Service
public class MyService {}
and another one which extends it to implement project specific stuff:
#Service
public class ExtendedMyService extends MyService {}
Now I would like to configure a bean alias to be able to use #Qualifier("MyServiceAlias") when autowiring it using a property:
# MyService qualifier (default: myService)
myService.qualifier=extendedMyService
In XML it would look like:
<alias name="${myService.qualifier}" alias="MyServiceAlias" />
It is also discussed here, but I need to do it w/o XML, JavaConfig only.
Is it possible and how to realize?
There is an open Jira for this: https://jira.spring.io/browse/SPR-6736
The workaround is to use #Bean in #Configuration class:
#Configuration
public class AppConfig {
#Bean(name = { "dataSource", "subsystemA-dataSource", "subsystemB-dataSource" })
public MyService myService() {}
}
If you want to use the placeholder, another workaround is to use #Bean in a #Configuration class using #Value and the Spring applicationContext.
#Configuration
public class AppConfig {
#Autowired
private ApplicationContext context;
#Bean
public MyService myService(#Value("${myService.qualifier}") String qualifier) {
return (MyService) context.getBean(qualifier);
}
}
NB : special consideration must be taken for the placeholder bean which must be loaded at the beginning (cf javadoc)
With small amount of configuration and one ImportBeanDefinitionRegistrar you can configure bean aliases via Java configuration. You can check bean-alias library project for reference - developed for the needs of my projects. Feel free to modify and/or copy the source into your own project in case the spring version used in it does not work with your setup.
Once you have the library on your path, you declare an alias through the annotation:
#Configuration
#BeanAlias(name = "fromName", alias = "toName")
public class ExampleConfiguration {
}
That's it.
How it works is that with the annotation we import a ImportBeanDefinitionRegistrar implementation
#Import(BeanAliasBeanRegistrar.class)
public #interface BeanAlias {
}
which registers the alias in the BeanDefinitionRegistry
class BeanAliasBeanRegistrar implements ImportBeanDefinitionRegistrar, PriorityOrdered {
#Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
...
registerAlias(registry, metadata.getAnnotationAttributes(BeanAlias.class.getName()));
}
private void registerAlias(BeanDefinitionRegistry registry, Map<String, Object> attributes) {
...
registry.registerAlias(name, alias);
}
}

SpringBeanAutowiringInterceptor - Configure to autowire by name instead of by type?

I'm using a SpringBeanAutowiringInterceptor in an EJB3 stateless session bean, as described in the Spring documentation.
#Stateless
#Interceptors(SpringBeanAutowiringInterceptor.class) // Allows spring injection for its setter methods
public class MyClassImpl extends MyAbstractClass implements MyClass
{
....
#Autowired
public void setMyCustomService2(MyService svc) {
this.service = svc;
}
And in SpringConfig.xml:
<bean id="myCustomService1" class="...MyService"/>
<bean id="myCustomService2" class="...MyService"/>
When Spring tries to autowire this I get
No unique bean of type [...MyService ] is defined:
expected single matching bean but found 2: [myCustomService1 , myCustomService2]
Unfortunately, it seems EJB autowiring defaults to byType mode, and I can't find a way to change it to byName mode.
Is this possible, and if so, how?
Have you tried with a Qualifier?
#Autowired
#Qualifier("myCustomService1")
public void setMyCustomService2(MyService svc) {
this.service = svc;
}

Categories

Resources