Pitfalls with aspect technologies
Even though aspect technologies are very powerful. Especially to address cross-cutting concerns like transactions, it shouldn’t be used everywhere it’s possible.
The first part of this post consists of a simple example that uses AspectJ to return an unexpected value. The last part is about where aspects fits and where you should consider to avoid it.
A quote from the Spider-Man movie:
With great power comes great responsibility
This applies to some degree to aspect technologies too. Since it can change the behaviour of an application at compilation, startup or at runtime. Even without leaving a trace in the source code.
An aspect that changes the return value of String’s toLowerCase()
To demonstrate an unexpected result from a method, I have created a StringAspect with AspectJ that returns the opposite of what the method name says.
When some code calls on String‘s toLowerCase() method, the StringAspect below reroutes the call to String‘s toUpperCase() method instead:
@Aspect public class StringAspect { @Pointcut("call(* String.toLowerCase())") public void toLowerCasePointcut() {} @Around("toLowerCasePointcut()") public String toLowerCaseAroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable { // String text = (String) joinPoint.proceed(); String text = ((String) joinPoint.getTarget()).toUpperCase(); return text; } }
To get the expected result, you can remove the comment on the line with joinPoint.proceed() and remove the line below. Eventually remove the AspectJ compiler or the JVM agent to avoid weaving in the advice.
The test class that asserts that the String‘s toLowerCase() method works as expected:
public class StringTest { @Test public void testAroundAdvice() { String demoText = "tHiS iS a TeSt"; assertEquals("this is a test", demoText.toLowerCase()); } }
Without AspectJ weaving, the test finishes successfully, but with weaving the result is:
org.junit.ComparisonFailure: expected:<[this is a test]> but was:<[THIS IS A TEST]>
Best practice from enterprise frameworks
Features like transaction handling, security and concurrency support are good candidates to be done with aspects. Spring, Guice and EJB implementations all use aspects to provide such enterprise capabilities without cluttering the application’s source code with infrastructure logic.
You can see this post for a good example. Best practice with Java 5 and later is to add an annotation on methods that will be intercepted.
Annotations that says this method should be executed asynchronously in a transaction:
@Async @Transactional public void demoMethod() { // logic .. }
These annotations are excellent pointcuts for enterprise frameworks.
Cases where aspects should be avoided
To change the target method like the StringAspect above should be avoided. You can replace an instance with a mock/stub object with AspectJ. In situations where the called instance is an object of a final class that doesn’t implement any interfaces, then an aspect is your only choice. It mocks away the dependency, but refactoring the code and use a mocking framework should be the preferred choice. It makes the code much more readable.
Especially without a tool like AJDT that visualizes the joinpoints in the source code. Here’s an example with the AJDT Eclipse plugin.
You should be really careful about adding or changing any data inside an advice. Such modifications makes the code almost impossible to read and the chance for bugs are higher.
Else, if many of the developers in a team isn’t familiar with aspects, you should also limit the use since it adds “magic” to the application that only a few developers on the team can understand.
Hi, very useful topic. I have a question, if you can help; I use spring and aspectj jars, when I run your code it gives me
org.aspectj.weaver.tools.UnsupportedPointcutPrimitiveException: Pointcut expression call(* String.toLowerCase()) contains unsupported pointcut primitive ‘call’
error. What am I doing wrong?
asyardrd
June 29, 2011 at 11:47
Thanks!
I can’t see the error from your little code snippet. You should post a question on stackoverflow.com.
Include the aspect class and tag it with aspectj and you should get an answer quite soon.
Espen
June 29, 2011 at 17:07
The question has been answered on stackoverflow.com here.
Espen
June 29, 2011 at 19:19
Very thoughtful bblog
Jery
January 17, 2023 at 03:31