I have 2 beans of the same type:
#Component("beanA")
public class BeanA implements BaseBean {}
#Component("beanB")
public class BeanB implements BaseBean {}
This type is used in my service:
#Service
public class MyService {
#Autowired
private BaseBean baseBean;
}
Now I want to use both possible MyService beans in another service
#Service
public class AnotherService {
#Autowired
private MyService myServiceWithBeanA;
#Autowired
private MyService myServiceWithBeanB;
}
How can I achieve that? Maybe I should take another approach?
I know how to do it in XML-based beans configuration. How can I do it using annotations?
<bean id="AnotherService" class="AnotherService">
<property name="myServiceWithBeanA" ref="myServiceWithBeanA" />
<property name="myServiceWithBeanB" ref="myServiceWithBeanB" />
</bean>
<bean id="myServiceWithBeanA" class="MyService">
<property name="baseBean" ref="beanA" />
</bean>
<bean id="myServiceWithBeanB" class="MyService">
<property name="baseBean" ref="beanB" />
</bean>
<bean id="beanA" class="BeanA" />
<bean id="beanB" class="BeanB" />
The problem is that MyService is annotated with #Service. This means that it is a singleton - only one instance will be created.
In order to create multiple instances, you need to expose two #Beans via configuration.
#Configuration
public class MyServiceConfig {
#Bean
public MyService serviceA(#Qualifier("beanA") beanA) {
return new MyService(beanA);
}
#Bean
public MyService serviceB(#Qualifier("beanB") beanB) {
return new MyService(beanB);
}
}
MyService would become
public class MyService {
private BaseBean baseBean;
public MyService(BaseBean baseBean) {
this.baseBean = baseBean;
}
}
You can then pass all of these to the other service with qualifiers
#Service
public class AnotherService {
#Autowired
#Qualifier("serviceA")
private MyService myServiceWithBeanA;
#Autowired
#Qualifier("serviceB")
private MyService myServiceWithBeanB;
}
Using the #Qualifier annotation you can specify which bean you want to autowire.
#Service
public class AnotherService {
#Autowired
#Qualifier("beanA")
private MyService myServiceWithBeanA;
#Autowired
#Qualifier("beanB")
private MyService myServiceWithBeanB;
}
Hope this helps.
In your xml bean definition add the these tags <qualifier value="name"/>.
<bean id="myServiceWithBeanA" class="MyService">
<qualifier value="A"/>
<property name="baseBean" ref="beanA" />
</bean>
<bean id="myServiceWithBeanB" class="MyService">
<qualifier value="B"/>
<property name="baseBean" ref="beanB" />
</bean>
And then you can get them by using Qualifier annotation like this:
#Autowired
#Qualifier("A")
private MyService myServiceWithBeanA;
#Autowired
#Qualifier("B")
private MyService myServiceWithBeanB;
You also can do more stuff than that.
Have a look at the documentation on the following link:
Qualifier documentaiton
Not everything can be done with annotations. In cases where you need to create multiple instance of a bean but with different arguments you have to fallback to #Configuration and define those beans as you would in the XML config.
#Configuration
public class AppConfig {
#Bean
public MyService myServiceWithBeanA(BeanA beanA) {
return new MyService(beanA);
}
#Bean
public MyService myserviceWithBeanB(BeanB beanB) {
return new MyService(beanB);
}
}
Now just tell AnotherService to expect MyService twice with matching bean names as name of the bean defining method is its default qualifier.
#Service
public class AnotherService {
#Autowired
private MyService myServiceWithBeanA;
#Autowired
private MyService myServiceWithBeanB;
}
Related
I have a bit of code that is working :
#Component
public class MessageUtil {
#Autowired
#Qualifier("processMessages")
private ReloadableConfig config;
public String createMessage() {
return config.getPropertyStr("message.simple.signature");
}
}
The bean processMessages is defined here :
<bean id="processMessages" class="com.company.framework.resources.ReloadableConfig">
<property name="basename" value="classpath:com/company/aaa/bbb/domain/service/processMessages"/>
<property name="defaultEncoding" value="UTF-8"/>
<property name="cacheSeconds" value="60"/>
</bean>
Then I created some new classes :
public abstract class MessageBuilder {
#Autowired
#Qualifier("processMessages")
protected ReloadableConfig config;
public abstract String createMessage();
}
#Component
public class SimpleMessageBuilder extends MessageBuilder {
private String template;
private void setTemplate() {
template = config.getPropertyStr("message.simple.signature");
}
#Override
public String createMessage() {
setTemplate();
return template;
}
}
I now have a NullPointerException because in setTemplate(), config is null.
What's the problem in the second code ?
#Autowired doesn't work neither on field neither on constructors of abstract classes. It works on setter of abstract class but be sure to make it final because if overwritten by concrete class behavior is unstable. An abstract class isn't component-scanned since it can't be instantiated without a concrete subclass.
This may looks like a duplicate of this question. But this is different.
I was trying to refactor my legacy code by using method injection in spring.
I have a bean class which contains many static helper methods. My targeted method as follows:
Context.java
private static MessageSender messageSender;
//...
public static MessageSender getMessageSender(){
return messageSender;
}
Context bean
<bean id="context" class="org.abc.Context">
<property name="messageSender"><ref bean="mailMessageSender"/></property>
</bean>
MailMessageSender.java
public abstract class MailMessageSender{
protected abstract Session createSession();
//using createSession() somewhere in this class
}
MailMessageSender bean
<bean id="session" class="javax.mail.Session" scope="prototype" />
<bean id="mailMessageSender" class="org.abc.MailMessageSender">
<lookup-method name="createSession" bean="session"/>
</bean>
I'm getting invalid property error when I'm installing the project.
You can't inject static field, change your variable in Context.java become like this:
private MessageSender messageSender;
//...
public MessageSender getMessageSender(){
return messageSender;
}
Hopefully someone might have the answer for this problem.
I have a autowire problem occuring when I run my tests but not else.
The exception is pretty clear as it says 'myField' is missing. The field is however inherited from a base class. I cannot find the answer to my problem anywhere so I will try my luck here.
The exception
org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'myService': Injection of autowired dependencies failed;
nested exception is java.lang.NoSuchFieldError: myField
My base class
public abstract class CommonStuff {
protected String myField; // the one thats gone missing
public abstract void setMyField(String myField);
}
My service
#Service("myService")
public class MyService extends CommonStuff {
#Value("${myProperty.myField}")
public void setMyField(String myField) {
this.myField = myField;
}
...
}
My controller
#Controller
public class MyController {
#Autowired
private MyService myService;
public void setMyService(MyService myService) {
this.myService = myService;
}
...
}
My application-context.xml
Nothing seems to be left out.
<context:component-scan base-package="com.myapp" />
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="ignoreResourceNotFound" value="true" />
<property name="locations">
<list>
<value>classpath:my.properties</value>
<value>classpath:myother.properties</value>
</list>
</property>
</bean>
my.properties
myProperty.myField=some value
My test
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:application-context.xml" })
public class MyControllerTest {
#Autowired
MyController myController;
#Mock
MyService myService;
#Test
public void myServiceTest() {
// do stuff with myService
}
...
}
The problem
As I mentioned the code runs fine but a spring has trouble autowiring whenever I try to run the test.
The problem is not the test but the wiring when starting the test. Somehow spring cannot find myField from the abstract CommonStuff class so it says that MyService do not have the field.
If needed I can post the full stacktrace but I think the essense of the exception is already here.
I gave up and moved the myField back into the service and skipped the setMyField.
This is how it is now.
Base class
public abstract class CommonStuff {
public abstract String getMyField();
public void doCommonStuff() {
// use getMyField()
}
}
My service
#Service("myService")
public class MyService extends CommonStuff {
#Value("${myProperty.myField}")
private String myField;
#Override
public String getMyField() {
return myField;
}
...
}
This solves what I wanted since I now have access to myField from CommonStuff.
Everything works fine when I try to get messages in a #Controller class, but when I try
to achieve the same in a #Service or #Component class I receive the following error:
org.springframework.context.NoSuchMessageException:
No message found under code 'email.ativacao.title' for locale 'pt_BR'.
My Controller:
#Controller
public class TestController {
#Autowired
TestService service;
#Autowired
TestComponent component;
#Autowired
private MessageSource message;
#RequestMapping(value = "/send", method = RequestMethod.GET)
public String go() {
String message = message.getMessage
("email.ativacao.title", null, new Locale("pt", "BR"));
service.getMessage();
component.getMessage();
return "signsucess";
}
}
My Service:
#Service
public class TestService {
#Autowired
private MessageSource message;
public void getMessage() {
//Error
String message = message.
getMessage("email.ativacao.title", null, new Locale("pt", "BR"));
}
}
My Component:
#Component
public class TestComponent {
#Autowired
private MessageSource message;
public void getMessage() {
//Error
String message = message.
getMessage("email.ativacao.title", null, new Locale("pt", "BR"));
}
}
My config:
<!-- i18n -->
<mvc:interceptors>
<bean class="org.springframework.web.servlet.i18n.
LocaleChangeInterceptor" p:paramName="lang"/>
</mvc:interceptors>
<!-- Mesage Source Config -->
<bean id="messageSource"
class="org.springframework.context.support.
ReloadableResourceBundleMessageSource" p:fallbackToSystemLocale="true" >
<property name="basename" value="WEB-INF/i18n/messages" />
</bean>
<!-- Mapeia o cookie que irá salvar as opções de idioma -->
<bean class="org.springframework.web.servlet.i18n.CookieLocaleResolver"
id="localeResolver" p:cookieName="locale"/>
MessageSource is not null on both #Service and #Component, but they're not able to
get the message (Exception above). My properties:
WebContent/WEB-INF/i18n
messages_pt_BR
messages_en_US
I really can't find the problem. Any suggestion to solve this? Thanks.
From what you were describing, I guess controller bean and messageSource were declared in same context. so then can find each other.
if your service bean and controller bean are not declared in same context, your service cannot find the messageSource.
same context doesn't mean same file. your one.xml could include two.xml.
anyway, if it worked for you, it's good.
I have:
#Component
class MyDecorator{
private Cache cache;
/*
some wrapped methods like get put remove
*/
}
Is it possible to autowire MyDecorator in different places with different cache?
I can configure XML like this:
<bean id="id1" class="MyDecorator ">
<property name="cache" value="Cache1" />
</bean>
<bean id="id2" class="MyDecorator ">
<property name="cache" value="Cache2" />
</bean>
But is there more elegance way without addition of xml configs, only with annotation?
Correct code should be
#Configuration
public class AppConfig {
#Bean
public MyAdapter adaptedCache2() {
return new MyAdapter (cache1);
}
#Bean
public MyAdapter adaptedCache2() {
return new MyAdapter (cache2);
}}
according to specs will be generated two beans adaptedCache1 and adaptedCache2
and now i can
autowire those beans with qualifiers adaptedCache1 and adaptedCache2
With Java configuration (Spring 3.1) you can write:
#Bean
public MyDecorator decoratedCache1() {
return new MyDecorator(cache1);
}
#Bean
public MyDecorator decoratedCache2() {
return new MyDecorator(cache2);
}
Of course in this case MyDecorator does not need #Component:
#Component
class MyDecorator{
private final Cache cache;
public MyDecorator(Cache cache) {
this.cache = cache;
}
}