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:
If we run the application we get the amazing output at the stdout:
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:
The config file is changed as follows:
So, we expect the application to print additional aop message during execution. Let's test that:
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:
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.