Is there a way to iterate list or map in spring? I am not able to find any references for this online.
This is what I defined-
<?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:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd">
<util:list id="myList" value-type="java.lang.String">
<value>foo</value>
<value>bar</value>
</util:list>
<bean id="bean1" class="com.StaticClass" factory-method="createObject">
<constructor-arg value="foo" />
</bean>
<bean id="bean2" class="com.StaticClass" factory-method="createObject">
<constructor-arg value="bar" />
</bean>
<bean id="myMap" class="java.util.HashMap">
<constructor-arg index="0" type="java.util.Map">
<map key-type="java.lang.Integer" value-type="java.lang.Float">
<entry key="foo" value-ref=bean1 />
<entry key="bar" value-ref=bean2 />
</map>
</constructor-arg>
</bean>
Instead of creating multiple bean objects, I want to iterate over this list and create a map, using following logic-
for (String m : myList) {
myMap.put(m, MyStaticFactory.createObject(m));
}
Can I do this in Spring?
How about using spring #Configuration (see explanation in this link) instead of spring XML?
#Configuration
public class MySpringContext {
#Bean(name="myMap")
public Map<String, StaticClass> getMyMapBean() {
// I'm not sure where you create 'm' but if that's a bean you can inject it to the class and use it.
for (String m : myList) {
myMap.put(m, MyStaticFactory.createObject(m));
}
}
}
#Configuration classes are a way to define your beans programatically instead of XMLs which gives you much more flexibility to do whatever you want.
Something like this maybe:
public class MyMapBean extends HashMap {
public MyMapBean(List<String> beanNames) {
for(name: beanNames) put(name, MyStaticFactory.createObject(name));
}
}
and then in application context:
<bean id="myMap" class="MyMapBean">
<constructor-arg index="0" value-ref="myList" />
</bean>
Related
I am writing a standalone java program that call spring data repository for manging enities. I am using mongo db for persistence. I am following stackoverflow posts and some projects from github but when I run my program it fails since the repository is null. I am not expert in spring so it would be helpful if someone could show me the issue with the posted program.
Application-context.xml
<?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"
xmlns:mongo="http://www.springframework.org/schema/data/mongo"
xsi:schemaLocation="http://www.springframework.org/schema/data/mongo
http://www.springframework.org/schema/data/mongo/spring-mongo.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<mongo:mongo id="mongo" host="monopolyvm3" port="27017" />
<mongo:db-factory dbname="test" mongo-ref="mongo" />
<mongo:db-factory id="mongoDbFactory" dbname="cloud"
mongo-ref="mongo" />
<bean id="mappingContext" class="org.springframework.data.mongodb.core.mapping.MongoMappingContext" />
<bean id="defaultMongoTypeMapper"
class="org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper">
<constructor-arg name="typeKey"><null/></constructor-arg>
</bean>
<bean id="mappingMongoConverter" class="org.springframework.data.mongodb.core.convert.MappingMongoConverter">
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory" />
<constructor-arg name="mappingContext" ref="mappingContext" />
<property name="typeMapper" ref="defaultMongoTypeMapper" />
</bean>
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg ref="mongoDbFactory" />
<constructor-arg name="mongoConverter" ref="mappingMongoConverter" />
<property name="writeConcern" value="SAFE" />
</bean>
<context:component-scan base-package="com.xxxx"></context:component-scan>
<mongo:repositories base-package="com.xxxx.yyyy" />
Repository
public interface SlpRepository extends MongoRepository<Slp, Long> {
}
main program
public class App {
#Autowired
static
SlpRepository slpRepository;
public static void main(String[] args) {
ApplicationContext ctx = new GenericXmlApplicationContext("application-context.xml");
Slp slp = new Slp();
slp.setClientCount(100000L);
slp.setPolicyName("testing");
slp.setSlpName("slp_testing");
slpRepository.save(slp);
}
}
Object to store
------------------
#Document(collection="slps")
public class Slp implements Serializable {
private Long slpId;
private String slpName;
private String policyName;
private Long clientCount;
}
.....all getters and setters
JFYI..I tried to save object using mongotemplate and it works well.
The problem in this example is that you create the context in your App class but never get a handle to the repository from the application context. You cannot auto wire into the same class that holds the application context. What you can try is the get the instance of the repository from the context (i.e ctx.getBean(SlpRepository.class)).
I am trying to define an enum map in my Spring beans xml, and I want it to populate in the xml, however when I try to define it like this
<bean class = "java.util.EnumMap">
<constructor-arg>
<util:map key-type="org.itemlist.products.stockitem">
<entry key="stockitem.SOAP">100</entry>
</util:map>
</constructor-arg>
UPDATE
Here is my beans 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:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.0.xsd">
<bean class = "java.util.EnumMap">
<constructor-arg>
<util:map key-type="org.itemlist.products.stockitem">
<entry key="stockitem.SOAP">100</entry>
</util:map>
</constructor-arg>
</bean>
</beans>
When I add a value inside entry, this is now the error
cvc-complex-type.2.3: Element 'entry' cannot have character [children], because the type's content type is element-only.
Have you define those schema in the header:
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.0.xsd
And the namespace:
xmlns:util="http://www.springframework.org/schema/util"
You can define it like this :
<bean id="springBean" class="a.b.c.d.MyEnum">
<constructor-arg>
<util:map key-type="a.b.c.d.MyEnum" value-type="aa.b.c.d.Mya.b.c.d.MyVal">
<entry>
<key><value type="a.b.c.d.MyEnum">ENUM-VAL1</value></key>
<ref bean="myValBean1" />
</entry>
<entry>
<key><value type="a.b.c.d.MyEnum">ENUM-VAL1</value></key>
<ref bean="myValBean1" />
</entry>
</util:map>
</constructor-arg>
</bean>
That error message means that the entry element must be empty, i.e. contain no text or other elements. The syntax you want is:
<entry key="stockitem.SOAP" value="100"/>
The entry element also lets you pass a reference to another bean as the value, e.g.:
<entry key="stockitem.SOAP" value-ref="myOtherBean"/>
(which is of no use in your situation, I just mention it for completeness)
Imagine a staged application context with different phases. We start with an early phase to define the necessary infrastructure. The xml application contexts are loaded sequentially.
The reason to split up these files is an extension/plugin mechanism.
Stage 01-default-configuration.xml
We prepare and declare the map with id exampleMapping to enhance them later with data.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="[...]">
<util:map id="exampleMapping" />
</beans>
Stage 02-custom-configuration.xml (optional)
We configure the exampleMapping and add an entry.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="[...]">
<util:map id="exampleMapping">
<entry key="theKey" value="theValue" />
</util:map>
</beans>
Stage 03-make-use-of-configuration.xml (mandatory)
Uses the defined map exampleMapping, whether it's configured customly or it's still the empty declared map.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="[...]">
<bean id="exampleService" class="com.stackoverflow.example.ExampleService">
<property name="mapping" ref="exampleMapping" />
</bean>
</beans>
The problem here is, that it's not possible to add entries to the exampleMapping map after the first stage. Spring throws an exception that the map with id exampleMapping already exists. If we leave out the first stage the map is undeclared and the third stage can't resolve exampleMapping which also produces an exception.
How can I solve this issue? I read Collection merging (spring docs) but this didn't helped. Is it possible to add values later to maps/lists before using them?
Thank you!
Both map and list has this attribute named merge=true|false for merging two lists. Alternatively you can use MethodInvokingFactoryBean for calling already defined list's add method to add extra items later.
Let's go over your example.
1) First the second scenario with MethodInvokingFactoryBean. Instead of defining the way you do, I defined your beans a little differently.
<bean class="java.util.HashMap" id="exampleMapping">
<constructor-arg index="0">
<map>
<entry key="theKey" value="theValue"/>
</map>
</constructor-arg>
</bean>
<bean id="exampleService" class="com.stackoverflow.example.ExampleService">
<property name="mapping" ref="exampleMapping"/>
</bean>
In another application content file, you can do the following to extend the map.
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="exampleMapping"/>
<property name="targetMethod" value="putAll"/>
<property name="arguments">
<list>
<map id="exampleMapping">
<entry key="theKey2" value="theValue2"/>
<entry key="theKey3" value="theValue3"/>
<map>
</list>
</property>
</bean>
2) Now the first scenario. For this one, I just found something on page http://forum.springsource.org/showthread.php?t=53358
<bean id="commonData" class="A">
<property name="map">
<util:map>
<entry key="1" value="1"/>
</util:map>
</property>
</bean>
<bean id="data" class="A" parent="commonData">
<property name="map">
<util:map merge="true">
<entry key="2" value="2"/>
</util:map>
</property>
</bean>
Hope it helps.
You can define exampleMapping is the second definition is in a separate file, and you use <import resource="..."/> to import one file into another, but it's a fragile approach, and easily broken.
I suggest a more robust strategy. Replace the exampleMapping with a Registry class, which in turn contains and manages the mappings:
public MappingRegistry<K,V> {
private final Map<K,V> mappings = new HashMap<K,V>();
public void addMapping(K key, V value) {
mappings.put(key, value);
}
public Map<K,V> getMappings() {
return Collections.unmodifiableMap(mappings);
}
}
Then, write a class which registers a mapping with the registry:
public class MappingRegistrar<K,V> {
private final MappingRegistry<K,V> registry;
private K key;
private V value;
#Autowired
public MappingRegistrar(MappingRegistry<K,V> registry) {
this.registry = registry;
}
public void setKey(K key) {
this.key = key;
}
public void setValue(V value) {
this.value = value;
}
#PostConstruct
public void registerMapping() {
registry.addMapping(key, value);
}
}
The your config becomes something like this:
<bean id="mappingRegistry" class="com.xyz.MappingRegistry"/>
<bean id="mappingA" class="com.xyz.MappingRegistrar" p:key="keyA" p:value="valueA"/>
<bean id="mappingB" class="com.xyz.MappingRegistrar" p:key="keyB" p:value="valueB"/>
<bean id="mappingC" class="com.xyz.MappingRegistrar" p:key="keyC" p:value="valueC"/>
These mappings can now be scattered throughout your config in any way you see fit, and they will self-assemble. ExampleServcie is then injected with the MappingRegistry and extracts the mappings accordingly.
It's a bit more work than what you already have, but it's a lot more flexible and less error prone. This is particularly valuable if you're trying to build an extensible framework of some kind; you want to put fewer constraints on how people use it.
I'm using Spring to define stages in my application. It's configured that the necessary class (here called Configurator) is injected with the stages.
Now I need the List of Stages in another class, named LoginBean. The Configurator doesn't offer access to his List of Stages.
I cannot change the class Configurator.
My Idea:
Define a new bean called Stages and inject it to Configurator and LoginBean.
My problem with this idea is that I don't know how to transform this property:
<property ...>
<list>
<bean ... >...</bean>
<bean ... >...</bean>
<bean ... >...</bean>
</list>
</property>
into a bean.
Something like this does not work:
<bean id="stages" class="java.util.ArrayList">
Can anybody help me with this?
Import the spring util namespace. Then you can define a list bean as follows:
<?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:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-2.5.xsd">
<util:list id="myList" value-type="java.lang.String">
<value>foo</value>
<value>bar</value>
</util:list>
The value-type is the generics type to be used, and is optional. You can also specify the list implementation class using the attribute list-class.
Here is one method:
<bean id="stage1" class="Stageclass"/>
<bean id="stage2" class="Stageclass"/>
<bean id="stages" class="java.util.ArrayList">
<constructor-arg>
<list>
<ref bean="stage1" />
<ref bean="stage2" />
</list>
</constructor-arg>
</bean>
<bean id="someBean"
class="com.somePackage.SomeClass">
<property name="myList">
<list value-type="com.somePackage.TypeForList">
<ref bean="someBeanInTheList"/>
<ref bean="someOtherBeanInTheList"/>
<ref bean="someThirdBeanInTheList"/>
</list>
</property>
</bean>
And in SomeClass:
class SomeClass {
List<TypeForList> myList;
#Required
public void setMyList(List<TypeForList> myList) {
this.myList = myList;
}
}
Another option is to use JavaConfig. Assuming that all stages are already registered as spring beans you just have to:
#Autowired
private List<Stage> stages;
and spring will automatically inject them into this list. If you need to preserve order (upper solution doesn't do that) you can do it in that way:
#Configuration
public class MyConfiguration {
#Autowired
private Stage1 stage1;
#Autowired
private Stage2 stage2;
#Bean
public List<Stage> stages() {
return Lists.newArrayList(stage1, stage2);
}
}
The other solution to preserve order is use a #Order annotation on beans. Then list will contain beans ordered by ascending annotation value.
#Bean
#Order(1)
public Stage stage1() {
return new Stage1();
}
#Bean
#Order(2)
public Stage stage2() {
return new Stage2();
}
Stacker posed a great answer, I would go one step farther to make it more dynamic and use Spring 3 EL Expression.
<bean id="listBean" class="java.util.ArrayList">
<constructor-arg>
<value>#{springDAOBean.getGenericListFoo()}</value>
</constructor-arg>
</bean>
I was trying to figure out how I could do this with the util:list but couldn't get it work due to conversion errors.
I think you may be looking for org.springframework.beans.factory.config.ListFactoryBean.
You declare a ListFactoryBean instance, providing the list to be instantiated as a property withe a <list> element as its value, and give the bean an id attribute. Then, each time you use the declared id as a ref or similar in some other bean declaration, a new copy of the list is instantiated. You can also specify the List class to be used.
<bean id="student1" class="com.spring.assin2.Student">
<property name="name" value="ram"></property>
<property name="id" value="1"></property>
<property name="listTest">
<list value-type="java.util.List">
<ref bean="test1"/>
<ref bean="test2"/>
</list>
</property>
</bean>
define those beans(test1,test2) afterwards :)
Inject list of strings.
Suppose you have Countries model class that take list of strings like below.
public class Countries {
private List<String> countries;
public List<String> getCountries() {
return countries;
}
public void setCountries(List<String> countries) {
this.countries = countries;
}
}
Following xml definition define a bean and inject list of countries.
<bean id="demoCountryCapitals" name="demoCountryCapitals" class="com.sample.pojo.Countries">
<property name="countries">
<list>
<value>Iceland</value>
<value>India</value>
<value>Sri Lanka</value>
<value>Russia</value>
</list>
</property>
</bean>
Reference link
Inject list of Pojos
Suppose if you have model class like below.
public class Country {
private String name;
private String capital;
.....
.....
}
public class Countries {
private List<Country> favoriteCountries;
public List<Country> getFavoriteCountries() {
return favoriteCountries;
}
public void setFavoriteCountries(List<Country> favoriteCountries) {
this.favoriteCountries = favoriteCountries;
}
}
Bean Definitions.
<bean id="india" class="com.sample.pojo.Country">
<property name="name" value="India" />
<property name="capital" value="New Delhi" />
</bean>
<bean id="russia" class="com.sample.pojo.Country">
<property name="name" value="Russia" />
<property name="capital" value="Moscow" />
</bean>
<bean id="demoCountryCapitals" name="demoCountryCapitals" class="com.sample.pojo.Countries">
<property name="favoriteCountries">
<list>
<ref bean="india" />
<ref bean="russia" />
</list>
</property>
</bean>
Reference Link.
Use the util namespace, you will be able to register the list as a bean in your application context. You can then reuse the list to inject it in other bean definitions.
As an addition to Jakub's answer, if you plan to use JavaConfig, you can also autowire that way:
import com.google.common.collect.Lists;
import java.util.List;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Bean;
<...>
#Configuration
public class MyConfiguration {
#Bean
public List<Stage> stages(final Stage1 stage1, final Stage2 stage2) {
return Lists.newArrayList(stage1, stage2);
}
}
You just remove id out of beans inside <list> tag. Like this:
<property name="listStaff">
<list>
<bean class="com.test.entity.Staff">
<constructor-arg name="name" value = "Jonh"/>
<constructor-arg name="age" value = "30"/>
</bean>
<bean class="com.test.entity.Staff">
<constructor-arg name="name" value = "Jam"/>
<constructor-arg name="age" value = "21"/>
</bean>
</list>
</property>
Use list-class attribute in util:list to make a standalone list of any particular type. for example if you want to make list of type ArrayList:
<util:list id="namesList" list-class="java.util.ArrayList" value-type="java.lang.String">
<value>Abhay</value>
<value>ankit</value>
<value>Akshansh</value>
<value>Db</value>
</util:list>
or if you want to make a list of type LinkedList then :
<util:list id="namesList" list-class="java.util.LinkedList" value-type="java.lang.String">
<value>Abhay</value>
<value>ankit</value>
<value>Akshansh</value>
<value>Db</value>
</util:list>
And this is how to inject set in some property in Spring:
<bean id="process"
class="biz.bsoft.processing">
<property name="stages">
<set value-type="biz.bsoft.AbstractStage">
<ref bean="stageReady"/>
<ref bean="stageSteady"/>
<ref bean="stageGo"/>
</set>
</property>
</bean>
In my service class I need the hibernate session available. I currently do this in the beans.xml:
<bean id = "userDao" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target">
<ref bean="userDaoTarget" />
</property>
<property name="proxyInterfaces">
<value>com.app.dao.UserDao</value>
</property>
<property name="interceptorNames">
<list>
<value>hibernateInterceptor</value>
</list>
</property>
<qualifier value="proxy" />
</bean>
...
<bean id="hibernateInterceptor"
class="org.springframework.orm.hibernate3.HibernateInterceptor">
<property name="sessionFactory">
<ref bean="sessionFactory" />
</property>
<bean>
(copied by hand, may be some typos..)
I'm moving to using annotations over XML, I was wondering if there was a way to use them to configure the proxy as I have above including the hibernate interceptor? If not - is there a way that I can reduce the amount of XML (with about 7 DAOs it makes it very cluttered)
Ok, Let's go. You said
I am moving to using annotations over XML
Enable an aspect as follows
package br.com.ar.aop;
#Aspect
public class HibernateInterceptorAdvice {
#Autowired
private HibernateInterceptor hibernateInterceptor;
/**
* I suppose your DAO's live in com.app.dao package
*/
#Around("execution(* com.app.dao.*(..))")
public Object interceptCall(ProceedingJoinPoint joinPoint) throws Throwable {
ProxyFactory proxyFactory = new ProxyFactory(joinPoint.getTarget());
proxyFactory.addAdvice(hibernateInterceptor);
Class [] classArray = new Class[joinPoint.getArgs().length];
for (int i = 0; i < classArray.length; i++)
classArray[i] = joinPoint.getArgs()[i].getClass();
return
proxyFactory
.getProxy()
.getClass()
.getDeclaredMethod(joinPoint.getSignature().getName(), classArray)
.invoke(proxyFactory.getProxy(), joinPoint.getArgs());
}
}
But keep in mind It just works if your DAO's implements some interface (For instance, UserDAOImpl implements UserDAO). Spring AOP uses JDK dynamic proxy in this case. If you does not have any interface, you can rely on your IDE To refactor your code by using Extract interface
Declare your xml as follows (Be aware i am using Spring 2.5 xsd schema)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<!--SessionFactory settings goes here-->
<bean class="org.springframework.orm.hibernate3.HibernateInterceptor">
<property name="sessionFactory" ref="sessionFactory"/>
<bean>
<!--To enable AspectJ AOP-->
<aop:aspectj-autoproxy/>
<!--Your advice-->
<bean class="br.com.ar.aop.HibernateInterceptorAdvice"/>
<!--Looks for any annotated Spring bean in com.app.dao package-->
<context:component-scan base-package="com.app.dao"/>
<!--Enables #Autowired annotation-->
<context:annotation-config/>
</beans>
Do not forget To put in the classpath besides Spring libraries
<SPRING_HOME>/lib/asm
<SPRING_HOME>/lib/aopalliance
<SPRING_HOME>/lib/aspectj
Have a look at the #Autowired annotation.