How to set MDB programmatic and dynamically - java

I am using Spring3.1 on standalone env.
I set topic with jms templates this way:
<bean id="mm1sessionsTopicSendingTemplate" class="org.springframework.jndi.JndiObjectFactoryBean"
depends-on="jmsServerManagerImpl">
<property name="jndiName">
<value>/topic/mm1sessionsTopic</value>
</property>
</bean>
For this topic I set MDB with DefaultMessageListenerContainer this way:
<bean id="mm1sessionDispatcherListener"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory" />
<property name="pubSubDomain" value="true" />
<property name="concurrentConsumers" value="1" />
<property name="destination" ref="mm1sessionsTopicSendingTemplate" />
<property name="messageListener" ref="mm1SessionMDB" />
<property name="sessionAcknowledgeModeName" value="AUTO_ACKNOWLEDGE" />
</bean>
In this way I must set mm1SessionMDB in advanced via xml:
<bean id="mm1SessionMDB" class="com.mdb.SessionMDB">
<property name="feedPropertiesDTO" ref="feedListenerMarketMaker1Properties" />
</bean>
But I need my application to create the MDB instances programmaticly.
I mean i want to create the mdb's via the code since each MDB will have different validation values for the messages that it will retrieve from the topic(via the feedPropertiesDTO)
basically I will have pool of MDB's with the same logic but each one will have different properties. the creation time of the MDB'S must be on runtime.
is that possible?
thanks,
ray.

I think you can use factory method for instantating your MDB bean and use method
Object getBean(String name, Object... args) throws BeansException;
of the ApplicationContext in your code to instantiate bean frogramatically.
As I know this method allows you to path argument to factory method.
Here what is said in java doc for this method:
Return an instance, which may be shared or independent, of the specified bean.
Allows for specifying explicit constructor arguments / factory method
arguments, overriding the specified default arguments (if any) in the
bean definition.
I have never used this approach but I think it colud work for your case.
EDIT.
Here is an example that demonstrates about what I'm talkin (It is very simple but I don't have enough time to write more complicated).
Suppose that have interface and two it's implementations:
public interface StringMakerInterface {
// Just return simple String depending on concrete implementation.
String returnDummyString();
}
public class StringMakerImpl1 implements StringMakerInterface {
public String returnDummyString() {
return "test bean impl 1";
}
}
public class StringMakerImpl2 implements StringMakerInterface{
public String returnDummyString() {
return "test bean impl 2";
}
}
And we have one class that uses concrete implementation of this interface and to which we should dinamically inject conctrete implementation:
public class StringPrinter {
private StringMakerInterface stringMaker;
public StringMakerInterface getStringMaker() {
return stringMaker;
}
public void setStringMaker(StringMakerInterface stringMaker) {
this.stringMaker = stringMaker;
}
public StringPrinter() {
}
// Just print dummy string, returned by implementation
public void printString() {
System.out.println(stringMaker.returnDummyString());
}
}
Here is configuration class for my example:
#Configuration
public class TestFactoryMethodConfig {
#Bean(name = "stringMaker1")
public StringMakerImpl1 stringMaker1() {
return new StringMakerImpl1();
}
#Bean(name = "stringMaker2")
public StringMakerImpl2 stringMaker2() {
return new StringMakerImpl2();
}
#Bean(name = "stringPrinter")
#Scope(value = "prototype")
public StringPrinter stringPrinter(#Qualifier("stringMaker1") StringMakerInterface stringMaker) {
StringPrinter instance = new StringPrinter();
instance.setStringMaker(stringMaker);
return instance;
}
}
And here is a test case that demonstrates dinamically injection at the runtime:
#RunWith(value = SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes={TestFactoryMethodConfig.class})
public class TestFactoryMethod {
#Autowired
private ApplicationContext applicationContext;
#Resource(name = "stringMaker1")
private StringMakerInterface stringMaker1;
#Resource(name = "stringMaker2")
private StringMakerInterface stringMaker2;
#Test
public void testFactoryMethodUsage() {
StringPrinter stringPrinter1 = (StringPrinter) applicationContext.getBean("stringPrinter", stringMaker1);
StringPrinter stringPrinter2 = (StringPrinter) applicationContext.getBean("stringPrinter", stringMaker2);
stringPrinter1.printString();
stringPrinter2.printString();
}
}

Related

Spring wire inherited property [duplicate]

