There are two most popular problems there imho. Here is the second one:
suppose you developed spring-based application that works like a charm and makes your customers happy. Let's define such a simple but pleasant application:
TestTask.java
package com.spring.aop;
import org.springframework.stereotype.Component;
@Component
public class TestTask implements Runnable{
public void run() {
System.out.println("The task is running");
}
}
TestClass.java
package com.spring.aop;
import org.springframework.stereotype.Component;
import org.springframework.beans.factory.annotation.Autowired;
@Component
public class TestClass {
private Runnable task;
@PostConstruct
public void start() {
task.run();
}
public Runnable getTask() {
return task;
}
@Autowired
public void setTask(Runnable task) {
this.task = task;
}
}
spring-config.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-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:component-scan base-package="com.spring.aop"/>
</beans>
SpringStart.java
package com.spring;
import com.spring.aop.TestClass;
import com.spring.aop.TestTask;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringStart {
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
TestClass testClass = (TestClass)context.getBean("testClass");
TestTask testTask = (TestTask)context.getBean("testTask");
System.out.printf("Test %s%n", testClass.getTask() == testTask ? "passed" : "failed");
}
}
If we run the application we get the amazing output at the stdout:
The task is running
Test passed
One day we decided to introduce aspects to the application. The may be multiple reasons for that like:
- aspects can relief my job;
- everybody talks about aspects, I should try them out;
- everybody talks about aspects, I want to be trendy and use them;
- I want to get my brain broken trying to understand AOP logic;
Whatever the reason, result is that aspects are introduced to the application. Here is a simple aspect for our simple application that just prints cool aspect message during application execution:
TestAspect.java
package com.spring.aop;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class TestAspect {
@Before(value = "execution(* Runnable.*(..))")
public void advice() {
System.out.println("Cool aspect message");
}
}
The config file is changed as follows:
spring-config.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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:component-scan base-package="com.spring.aop"/>
<aop:aspectj-autoproxy/>
</beans>
So, we expect the application to print additional aop message during execution. Let's test that:
Cool aspect message
The task is running
Exception in thread "main" java.lang.ClassCastException: $Proxy8
at com.spring.SpringStart.main(SpringStart.java:11)
Ok, so we got not only cool aspect message but ugly ClassCastException as well (also note that AspectJ binaries are required to remain at the application classpath).
The reason of the problem is that we used Spring AOP here. Many new spring developers forget that Spring AOP is proxy-based, i.e. its main idea is to return aspect-aware bean proxy instead of the bean itself from the context. So, the user thinks that he or she works with the bean itself and calls bean methods. However, the methods are invoked on a proxy object, hence, the proxy may execute aspect logic before/after delegating the call to the target bean.
That's all is fine but how does proxy-based AOP relate to the ClassCastException? The answer is that spring framework offers two proxying mechanism - JDK proxy-based and CGLIB-based. The first one can create proxies only for the interfaces; the second one is able to proxy concrete classes with particular limitations (feel free to check spring reference to get more about that - 6.6. Proxying mechanisms). The same reference chapter says the following about proxying mechanism selection:
If the target object to be proxied implements at least one interface then a JDK dynamic proxy will be used. All of the interfaces implemented by the target type will be proxied. If the target object does not implement any interfaces then a CGLIB proxy will be created.
Our TestTask class implements Runnable interface, hence, jdk proxies are used. That means that the proxy can be safely cast only to the Object and Runnable. When we try to cast it to the TestTask (TestTask testTask = (TestTask)context.getBean("testTask");), ClassCastException is thrown.
Quick solution here is to use CGLIB proxies. The only config change is to add additional attribute:
<aop:aspectj-autoproxy proxy-target-class="true"/>
If we run the application this time everything is fine (note that we need to put cglib binaries to the application classpath now).
I'm going to talk about more popular Spring AOP problem - self calls. After that I'll post a little article that describes how to use AspectJ weaving with spring and shows that it eliminates the mentioned problems.

Thanks
ReplyDeleteWelcome
ReplyDeleteThanks a lot..It helped....
ReplyDeleteMe too...
ReplyDeleteThanks. That's the kind of info I was looking for...
ReplyDeleteThanks, very useful ..
ReplyDeleteAwesome!!!
ReplyDeleteThanks your information helped a lot.
ReplyDelete