I'm trying to lazily initialize beans in a Spring map such that the beans inside are only initialized when they are retrieved via map.get. Consider the following code:
Spring config:
<bean class="java.util.HashMap">
<constructor-arg>
<map>
<entry key="foo">
<bean class="Messager" lazy-init="true">
<constructor-arg index="0" value="bar" />
</bean>
</entry>
</map>
</constructor-arg>
</bean>
Test class:
public class Messager {
public Messager(String message) {
System.out.println(String.format("Initialized %s", message));
}
}
If you run the above code, the Messager bean in the map is initialized upon application startup. How can I defer bean initialization until the map entry is retrieved?
lazy-true simply defers the initialization till the first reference of this spring bean from other bean (or internal Spring infrastructure code as it happens in this case).
Here atmost the initialization of outer map can be deferred but once it is initialized the map passed via constructor would be completely initialized. The map element functionality is supported by Spring's MapFactoryBean and it does not looks like that it supports the feature you desire as it initializes the values in the map in one go. So
AFAIK , Spring does not have this support out of box - however you can try your own implmentation of a lazy map or use LazyMap of commons-collection.
Also you might need to write a FactoryBean to enable this class to be usable in Spring context.
Related
I have configured a map in spring which returns me the instance based on key. But the thing is it always returns me the same instance of the map entries(plate, spoon, fork) instead of creating a new instance every time even though the bean is a prototype. What am i missing?
Please note i am working on a very old version of spring.
Here my configuration:
<bean id="plate" class="com.xyz.items" singleton="false"/>
<bean id="spoon" class="com.xyz.items" singleton="false"/>
<bean id="fork" class="com.xyz.items" singleton="false"/>
<bean id="ItemFactory" class="com.xyz.items.ItemFactory" >
<property name="registeredItems">
<map>
<entry key="spoon" value-ref="spoon"/>
<entry key="plate" value-ref="plate"/>
<entry key="fork" value-ref="fork"/>
</map>
</property>
</bean>
//Here's the stuff in java
public class ItemFactory {
private Map registeredItems;
private Item getItem(String item ){
Item item = (Item)registeredItems.get(item);
return item;
}}
|
I think that since ItemFactory is singleton it all it's life holds reference to same instance of Bean no matter that referenced bean is prototype scoped. Spring doesn't care about prototype scoped bean destruction, it simply creates new instance of bean when it's referenced from singleton and that's it.
You are referencing bean with smaller scope (prototype) from bean with larger scope (singleton), that's problematic.
See referencing prototype beans from singletons
There are two solution for this case:
Use method injection as described in link above.
In getItem() method retrieve beans directly from Spring application context.
I have a problem with Spring: I need to reuse the same instance of bean twice, but not making it singleton.
Here is a brief ApplicationContext:
<bean class="a.b.c.Provider" id="defaultProvider" scope="prototype">
<constructor-arg ref="lifecycle" />
<constructor-arg ref="propertySetter" />
</bean>
<bean name="lifecycle" class="a.b.c.Lifecycle" scope="prototype">
<constructor-arg ref="someParam" />
... and more args
</bean>
<bean id="propertySetter" class="a.b.c.PropertySetter" scope="prototype">
<constructor-arg ref="lifecycle" />
</bean>
So, I need to have fully initialized Provider with Lifecycle and PropertySetter inside,
and this PropertySetter must contain reference to same Lifecycle, as the Provider have.
When I define lifecycle and propertySetter as singletons, it causes big problems, because
if I create more than one Provider, all instances of Provider class shares same lifecycle
and property setter, and it's breaking application logic.
When I try to define all beans as prototypes, Lifecycles in Provider and in PropertySetter are not the same => exceptions again.
I have one solution: to pass to Provider only Lifecycle and create PropertySetter inside Provider java constructor (by extending Provider).
It is working well, but only in my local environment. In production code I can't extend 3pty Provider class, so I can't use this solution.
Please advise me, what most appropriate to do in this situation?
You don't need to extend Provider. Just create your own ProviderFactory that will take reference to lifecycle and will create PropertySetter and then Provider:
public class ProviderFactory {
public static create(Lifecycle lc) {
return new Provider(lc, new PropertySetter(lc));
}
}
Here is Spring declaration:
<bean id="defaultProvider" scope="prototype"
class="a.b.c.ProviderFactory" factory-method="create">
<constructor-arg ref="lifecycle" />
</bean>
In Spring I can define a HashSet like so in XML:
<bean id="subscriberStore" class="java.util.HashSet"/>
And, I can do the following in the code to create a concurrent hash set:
subscriberStore = Collections.newSetFromMap(
new ConcurrentHashMap<Subscriber, Boolean>());
But is there any way I can do this in one step in the XML? E.g. something like:
<bean id="subscriberStore" class="java.util.HashSet"/>
< Some code here to set subscriberStore to the result
of Collections.newSetFromMap(new ConcurrentHashMap<Subscriber, Boolean>? >
Many Thanks!
Bean configuration:
<!-- The bean to be created via the factory bean -->
<bean id="exampleBean"
factory-bean="myFactoryBean"
factory-method="createInstance"/>
<bean id="myFactoryBean" class="com.rory.ConcurrentHashMapFactory"/>
And the factory class:
public class ConcurrentHashMapFactory {
public Set<Subscriber> createInstance() {
Collections.newSetFromMap(new ConcurrentHashMap<Subscriber, Boolean>());
}
}
You could use something like the following:
<bean
id="subscriberStore"
class="java.util.Collections"
factory-method="newSetFromMap"
>
<constructor-arg>
<bean class="java.util.concurrent.ConcurrentHashMap" />
</constructor-arg>
</bean>
However, if generic types are important to you, create a custom, static factory method (as Boris Pavlović kind of suggests in his answer). You might want to take a look at this SO entry for some information regarding generics and Spring XML bean definitions.
I have a spring beans configuration file where I define the following jackson classes as spring beans.
For some reason on run-time the filterProvider bean is instantiated without the map argument.
You can see from the docs that the SimpleFilterProvider does have such a constructor and that SimpleBeanPropertyFilter implements BeanPropertyFilter.
<bean id="productAttributesAndAdvertiserNameFilter" class="org.codehaus.jackson.map.ser.impl.SimpleBeanPropertyFilter" factory-method="filterOutAllExcept">
<constructor-arg value="name"/>
</bean>
<bean id="offerIdFilter" class="org.codehaus.jackson.map.ser.impl.SimpleBeanPropertyFilter" factory-method="filterOutAllExcept">
<constructor-arg value="id"/>
</bean>
<bean id="filterProvider" class="org.codehaus.jackson.map.ser.impl.SimpleFilterProvider">
<constructor-arg>
<util:map value-type="org.codehaus.jackson.map.ser.BeanPropertyFilter">
<entry key="onlyNameFilter" value-ref="productAttributesAndAdvertiserNameFilter" />
<entry key="onlyIdFilter" value-ref="offerIdFilter" />
</util:map>
</constructor-arg>
</bean>
Update:
As of Jackson 1.9.5 this issue is fixed (thanks Tatu)
Any help would be appreciated.
Looks like you've found a bug in SimpleFilterProvider.
I just downloaded the latest sources (1.9.4) and the constructors are defined as such:
public SimpleFilterProvider() {
_filtersById = new HashMap<String,BeanPropertyFilter>();
}
/**
* #param mapping Mapping from id to filter; used as is, no copy is made.
*/
public SimpleFilterProvider(Map<String,BeanPropertyFilter> mapping) {
_filtersById = new HashMap<String,BeanPropertyFilter>();
}
The constructor which takes the mapping ignores it... (i.e. javadoc is incorrect)
I think <util:map> is misplaced here. I'd make it a separate bean, outside of the filter provider declaration, and refer to it. OR I'd change that to a <map> without the util namespace.
I don't see why it is not working.
At worst, you can create your own class by extending the SimpleFilterProvider and declare this bean in your Spring context...
I Am very new to Spring. I have an Interface (MessageHandler ) which has a get method, this method returns a list of Implementations of another interface (messageChecker).
public interface MessageHandler {
public void process(BufferedReader br);
public void setMessageCheckerList(List mcList);
[B]public List getMessageCheckerList();[/B]
}
In my Spring XML configuration , i have something like this ,along with other beans
<bean id="messageHandler" class="com.XXX.messagereceiver.MessageHandlerImpl">
<property name="messageCheckerList" ref="checkerList"/>
</bean>
<bean id="checkerList" class="java.util.ArrayList">
<constructor-arg>
<list>
<ref bean="HL7Checker"/>
</list>
</constructor-arg>
</bean>
<bean id="HL7Checker" class="com.XXX.messagereceiver.HL7CheckerImpl">
<property name="messageExecutor" ref="kahootzExecutor"/>
</bean>
Here i am passing a checkerlist - which is a list of Implementations ( For now i have only 1) of the Interface (messageChecker)
Checkerlist is containing references to Bean Id's which are actual implementaions.
HL7Checker is an implementation of an Interface messageChecker.
But when i run the main program, When i inject the bean "messageHandler" and call the getMessageCheckerList, It returns a null value. These getter and setter methods are working fine without using spring.
I am not sure what seems to be the problem.
I don't know the answer for you troubles, but I would check:
is the setter setMessageCheckerList(List) in messageHandler bean called? (either using some debugger or some trace output like System.out...). If it's not, there's probably something wrong with your Spring XML configuration setup. The bean definition you posted requires the property to be set and Spring wouldn't create the messageHandler bean without setting the property.
who calls the setMessageCheckerList(List) setter? Or even more precise, what code writes to the field which stores the value of the property? Maybe the field is initialized properly by Spring but gets overwritten to null later on?
are you sure you call the getMessageCheckerList on the very same object Spring has configured for you (that is, the messageHandler bean). The definition you have posted clearly states an instance of MessageHandlerImpl is created by Spring, but it doesn't prevent other instances to be created in other ways. So maybe the instance created by Spring holds the proper value, but you run the get... on a wrong instance?