Use annotation in dao
#Repository("testDao")
public class TestDaoImpl extends JdbcDaoSupport implements BaseDao{
#Override
public Object addObject(String sqlid, Object obj) {
// TODO Auto-generated method stub
return null;
}
Caused by: java.lang.IllegalArgumentException: 'dataSource' or 'jdbcTemplate' is required
I do not want to use :
<bean id="termsDao" class="com.manage.base.dao.impl.TestDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
this code set in xml, and “jdbcTemplate” has been defined in other “spring-xml”。
How to solve this problem by an annotation :“'dataSource' or 'jdbcTemplate' is required”
You can use one of the below approaches. The first one - taking a dataSource is preferred / recommended as you don't expose a SpringFramework class in your public interface. Both will work.
#Repository("testDao")
public class TestDaoImpl extends JdbcDaoSupport implements BaseDao{
#Autowired
TestDaoImpl(DataSource dataSource) {
setDataSource(dataSource);
}
}
Or
#Repository("testDao")
public class TestDaoImpl extends JdbcDaoSupport implements BaseDao{
#Autowired
TestDaoImpl(JDBCTemplate template) {
setJdbcTemplate(template);
}
}
I even feel injecting datasource as a constructor to your DAO is a unnecessary coding step.
Why not inject datasource in Spring config XML into JDBC template and just get jdbctTemplate
object in every DAO.
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
and let your DAO extend JDBCSupport class.
public class PersonDao extends JdbcDaoSupport{
public List<Person> selectAll(){
String selectAllSql = "SELECT * FROM PERSON;";
return getJdbcTemplate().query(selectAllSql, new PersonRowMapper());
........
}
}
Full example :
http://www.studytrails.com/frameworks/spring/spring-jdbc-dao-support.jsp

Spring Annotation : Converting XML Configuration to Annotation

I am using xml configuration in my Spring Application. Now i would like to convert the existing classes to use annotation(like #service, #Repository etc) instead of xml configuration.
Business Logic(Irrelevant for this question, just for understanding):
Sevice connnects to Americas database and find the skus(products) and deactivates the skus.
Sevice connnects to EMEA database and find the skus(products) and deactivates the skus.
Here is the sample code.
/* Service code, which has 2 instances of SkuDAO, one connecting to US database and one connecting to EMEA database */
public class DeactivationService {
private static final Logger LOG = Logger.getLogger(DeactivationService.class);
private SkuDAO amerdao; //Dependency Injection Amer
private SkuDAO emeadao; //Dependency Injection EMEA
public DeactivationService(SkuDAO amerdao,SkuDAO emeadao) {
this.amerdao=amerdao;
this.emeadao=emeadao;
}
/*
* Step 1: find inactive sku in americas skudao1.find()
* Step 2: find inactive sku in emea skudao2.find()
* Step 3: deactivate sku in americas
* Step 4: deactivate sku in emea
*/
public void deactivateSku() {
List<Sku> totalList = new ArrayList<Sku>();
List<Sku> amerList = amerdao.find();
List<Sku> emeaList = emeadao.find();
amerdao.deactivate(amerList);
emeaList.deactivate(emeaList);
}
}
/* DAO interface */
public interface SkuDAO {
public List<Sku> find();
public void deactivate(List<Sku>);
}
/* DAO Implementation
Here one constructor in which DataSource is injected
*/
public class SkuDAOImpl implements SkuDAO {
private DataSource datasource; //Dependency injection
private JdbcTemplate jdbcTemplate;
public SkuDAOImpl(DataSource datasource) {
this.datasource=datasource;
}
public List<Sku> find() {
//some processing to find the sku, purposely left empty as it is a sample code
}
public void deactivate(List<Sku>) {
//some processing to deactivate the sku, purposely left empty as it is a sample code
}
}
Spring Configuration:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:property-placeholder location="file:${dbconfiguration}"/>
<bean id="AmericasDataSource" class="dell.harmony.data.HarmonyBasicDataSource" destroy-method="close" >
<property name="url"><value>${HarmonyAmericasDb.url}</value></property>
<property name="driverClassName"><value>${HarmonyAmericasDb.driverClassName}</value></property>
<property name="username"><value>${HarmonyAmericasDb.username}</value></property>
<property name="password"><value>${HarmonyAmericasDb.password}</value></property>
<property name="initialSize"><value>${HarmonyAmericasDb.initialSize}</value></property>
<property name="maxActive"><value>${HarmonyAmericasDb.maxActive}</value></property>
<property name="maxWait"><value>${HarmonyAmericasDb.maxWait}</value></property>
<property name="maxIdle"><value>${HarmonyAmericasDb.maxIdle}</value></property>
<property name="minIdle"><value>${HarmonyAmericasDb.minIdle}</value></property>
<property name="removeAbandoned"><value>${HarmonyAmericasDb.removeAbandoned}</value></property>
<property name="removeAbandonedTimeout"><value>${HarmonyAmericasDb.removeAbandonedTimeout}</value></property>
</bean>
<bean id="EMEADataSource" class="dell.harmony.data.HarmonyBasicDataSource" destroy-method="close" >
<property name="url"><value>${HarmonyEMEADb.url}</value></property>
<property name="driverClassName"><value>${HarmonyEMEADb.driverClassName}</value></property>
<property name="username"><value>${HarmonyEMEADb.username}</value></property>
<property name="password"><value>${HarmonyEMEADb.password}</value></property>
<property name="initialSize"><value>${HarmonyEMEADb.initialSize}</value></property>
<property name="maxActive"><value>${HarmonyEMEADb.maxActive}</value></property>
<property name="maxWait"><value>${HarmonyEMEADb.maxWait}</value></property>
<property name="maxIdle"><value>${HarmonyEMEADb.maxIdle}</value></property>
<property name="minIdle"><value>${HarmonyEMEADb.minIdle}</value></property>
<property name="removeAbandoned"><value>${HarmonyEMEADb.removeAbandoned}</value></property>
<property name="removeAbandonedTimeout"><value>${HarmonyEMEADb.removeAbandonedTimeout}</value></property>
</bean>
**<!-- Sku Deactivation -->**
<bean id="SkuAmerDao" class="dell.harmony.service.skudeactivation.dao.SkuDAOImpl">
<constructor-arg index="0"><ref bean="AmericasDataSource"/></constructor-arg>
</bean>
<bean id="SkuEMEADao" class="dell.harmony.service.skudeactivation.dao.SkuDAOImpl">
<constructor-arg index="0"><ref bean="EMEADataSource"/></constructor-arg>
</bean>
<bean id="ServiceManager" class="dell.harmony.service.skudeactivation.service.DeactivationService">
<constructor-arg index="0"><ref bean="SkuAmerDao"/></constructor-arg>
<constructor-arg index="1"><ref bean="SkuEMEADao"/></constructor-arg>
</bean>
</beans>
Now i want to convert the above classes to highlighted inside xml("Sku Deactivation") , into annodation.
My code for convertion is as follows:
#Service
public class DeactivationService {
private static final Logger LOG = Logger.getLogger(DeactivationService.class);
private SkuDAO amerdao; //Dependency Injection Amer
private SkuDAO emeadao; //Dependency Injection EMEA
#Autowired(required=true)
public DeactivationService( #Qualifier("SkuAmerDao") SkuDAO amerdao, #Qualifier("SkuEMEADao") SkuDAO emeadao) {
this.amerdao=amerdao;
this.emeadao=emeadao;
}
}
In the above constructor, now 'amerdao' instance, should be injected with AmericasDataSource and 'emeadao' with EMEADataSource, how to do that?
Please note, i dont have a setter in the SkuDAOImpl. Also there is only one datasource instance inside the SkuDAOImpl.
can you given sample code of SkuDAOImpl with annodation.
Any suggestion, to improve the coding from service to dao , if it can be done in a better way. (Not required to answer this)
EDITED NOW: just to be clear with question 1, I would like to remove the below two lines in Spring xml and use annotation instead my DeactivationService. Is it possible?
<bean id="SkuAmerDao" class="dell.harmony.service.skudeactivation.dao.SkuDAOImpl">
<constructor-arg index="0"><ref bean="AmericasDataSource"/></constructor-arg>
</bean>
<bean id="SkuEMEADao" class="dell.harmony.service.skudeactivation.dao.SkuDAOImpl">
<constructor-arg index="0"><ref bean="EMEADataSource"/></constructor-arg>
</bean>
What about:
#Service
public class DeactivationService {
private static final Logger LOG = Logger.getLogger(DeactivationService.class);
#Autowired
#Qualifier("SkuAmerDao")
private SkuDAO amerdao; //Dependency Injection Amer
#Autowired
#Qualifier("SkuEMEADao")
private SkuDAO emeadao; //Dependency Injection EMEA
// no constructor needed.
}
public abstract class BaseDao implements SkuDAO {
private final JdbcTemplate jdbcTemplate;
protected BaseDao() {
this.jdbcTemplate = new JdbcTemplate(getDataSource());
}
protected abstract DataSource getDataSource();
public List<Sku> find() {
//some processing to find the sku, purposely left empty as it is a sample code
}
public void deactivate(List<Sku>) {
//some processing to deactivate the sku, purposely left empty as it is a sample code
}
}
#Repository("SkuAmerDao")
public class SkuAmerDAOImpl extends BaseDao {
#Autowired
#Qualifier("AmericasDataSource")
private DataSource datasource; //Dependency injection
#Override
protected DataSource getDatasource() {
return dataSource;
}
}
#Repository("SkuEMEADao")
public class SkuEMEADAOImpl extends BaseDao {
#Autowired
#Qualifier("EMEADataSource")
private DataSource datasource; //Dependency injection
#Override
protected DataSource getDatasource() {
return dataSource;
}
}
Always the same principle:
class is made a bean by an annotation #Service, #Component, #Repository (those annotations can take the name of the bean as value)
injection of dependency is made on fields with #Autowired and if there are more than one corresponding bean (in your case you have two DataSources), add a #Qualifier to specify which one.
Full documentation here.

Spring JUnit4 manual-/auto-wiring dilemma

I ran into an issue that can only be explained with my fundamental lack of understanding of Spring's IoC container facilities and context setup, so I would ask for clarification regarding this.
Just for reference, an application I am maintaing has the following stack of technologies:
Java 1.6
Spring 2.5.6
RichFaces 3.3.1-GA UI
Spring framework is used for bean management with Spring JDBC module used for DAO support
Maven is used as build manager
JUnit 4.4 is now introduced as test engine
I am retroactively (sic!) writing JUnit tests for the application and what suprised me is that I wasn't able to inject a bean into a test class by using setter injection without resorting to #Autowire notation.
Let me provide set up an example and accompanying configuration files.
The test class TypeTest is really simple:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class TypeTest {
#Autowired
private IType type;
#Test
public void testFindAllTypes() {
List<Type> result;
try {
result = type.findAlltTypes();
assertNotNull(result);
} catch (Exception e) {
e.printStackTrace();
fail("Exception caught with " + e.getMessage());
}
}
}
Its context is defined in TestStackOverflowExample-context.xml:
<context:property-placeholder location="classpath:testContext.properties" />
<context:annotation-config />
<tx:annotation-driven />
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${db.connection.driver.class}" />
<property name="url" value="${db.connection.url}" />
<property name="username" value="${db.connection.username}" />
<property name="password" value="${db.connection.password}" />
</bean>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="beanDAO" class="com.example.BeanDAOImpl">
<property name="ds" ref="dataSource"></property>
<property name="beanDAOTwo" ref="beanDAOTwo"></property>
</bean>
<bean id="beanDAOTwo" class="com.example.BeanDAOTwoImpl">
<property name="ds" ref="dataSource"></property>
</bean>
<bean id="type" class="com.example.TypeImpl">
<property name="beanDAO" ref="beanDAO"></property>
</bean>
TestContext.properties is in classpath and contains only db-specific data needed for datasource.
This works like a charm but my question is - why doesn't it work when I try to manually wire beans and perform setter injection as in:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class TypeTest {
private IType type;
public IType getType () {
return type;
}
public void setType(IType type) {
this.type= type;
}
#Test
public void testFindAllTypes(){
//snip, snip...
}
}
What am I missing here? What part of configuration is wrong here? When I try to manually inject beans via setters, test fails because this part
result = type.findAlltTypes();
is resolved as null in runtime. I've, of course, consulted the Spring reference manual and tried various combinations of XML configuration; all I could conclude is that Spring was unable to inject beans because it somehow fails to properly dereference Spring Test Context reference but by using #Autowired this happens "automagically" and I really can't see why is that because JavaDoc of both Autowired annotation and its PostProcessor class doesn't mention this.
Also worth adding is the fact that #Autowired is used in application only here. Elsewhere only manual wiring is performed, so this also brings forth question - why is it working there and not here, in my test? What part of DI configuration am I missing? How does #Autowired get reference of Spring Context?
EDIT:
I've also tried this but with same results:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class TypeTest implements ApplicationContextAware{
private IType type;
private ApplicationContext ctx;
public TypeTest(){
super();
ctx = new FileSystemXmlApplicationContext("/TypeTest-context.xml");
ctx.getBean("type");
}
public IType getType () {
return type;
}
public void setType(IType type) {
this.type= type;
}
#Test
public void testFindAllTypes(){
//snip, snip...
}
}
Any other ideas, perhaps?
EDIT2:
I've found a way without resorting to writing own TestContextListener or BeanPostProcessor. It is suprisingly simple and it turns out that I was on the right track with my last edit:
1) Constructor-based context resolving:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class TypeTest{
private IType type;
private ApplicationContext ctx;
public TypeTest(){
super();
ctx = new FileSystemXmlApplicationContext("/TypeTest-context.xml");
type = ctx.getBean("type");
}
public IType getType () {
return type;
}
public void setType(IType type) {
this.type= type;
}
#Test
public void testFindAllTypes(){
//snip, snip...
}
}
2) By implementing ApplicationContextAware interface:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class TypeTest implements ApplicationContextAware{
private IType type;
private ApplicationContext ctx;
public IType getType () {
return type;
}
public void setType(IType type) {
this.type= type;
}
#Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
this.ctx = ctx;
type = (Type) ctx.getBean("type");
}
#Test
public void testFindAllTypes(){
//snip, snip...
}
}
Both of these approaches properly instanced beans.
If you take a look at the source of org.springframework.test.context.support.DependencyInjectionTestExecutionListener, you will see the following method (formatted and commented for clarity):
protected void injectDependencies(final TestContext testContext)
throws Exception {
Object bean = testContext.getTestInstance();
AutowireCapableBeanFactory beanFactory = testContext.getApplicationContext()
.getAutowireCapableBeanFactory();
beanFactory.autowireBeanProperties(bean,
AutowireCapableBeanFactory.AUTOWIRE_NO,
// no autowiring!!!!!!!!
false
);
beanFactory.initializeBean(bean, testContext.getTestClass().getName());
// but here, bean post processors are run
testContext.removeAttribute(REINJECT_DEPENDENCIES_ATTRIBUTE);
}
So the test object is a bean without auto-wiring. However, #AutoWired, #Resource etc, don't use the autowiring mechanism, they use BeanPostProcessor. And so the dependencies are injected if and only if the annotations are used (or if you register some other BeanPostProcessor that does it).
(The above code is from Spring 3.0.x, but I bet it was the same in 2.5.x)

