I've to manage the association between bean instance and some type of resource. More specifically, when I receive some data associated to Resource A, I have to dispatch the data to the create an instance of some bean type every time I receive data associated with a specified resource. For example, if I receive some data associated with Resource "a", then i have to dispatch this data to a Bean instance associated with "a". If there isn't a Bean instance associated to Resource "a", then it has to be created. The number of Resource isn't statically known, and it will be known only during execution.
I understand I need some type of dictionary to store the association between Resource id and bean instance, building something like a Factory to manage bean creation.
How can I implement this in Spring? What's the proper way?
the Spring ApplicationContext can be referred to in this 'dictionary' fashion. that is, you specify to the application context to 'setup' the 'resource beans'. you then map the 'received data' via way of a Map object, that, in turn, holds a reference to the bean. once the data maps to the bean, you can then retrieve the bean and process the received data. here's a brief example
here's a resource service interface
package de.incompleteco.spring.service;
public interface ResourceService {
public void processData(Object data);
}
here's an implementation to process 'TypeA' data
package de.incompleteco.spring.service;
public class TypeAResourceService implements ResourceService {
public void processData(Object data) {
//do something for 'type A' data
}
}
here's a 'delegate' service
package de.incompleteco.spring.service;
import java.util.Map;
import de.incompleteco.spring.domain.TypeA;
import de.incompleteco.spring.domain.TypeB;
public class DelegateResourceService implements ResourceService {
private Map<String,ResourceService> services;
public void processData(Object data) {
if (data instanceof TypeA) {
services.get(TypeA.class.getSimpleName()).processData(data);
} else if (data instanceof TypeB) {
services.get(TypeA.class.getSimpleName()).processData(data);
} else {
throw new IllegalArgumentException("...");
}
}
public void setServices(Map<String, ResourceService> services) {
this.services = services;
}
}
the 'delegate' service is responsible for retrieving the right service from a map to process the right data (keyed by Class Name)
here's the spring configuration to support
<?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.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<!-- type 'a' bean -->
<bean id="typeA" class="de.incompleteco.spring.service.TypeAResourceService"/>
<!-- type 'b' bean -->
<bean id="typeB" class="de.incompleteco.spring.service.TypeBResourceService"/>
<util:map id="dictionary">
<entry key="TypeA" value-ref="typeA"/>
<entry key="TypeB" value-ref="typeB"/>
</util:map>
<bean class="de.incompleteco.spring.service.DelegateResourceService">
<property name="services" ref="dictionary"/>
</bean>
</beans>
this configuration does the following;
sets up services for type A and B data
maps those services to keys
sets up the delegate service, setting the map to be used
this pattern is not the only way of doing it, but gives you the following;
ability to configure the service beans independantly
the delegate only needs a map to process
the constraint is that the service objects have to be of the same interface
you can think of the Spring ApplicationContext as a giant factory, but (in many ways) a whole lot simpler to setup.
Related
In some cases, we need to write to database in a Spring -application within an ApplicationListener, so we need transactions within the listener using #Transactional-annotation. These listeners are extended from an abstract baseclass, so normal ScopedProxyMode.INTERFACES won't do, as Spring container complains about expecting a bean of the abstract class-type, not "[$Proxy123]". However, using Scope(proxyMode=ScopedProxyMode.TARGET_CLASS), the listener receives the same event twice. We are using Spring version 3.1.3.RELEASE. (Edit: Still occurring with version 3.2.4.RELEASE)
Digging into Spring source with debugger, I found out that org.springframework.context.event.AbstractApplicationEventMulticaster.getApplicationListeners returns a LinkedList that contains the same listener twice (same instance: [com.example.TestEventListenerImpl#3aa6d0a4, com.example.TestEventListenerImpl#3aa6d0a4]), if the listener is a ScopedProxyMode.TARGET_CLASS.
Now, I can work around this by placing the code handling database write into a separate class and putting the #Transactional there, but my question is, is this a bug in Spring or expected behavior? Are there any other workarounds so we wouldn't need to create separate service-classes (ie. handle the transaction in the listener, but don't get the same event twice) for even the simplest cases?
Below is a smallish example showing the problem.
With #Scope(proxyMode=ScopedProxyMode.TARGET_CLASS) in TestEventListenerImpl, the output is as follows:
Event com.example.TestEvent[source=Main] created by Main
Got event com.example.TestEvent[source=Main]
Got event com.example.TestEvent[source=Main]
With #Scope(proxyMode=ScopedProxyMode.TARGET_CLASS) removed from TestEventListenerImpl, the output is:
Event com.example.TestEvent[source=Main] created by Main
Got event com.example.TestEvent[source=Main]
So it seems that TARGET_CLASS -scoped beans get inserted twice into the listener list.
Example:
applicationContext.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"
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">
<context:component-scan base-package="com.example/**"/>
</beans>
com.example.TestEvent
public class TestEvent extends ApplicationEvent
{
public TestEvent(Object source)
{
super(source);
System.out.println("Event " + this + " created by " + source);
}
}
com.example.TestEventListener
public interface TestEventListener extends ApplicationListener<TestEvent>
{
#Override
public void onApplicationEvent(TestEvent event);
}
com.example.TestEventListenerImpl
#Component
#Scope(proxyMode=ScopedProxyMode.TARGET_CLASS) //If commented out, the event won't be received twice
public class TestEventListenerImpl implements TestEventListener
{
#Override
public void onApplicationEvent(TestEvent event)
{
System.out.println("Got event " + event);
}
}
com.example.ListenerTest
public class ListenerTest
{
public static void main(String[] args)
{
ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
SimpleApplicationEventMulticaster eventMulticaster = appContext.getBean(SimpleApplicationEventMulticaster.class);
//This is also needed for the bug to reproduce
TestEventListener listener = appContext.getBean(TestEventListener.class);
eventMulticaster.multicastEvent(new TestEvent("Main"));
}
}
I can't speak to if this is a bug or expected behavior, but here's the dirty:
Declaring a bean like
#Component
#Scope(proxyMode=ScopedProxyMode.TARGET_CLASS) //If commented out, the event won't be received twice
public class TestEventListenerImpl implements TestEventListener
{
Creates two BeanDefinition instances:
A RootBeanDefinition describing the Scoped bean.
A ScannedGenericBeanDefinition describing the actual object.
The ApplicationContext will use these bean definitions to create two beans:
A ScopedProxyFactoryBean bean. This is a FactoryBean that wraps the TestEventListenerImpl object in a proxy.
A TestEventListenerImpl bean. The actual TestEventListenerImpl object.
Part of the initialization process is to register beans that implement the ApplicationListener interface. The TestEventListenerImpl bean is created eagerly (right away) and registered as an ApplicationListener.
The ScopedProxyFactoryBean is lazy, the bean (proxy) it's supposed to create is only generated when requested. When that happens, it also gets registered as an ApplicationListener. You only see this when you explicitly request it
TestEventListener listener = appContext.getBean(TestEventListener.class);
Or implicitly by using #Autowired to inject it into another bean. Note that the actual target object is added, not the proxy.
I'm trying to grasp Spring's FactoryBean and I've had and issue. Could you please see my sources below and answer. It's my app context:
<?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-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<context:annotation-config/>
<bean id="SHADigest" class="com.dtoryanik.spring.factorybean.MessageDigestFactoryBean">
<property name="algorithmName">
<value>SHA1</value>
</property>
</bean>
<bean id="defaultDigest" class="com.dtoryanik.spring.factorybean.MessageDigestFactoryBean"/>
<bean id="digester" class="com.dtoryanik.spring.factorybean.MessageDigester">
<property name="messageDigest1">
<ref local="SHADigest"/>
</property>
<property name="messageDigest2">
<ref local="defaultDigest"/>
</property>
</bean>
</beans>
It's a factory bean actually:
public class MessageDigestFactoryBean implements FactoryBean<MessageDigest>{
private String algorithmName = "MD5";
private MessageDigest messageDigest = null;
#Override
public MessageDigest getObject() throws Exception {
System.out.println("<> MessageDigestFactoryBean.getObject()");
return messageDigest;
}
#Override
public Class<?> getObjectType() {
System.out.println("<> MessageDigestFactoryBean.getObjectType()");
return MessageDigest.class;
}
#Override
public boolean isSingleton() {
System.out.println("<> MessageDigestFactoryBean.isSingleton()");
return true;
}
#PostConstruct
public void postConstructHandler() throws NoSuchAlgorithmException {
System.out.println("<> MessageDigestFactoryBean.postConstructHandler()");
messageDigest = MessageDigest.getInstance(algorithmName);
}
public void setAlgorithmName(String algorithmName) {
this.algorithmName = algorithmName;
}
}
There is another class - MessageDigester but it does not do anything helpful for topic. And I have a main-method class:
public class MessageDigestDemo {
public static void main(String[] args) {
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
ctx.load("classpath:app-ctx.xml");
ctx.refresh();
MessageDigester messageDigester = (MessageDigester) ctx.getBean("digester");
messageDigester.digest("Hello World!");
}
}
The issue is in my output. It seems I have a double instantiating. Methods isSingleton(), getObject() are called two times for each bean (although I retrieve only 2 instances from factory). Why does it occur? Maybe I do something wrong?
<> MessageDigestFactoryBean.postConstructHandler()
<> MessageDigestFactoryBean.postConstructHandler()
<> MessageDigestFactoryBean.isSingleton()
<> MessageDigestFactoryBean.getObject()
<> MessageDigestFactoryBean.isSingleton()
<> MessageDigestFactoryBean.getObject()
<> MessageDigestFactoryBean.isSingleton()
<> MessageDigestFactoryBean.getObjectType()
<> MessageDigestFactoryBean.isSingleton()
<> MessageDigestFactoryBean.getObjectType()
I've modified your MessageDigestFactoryBean class so that it outputs algorithmName as well, this will help to clear out the case. After that change, the output is:
<> MessageDigestFactoryBean.postConstructHandler() SHA1
<> MessageDigestFactoryBean.postConstructHandler() MD5
<> MessageDigestFactoryBean.isSingleton() SHA1
<> MessageDigestFactoryBean.getObject() SHA1
<> MessageDigestFactoryBean.isSingleton() MD5
<> MessageDigestFactoryBean.getObject() MD5
<> MessageDigestFactoryBean.isSingleton() SHA1
<> MessageDigestFactoryBean.getObjectType() SHA1
<> MessageDigestFactoryBean.isSingleton() MD5
<> MessageDigestFactoryBean.getObjectType() MD5
Let's try to analyze this output.
You have declared two MessageDigestFactoryBean instances, so when Spring discovers them in context, it initializes them, and in process calls the method annotated with #PostConstruct - MessageDigestFactoryBean.postConstructHandler().
Then as Spring discovers digester bean, it tries to obtain it's dependencies. Spring sees that dependency is FactoryBean, so it eventually calls FactoryBeanRegistrySupport.getObjectFromFactoryBean. This method first checks if the bean is singleton, calling MessageDigestFactoryBean.isSingleton(). If the bean is singleton, it first tries to obtain reference to it from factory bean objects cache. Since this is the first time this bean is referenced, it's not yet cached, so reference is obtained via MessageDigestFactoryBean.getObject(), cached and then returned. Since you have two factory beans referenced in digester, obviously this process is repeated for each one.
After initialization is complete, Spring tries to publish ContextRefreshedEvent to the lifecycle processor, so it searches all bean definitions for an instance that implements Lifecycle interface. Basically Spring loops through all beans defined in context, checking for singleton bean of matching type (actually there are more checks, but we're interested only in these). In process of this, for factory bean MessageDigestFactoryBean.isSingleton() is called to determine whether object is singleton, and MessageDigestFactoryBean.getObjectType() is called to check whether object's type is assignable from Lifecycle interface. Again, since you have two instances of MessageDigestFactoryBean, each of these methods is called twice.
That's what happens when you call ctx.refresh(). This is just over the top look and obviously a lot more is done by Spring under the hood, but that's all I can see related to your output. Hope this answers your first question.
Now for the second question - no, you did not do anything wrong, the output you see just reflects how Spring functions internally.
Question: How can I properly create class reflection, if class constructor contains heir class argument?
I have:
interface Car {}
class SportCar implement Car{}
class CarService {
public CarService(String str, Car car) {
...
}
}
But, when I try to do:
Class c = Class.forName("vbspring.model.CarService");
Class[] paramTypes = {String.class, SportCar.class};
Constructor constr = c.getDeclaredConstructor(paramTypes);
it throws: java.lang.NoSuchMethodException: vbspring.model.CarService.(java.lang.String, vbspring.model.SportCar)
P.S. My .xml file:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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-3.0.xsd">
<bean id="carService" class="model.CarService">
<constructor-arg value="Car Rental Service"/>
<constructor-arg ref="sportCar"/>
<property name="startCar"/>
</bean>
<bean id="sportCar" class="model.SportCar"/>
</beans>
EDIT: I try to write something like my own Spring Framework. I must create not only classes that belongs to Car hierarchy, so I try to write universal method that can create class of arbitrary type.
I can't write: Class[] paramTypes = {String.class, Car.class};
I must write something universal, like:
paramTypes[index++] = obj.getClass();
where obj - is SportCar or Car bean pulled from .xml by parser
The errors java.lang.NoSuchMethodException: vbspring.model.CarService.(java.lang.String, vbspring.model.SportCar) states you have no constructor like:
public CarService(String, SportCar)
which you don't. You have:
public CarService(String, Car) {
Change your code to
Class c = Class.forName("vbspring.model.CarService");
Class[] paramTypes = {String.class, Car.class}; // the constructor takes Car not SportsCar
Constructor constr = c.getDeclaredConstructor(paramTypes);
Then when you call the constructor to get a new instance, you can pass a SportsCar instance or any other Car instance for that matter.
constr.newInstance("sport", new SportsCar());
// or
constr.newInstance("a jeep", new Jeep());
// or
constr.newInstance("long car", new Limousine());
assuming Jeep and Limousine implement Car.
I have code which looks like the following:
MyContext context = new MyContext();
context.start();
MyEntity entity = context.getEntity();
I want to inject the MyEntity instance into various classes.
But I don't know how to setup my Spring configuration, where I first create an object, then call a method on it and then finally call another method which returns the entity I want to inject.
EDIT 2 - removed the Strings altogether
The most common type of dependencies injected using Spring don't depend on the user input for their construction. This includes data access objects, services etc.,
You are talking about injecting domain objects whose construction depends on the user input either directly or indirectly.
Spring provides #Configurable annotation to inject such domain objects that are created using new operator. You can search for "#Configurable Domain Driven Design" on the internet to get examples of how this can be implemented. I myself used it in one my applications and wrote a simple post here that might help you get started.
Edit:
To create a bean of type MyEntity as per the specification in your updated question, you would need to
define a bean of type MyContext
Create a MyEntityFactory class that would depend on the MyContext bean.
The factory method would take the MyContext bean as argument, calls context.start() on it and returns an instance of MyEntity.
You would define the MyEntity bean using this factory class.
The MyEntityFactory class would be as follows:
public class MyEntityFactory
{
public static MyEntity getMyEntity(MyContext context)
{
context.start();
return context.getEntity();
}
}
The spring bean configuration will be as follows:
<bean id="myContext" class="FQCN.Of.MyContext" />
<bean id="myEntity" class="FQCN.Of.MyEntityFactory" factory-method="getMyEntity">
<constructor-arg ref="myContext" />
</bean>
Since MyEntity is a singleton bean, the factory method will be called only once, btw.
More on creating beans using factory methods here.
I have an application running with Spring, and I'm using AOP in some places. Since I want to use the #Transactional annotation at interface level, I have to allow Spring to create JDK proxies. So, I don't set the proxy-target-class property to true. On the other hand, I don't want to create an interface for every single class I want advised: if the interface just doesn't make sense, I want to have just the implementation, and Spring should create a CGLIB proxy.
Everything was working perfectly, just as I described. But I wanted to have some other annotations (created by me) going in interfaces and being "inherited" by the implementation classes (just like the #Transactional one). Turns out that I can't do that with the built-in support for AOP in Spring (at least I could not figure it out how to do it after some research. The annotation in the interface is not visible in the implementation class, and hence that class does not get advised).
So I decided to implement my own pointcut and interceptor, allowing other method annotations to go on interfaces. Basically, my pointcut look for the annotation on the method and, until not found, in the same method (same name and parameter types) of the interfaces that the class or its superclasses implements.
The problem is: when I declare a DefaultAdvisorAutoProxyCreator bean, that will properly apply this pointcut/interceptor, the behavior of advising classes with no interfaces is broken. Apparently something goes wrong and Spring tries to proxy my classes twice, once with CGLIB and then with JDK.
This is my configuration file:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:task="http://www.springframework.org/schema/task"
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/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd">
<!-- Activates various annotations to be detected in bean classes: Spring's
#Required and #Autowired, as well as JSR 250's #Resource. -->
<context:annotation-config />
<context:component-scan base-package="mypackage" />
<!-- Instruct Spring to perform declarative transaction management automatically
on annotated classes. -->
<tx:annotation-driven transaction-manager="transactionManager" />
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" />
<bean id="logger.advisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<constructor-arg>
<bean class="mypackage.MethodAnnotationPointcut">
<constructor-arg value="mypackage.Trace" />
</bean>
</constructor-arg>
<constructor-arg>
<bean class="mypackage.TraceInterceptor" />
</constructor-arg>
</bean>
</beans>
This is the class I want to be proxied, with no interfaces:
#Component
public class ServiceExecutorImpl
{
#Transactional
public Object execute(...)
{
...
}
}
When I try to autowire it in some other bean, like:
public class BaseService {
#Autowired
private ServiceExecutorImpl serviceExecutorImpl;
...
}
I get the following exception:
java.lang.IllegalArgumentException: Can not set mypackage.ServiceExecutorImpl field mypackage.BaseService.serviceExecutor to $Proxy26
This are some lines of the Spring output:
13:51:12,672 [main] DEBUG [org.springframework.aop.framework.Cglib2AopProxy] - Creating CGLIB2 proxy: target source is SingletonTargetSource for target object [mypackage.ServiceExecutorImpl#1eb515]
...
13:51:12,782 [main] DEBUG [org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator] - Creating implicit proxy for bean 'serviceExecutorImpl' with 0 common interceptors and 1 specific interceptors
13:51:12,783 [main] DEBUG [org.springframework.aop.framework.JdkDynamicAopProxy] - Creating JDK dynamic proxy: target source is SingletonTargetSource for target object [mypackage.ServiceExecutorImpl$$EnhancerByCGLIB$$2eb5f51#5f31b0]
I could supply the full output if someone thinks it will help. I have no idea why Spring is trying to "double-proxy" my class, and why this just happens when I declare the DefaultAdvisorAutoProxyCreator bean.
I have been struggling with this for some time now, and any help or ideas would be very much appreciated.
EDIT:
This is my interceptor source code, as requested. It basically log the method execution (only methods annotated with #Trace get intercepted). If the method is annotated with #Trace(false), the logging is suspended until the method returns.
public class TraceInterceptor
implements
MethodInterceptor
{
#Override
public Object invoke(
MethodInvocation invocation )
throws Throwable
{
if( ThreadExecutionContext.getCurrentContext().isLogSuspended() ) {
return invocation.proceed();
}
Method method = AopUtils.getMostSpecificMethod( invocation.getMethod(),
invocation.getThis().getClass() );
Trace traceAnnotation = method.getAnnotation( Trace.class );
if( traceAnnotation != null && traceAnnotation.value() == false ) {
ThreadExecutionContext.getCurrentContext().suspendLogging();
Object result = invocation.proceed();
ThreadExecutionContext.getCurrentContext().resumeLogging();
return result;
}
ThreadExecutionContext.startNestedLevel();
SimpleDateFormat dateFormat = new SimpleDateFormat( "dd/MM/yyyy - HH:mm:ss.SSS" );
Logger.log( "Timestamp: " + dateFormat.format( new Date() ) );
String toString = invocation.getThis().toString();
Logger.log( "Class: " + toString.substring( 0, toString.lastIndexOf( '#' ) ) );
Logger.log( "Method: " + getMethodName( method ) );
Logger.log( "Parameters: " );
for( Object arg : invocation.getArguments() ) {
Logger.log( arg );
}
long before = System.currentTimeMillis();
try {
Object result = invocation.proceed();
Logger.log( "Return: " );
Logger.log( result );
return result;
} finally {
long after = System.currentTimeMillis();
Logger.log( "Total execution time (ms): " + ( after - before ) );
ThreadExecutionContext.endNestedLevel();
}
}
// Just formats a method name, with parameter and return types
private String getMethodName(
Method method )
{
StringBuffer methodName = new StringBuffer( method.getReturnType().getSimpleName() + " "
+ method.getName() + "(" );
Class<?>[] parameterTypes = method.getParameterTypes();
if( parameterTypes.length == 0 ) {
methodName.append( ")" );
} else {
int index;
for( index = 0; index < ( parameterTypes.length - 1 ); index++ ) {
methodName.append( parameterTypes[ index ].getSimpleName() + ", " );
}
methodName.append( parameterTypes[ index ].getSimpleName() + ")" );
}
return methodName.toString();
}
}
Thanks!
I found a solution using the 'scoped-proxy' suggested by Bozho.
Since I'm using almost only annotations, my ServiceExecutor class now looks like this:
#Component
#Scope( proxyMode = ScopedProxyMode.TARGET_CLASS )
public class ServiceExecutor
{
#Transactional
public Object execute(...)
{
...
}
}
Until now everything seens to be working fine. I don't know why I have to explicitly tell Spring this class should be proxied using CGLIB, since it does not implement any interface. Maybe it's a bug, I don't know.
Thanks a lot, Bozho.
Something doesn't match here - if ther is a $ProxyXX, it means there is an interface. Make sure there is no interface. Some other notes:
in your pointcut you can check if the target object is already a proxy using (x instanceof Advised), then you can cast to Advised
you can use <aop:scoped-proxy /> to define the proxy strategy per-bean