Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Aspect-Oriented Programming with Annotations

Tags:

java

aop

In a post entitled "AOP Fundamentals", I asked for a King's English explanation of what AOP is, and what it does. I received some very helpful answers and links to articles that helped fill me in on all the theory.

But now AOP's got my full attention, and all these articles and chapter excerpts are fantastic, but in every single case they consist of lofty theory, vague UML models, and order of abstraction that are way too high-up for my liking.

Here is my understanding of AOP theory, just to clarify, so if you see something that looks wrong, let me know!:

  1. Cross-cutting concerns such as Logging, Authenticating, Synchronizing, Validating, Exception Handling, etc. become highly-coupled in non-AOP systems as they are used universally by almost every component/module in the codebase.

  2. AOP defines aspects (classes/methods) that abstract these cross-cutting concerns with the use of join points, advice, and pointcuts.

    a. Advice - The actual code (method of an aspect, perhaps?) implementing the cross-cutting concern (i.e. doing the actual logging, validating, authenticating, etc.)

    b. Join Point - An event that is triggered in non-AOP code that causes a particular aspect's advice to be executed ("woven" into the non-AOP code)

    c. Pointcut - Essentially, a mapping of join points (triggering events) to advice execution

  3. All aspects are modularized (LoggingAspect, AuthenticationAspect, ValidationAspect, etc.) into components and registered with an AspectWeaver. When non-AOP/POJO code comes across a join point, AspectWeaver "weaves" (integrates) the mapped advice around the non-AOP code:

 public class LoggingAspect {     // ...      public void log(String msg) { ... } }  public class ExceptionHandlingAspect {     // ..      public void handle(Exception exc) { ... } }  public class NonAOPCode {     // ...      @LoggingAspect @ExceptionHandlingAspect     public void foo()     {         // do some stuff...     } }  // Now in the driver public static int main void(String[] args) {     NonAOPCode nonAOP = new NonAOPCode();     nonAOP.foo(); }  // The AspectWeaver *magically* might weave in method calls so main now becomes: {     NonAOPCode nonAOP = new NonAOPCode();      log(someMsg);     nonAOP.foo();     handle(someExc); }  

The $64,000 Question: Is my understanding of Java-based AOP on target, or way off, and why? How could one correctly use annotations to implement aspects, advice, join points, pointcuts and this so-called aspect weaver?

like image 216
Eugie Avatar asked Jan 28 '11 14:01

Eugie


People also ask

Which is the annotation for aspect style?

@AspectJ refers to a style of declaring aspects as regular Java classes annotated with Java 5 annotations. The @AspectJ style was introduced by the AspectJ project as part of the AspectJ 5 release.

What is the use of @aspect annotation in spring?

@After declares the after advice. It is applied after calling the actual method and before returning result. @AfterReturning declares the after returning advice. It is applied after calling the actual method and before returning result.

What is pointcut and JoinPoint in spring?

JoinPoint: Joinpoint are points in your program execution where flow of execution got changed like Exception catching, Calling other method. PointCut: PointCut are basically those Joinpoints where you can put your advice(or call aspect). So basically PointCuts are the subset of JoinPoints.


1 Answers

Let's imagine you want to log the time taken by some annoted methods using a @LogExecTime annotation.

I first create an annotation LogExecTime:

@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface LogExecTime {  } 

Then I define an aspect:

@Component  // For Spring AOP @Aspect public class LogTimeAspect {     @Around(value = "@annotation(annotation)")     public Object LogExecutionTime(final ProceedingJoinPoint joinPoint, final LogExecTime annotation) throws Throwable {         final long startMillis = System.currentTimeMillis();         try {             System.out.println("Starting timed operation");             final Object retVal = joinPoint.proceed();             return retVal;         } finally {             final long duration = System.currentTimeMillis() - startMillis;             System.out.println("Call to " + joinPoint.getSignature() + " took " + duration + " ms");         }      } } 

I create a class annoted with LogExecTime:

@Component public class Operator {      @LogExecTime     public void operate() throws InterruptedException {         System.out.println("Performing operation");         Thread.sleep(1000);     } } 

And a main using Spring AOP:

public class SpringMain {      public static void main(String[] args) throws InterruptedException {         ApplicationContext context = new GenericXmlApplicationContext("applicationContext.xml");         final Operator bean = context.getBean(Operator.class);         bean.operate();     } } 

If I run this class I'm getting the following output on stdout:

Starting timed operation Performing operation Call to void testaop.Operator.Operate() took 1044 ms 

Now with the magic. As I did use Spring AOP rather than AspectJ weaver, the magic is occurring at run time using proxy-ish mechanisms. So the .class files are left untouched. For instance if I debug this program and put a breakpoint in operate you'll see how Spring has performed the magic:

Debug screen shot

As Spring AOP implementation is non-intrusive and uses the Spring mechanisms you need to add the @Component annotation and create the object using Spring context rather than plain new.

AspectJ on the other side will change the .class files. I tried this project with AspectJ and decompiled the Operator class with jad. Which lead to:

public void operate()     throws InterruptedException {     JoinPoint joinpoint = Factory.makeJP(ajc$tjp_0, this, this);     operate_aroundBody1$advice(this, joinpoint, LogTimeAspect.aspectOf(), (ProceedingJoinPoint)joinpoint, (LogExecTime)(ajc$anno$0 == null && (ajc$anno$0 = testaop/Operator.getDeclaredMethod("operate", new Class[0]).getAnnotation(testaop/LogExecTime)) == null ? ajc$anno$0 : ajc$anno$0)); }  private static final void operate_aroundBody0(Operator ajc$this, JoinPoint joinpoint) {     System.out.println("Performing operation");     Thread.sleep(1000L); }  private static final Object operate_aroundBody1$advice(Operator ajc$this, JoinPoint thisJoinPoint, LogTimeAspect ajc$aspectInstance, ProceedingJoinPoint joinPoint, LogExecTime annotation) {     long startMillis = System.currentTimeMillis();     Object obj;     System.out.println("Starting timed operation");     ProceedingJoinPoint proceedingjoinpoint = joinPoint;     operate_aroundBody0(ajc$this, proceedingjoinpoint);     Object retVal = null;     obj = retVal;     long duration = System.currentTimeMillis() - startMillis;     System.out.println((new StringBuilder("Call to ")).append(joinPoint.getSignature()).append(" took ").append(duration).append(" ms").toString());     return obj;     Exception exception;     exception;     long duration = System.currentTimeMillis() - startMillis;     System.out.println((new StringBuilder("Call to ")).append(joinPoint.getSignature()).append(" took ").append(duration).append(" ms").toString());     throw exception; }  private static void ajc$preClinit() {     Factory factory = new Factory("Operator.java", testaop/Operator);     ajc$tjp_0 = factory.makeSJP("method-execution", factory.makeMethodSig("1", "operate", "testaop.Operator", "", "", "java.lang.InterruptedException", "void"), 5); }  private static final org.aspectj.lang.JoinPoint.StaticPart ajc$tjp_0; /* synthetic field */ private static Annotation ajc$anno$0; /* synthetic field */  static  {     ajc$preClinit(); } 
like image 144
gabuzo Avatar answered Oct 24 '22 21:10

gabuzo