Prototype scope not working

I've created a prototype scoped bean in my application and I'm injecting that to another bean using a setter. But when I'm using the injected bean in my class it is always using the same instance instead of new instance every time.
Here is a snapshot of the code
<bean name="prototypeScope" Class="A" scope="prototype">
</bean>
<bean Class="Consumer">
<property name="a" ref="prototypeScope" />
</bean>
public class Consumer{
privare A a;
public void setA(A a){
this.a = a;
}
public void consume(){
a.doSomething();
}
}
Regards
It is an common mistake related to prototype scoped beans.
A new instance of the prototype scoped bean will be created only when we request a copy of the bean from the application context, not every time we call a method on the instance.
In your case you are injecting the prototype scoped bean to another bean using the setter of the later, so then the second class is created a new instance of the prototype scoped bean will be created. But it will use the same instance as long as it is replace by another instace mannualy by you.
If you want a new instance of the prototype scoped bean during a particular operation like a method call, you have to get a new instance of the bean from the application content.
Ex:
<bean name="prototypeScope" Class="A" scope="prototype">
</bean>
<bean Class="Consumer">
</bean>
Java code:
public class Consumer implements ApplicationContextAware{
privare ApplicationContext context;
public void setApplicationContext(ApplicationContext context){
this.context = context;
}
public void consume(){
A a = context.getBean("prototypeScope", A.class);
a.doSomething();
}
}
In this example when ever the consume method is called a new instance of class A is created.
There are two main ways to deal with the Singleton-Bean-has-Prototype-dependency problem.
One is tight coupling to the applicationContext, as in Ram's answer, the other is Lookup Method Injection.
Basically, you make your bean class abstract and add an abstract method for the dependency, something like this:
public abstract class MyBean{
public abstract MyService lookupService();
}
Then you add a bean definition something like this:
<bean id="myBean" class="fiona.apple.sucks.MyBean">
<!-- sorry, just wanted to insert sucks after Spring's fiona apple example,
didn't think about the connotations :-) -->
<lookup-method name="lookupService"/>
</bean>
Now Spring will create a CGLib subclass of your bean class that will return a new Prototype instance for every time you call myBean.lookupService().
I used such approach once..
First I declared a bean
<bean id="notSingelton" class="com.Foo" singleton="false" />
Then made an interface
public interface FooFactory {
Foo make(String name);
}
wrapped it into ServiceLocatorFactoryBean
<bean id="fooFactory"
class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean">
<property name="serviceLocatorInterface" value="com.FooFactory" />
</bean>
<bean id="consumer" class="com.Consumer">
<constructor-arg ref="fooFactory" />
</bean>
And consumer class looked something like that:
public class Consumer {
private FooFactory fooFactory;
public Consumer(FooFactory fooFactory) {
this.fooFactory = fooFactory;
}
public void consume(){
Foo foo = fooFactory.make("notSingelton");
foo.doSomething();
}
}

