Difference between @target and @within (Spring AOP)

前端 未结 2 429
情书的邮戳
情书的邮戳 2021-02-08 19:48

Spring manual says:

any join point (method execution only in Spring AOP) where the target object has an @Transactional annotation: @target(org.spring

2条回答
  •  执念已碎
    2021-02-08 20:29

    You are noticing no difference because Spring AOP, while using AspectJ syntax, actually only emulates a limited subset of its functionality. Because Spring AOP is based on dynamic proxies, it only provides interception of public, non-static method execution. (When using CGLIB proxies, you can also intercept package-scoped and protected methods.) AspectJ however can also intercept method calls (not just executions), member field access (both static and non-static), constructor call/execution, static class initialisation and a few more.

    So let us construct a very simple AspectJ sample:

    Marker annotation:

    package de.scrum_master.app;
    
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyAnnotation {}
    

    Driver application:

    package de.scrum_master.app;
    
    @MyAnnotation
    public class Application {
      private int nonStaticMember;
      private static int staticMember;
    
      public void doSomething() {
        System.out.println("Doing something");
        nonStaticMember = 11;
      }
    
      public void doSomethingElse() {
        System.out.println("Doing something else");
        staticMember = 22;
      }
    
      public static void main(String[] args) {
        Application application = new Application();
        application.doSomething();
        application.doSomethingElse();
      }
    }
    

    Aspect:

    package de.scrum_master.aspect;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    
    @Aspect
    public class MyAspect {
      @Before("@within(de.scrum_master.app.MyAnnotation) && execution(public !static * *(..))")
      public void adviceAtWithin(JoinPoint thisJoinPoint) {
        System.out.println("[@within] " + thisJoinPoint);
      }
    
      @Before("@target(de.scrum_master.app.MyAnnotation) && execution(public !static * *(..))")
      public void adviceAtTarget(JoinPoint thisJoinPoint) {
        System.out.println("[@target] " + thisJoinPoint);
      }
    }
    

    Please note that I am emulating Spring AOP behaviour here by adding && execution(public !static * *(..)) to both pointcuts.

    Console log:

    [@within] execution(void de.scrum_master.app.Application.doSomething())
    [@target] execution(void de.scrum_master.app.Application.doSomething())
    Doing something
    [@within] execution(void de.scrum_master.app.Application.doSomethingElse())
    [@target] execution(void de.scrum_master.app.Application.doSomethingElse())
    Doing something else
    

    No surprise here. This is exactly what you would also see in Spring AOP. Now if you remove the && execution(public !static * *(..)) part from both pointcuts, in Spring AOP the output is still the same, but in AspectJ (e.g. also if you activate AspectJ LTW in Spring) it changes to:

    [@within] staticinitialization(de.scrum_master.app.Application.)
    [@within] execution(void de.scrum_master.app.Application.main(String[]))
    [@within] call(de.scrum_master.app.Application())
    [@within] preinitialization(de.scrum_master.app.Application())
    [@within] initialization(de.scrum_master.app.Application())
    [@target] initialization(de.scrum_master.app.Application())
    [@within] execution(de.scrum_master.app.Application())
    [@target] execution(de.scrum_master.app.Application())
    [@within] call(void de.scrum_master.app.Application.doSomething())
    [@target] call(void de.scrum_master.app.Application.doSomething())
    [@within] execution(void de.scrum_master.app.Application.doSomething())
    [@target] execution(void de.scrum_master.app.Application.doSomething())
    [@within] get(PrintStream java.lang.System.out)
    [@within] call(void java.io.PrintStream.println(String))
    Doing something
    [@within] set(int de.scrum_master.app.Application.nonStaticMember)
    [@target] set(int de.scrum_master.app.Application.nonStaticMember)
    [@within] call(void de.scrum_master.app.Application.doSomethingElse())
    [@target] call(void de.scrum_master.app.Application.doSomethingElse())
    [@within] execution(void de.scrum_master.app.Application.doSomethingElse())
    [@target] execution(void de.scrum_master.app.Application.doSomethingElse())
    [@within] get(PrintStream java.lang.System.out)
    [@within] call(void java.io.PrintStream.println(String))
    Doing something else
    [@within] set(int de.scrum_master.app.Application.staticMember)
    

    When looking at this in detail you see that a lot more @within() joinpoints get intercepted, but also a few more @target() ones, e.g. the call() joinpoints mentioned before, but also set() for non-static fields and object initialization() happening before constructor execution.

    When only looking at @target() we see this:

    [@target] initialization(de.scrum_master.app.Application())
    [@target] execution(de.scrum_master.app.Application())
    [@target] call(void de.scrum_master.app.Application.doSomething())
    [@target] execution(void de.scrum_master.app.Application.doSomething())
    Doing something
    [@target] set(int de.scrum_master.app.Application.nonStaticMember)
    [@target] call(void de.scrum_master.app.Application.doSomethingElse())
    [@target] execution(void de.scrum_master.app.Application.doSomethingElse())
    Doing something else
    

    For each of these aspect output lines we also see a corresponding @within() match. Now let's concentrate on what is not the same, filtering the output for differences:

    [@within] staticinitialization(de.scrum_master.app.Application.)
    [@within] execution(void de.scrum_master.app.Application.main(String[]))
    [@within] call(de.scrum_master.app.Application())
    [@within] preinitialization(de.scrum_master.app.Application())
    [@within] get(PrintStream java.lang.System.out)
    [@within] call(void java.io.PrintStream.println(String))
    Doing something
    [@within] get(PrintStream java.lang.System.out)
    [@within] call(void java.io.PrintStream.println(String))
    Doing something else
    [@within] set(int de.scrum_master.app.Application.staticMember)
    

    Here you see, in oder of appearance

    • static class initialisation,
    • static method execution,
    • constructor call (not execution yet!),
    • constructed object pre-initialisation,
    • member variable access in another class (System.out),
    • calling a method from another class (PrintStream.println(String)),
    • setting a static class member.

    What do all of those pointcuts have in common? There is no target object, because either we are talking about static methods or members, static class initialisation, object pre-initialisation (no this defined yet) or calling/accessing stuff from other classes not bearing the annotation we are targeting here.

    So you see that in AspectJ there are significant differences between the two pointcuts, in Spring AOP they are just not noticeable because of its limitations.

    My advice for you is to use @target() if your intention is to intercept non-static behaviour within a target object instance. It will make switching to AspectJ easier if you ever decide to activate the AspectJ mode in Spring or even port some code to a non-Spring, aspect-enabled application.

提交回复
热议问题