How to collect and inject all beans of a given type in Spring XML configuration

One of the strongest accents of the Spring framework is the Dependency Injection concept. I understand one of the advices behind that is to separate general high-level mechanism from low-level details (as announced by Dependency Inversion Principle).
Technically, that boils down to having a bean implementation to know as little as possible about a bean being injected as a dependency, e.g.
public class PrintOutBean {
private LogicBean logicBean;
public void action() {
System.out.println(logicBean.humanReadableDetails());
}
//...
}
<bean class="PrintOutBean">
<property name="loginBean" ref="ShoppingCartBean"/>
</bean>
But what if I wanted to a have a high-level mechanism operating on multiple dependent beans?
public class MenuManagementBean {
private Collection<Option> options;
public void printOut() {
for (Option option:options) {
// do something for option
}
//...
}
}
I know one solution would be to use #Autowired annotation in the singleton bean, that is...
#Autowired
private Collection<Option> options;
But doesn't it violate the separation principle? Why do I have to specify what dependents to take in the very same place I use them (i.e. MenuManagementBean class in my example)?
Is there a way to inject collections of beans in the XML configuration like this (without any annotation in the MMB class)?
<bean class="MenuManagementBean">
<property name="options">
<xxx:autowire by-type="MyOptionImpl"/>
</property>
</bean>
Old question and in Spring 3.1 it is possible:
public class PluginPrototypeTest extends ASpringWebTest {
#Autowired
Collection<IDummyRepo> repos;
#Test
public void cacheTest() {
assertNotNull(repos);
assertEquals(2, repos.size());
for(IDummyRepo r: repos){
System.out.println(r.getName());
}
}
}
#Repository
public class DummyRepo implements IDummyRepo {
#Override
public String getName(){
return "DummyRepo";
}
}
#Repository
public class DummyRepo2 implements IDummyRepo {
#Override
public String getName(){
return "DummyRepo2";
}
}
There's no out-of-the-box facility to do this, no. However, if you want a way of collecting all beans of a given type into a collection, without using an #Autowired list, then it's easy to write a custom FactoryBean to do it for you:
public class BeanListFactoryBean<T> extends AbstractFactoryBean<Collection<T>> {
private Class<T> beanType;
private #Autowired ListableBeanFactory beanFactory;
#Required
public void setBeanType(Class<T> beanType) {
this.beanType = beanType;
}
#Override
protected Collection<T> createInstance() throws Exception {
return beanFactory.getBeansOfType(beanType).values();
}
#Override
public Class<?> getObjectType() {
return Collection.class;
}
}
and then
<bean class="MenuManagementBean">
<property name="options">
<bean class="BeanListFactoryBean">
<property name="beanType" class="MyOptionImpl.class"/>
</bean>
</property>
</bean>
However, this all seems like a lot of effort to avoid putting #Autowired in your original class. It's not much of a violation of SoC, if it is at all - there's no compiltime dependency, and no knowledge of where the options are coming from.
Alternative to #Autowired, using a context file: http://static.springsource.org/spring/docs/2.5.x/reference/beans.html#beans-factory-autowire
So you'd have:
<bean class="MenuManagementBean" autowire="byType" />
Other properties can be specified, as normal, and that would override the autowiring only for those properties.

Categories

Resources