justin
justin

Reputation: 39

what kind of method can be advised by spring aop or what's a spring aop joinpoint's limitation?

I am working with spring aop and i found out that there are 3 situations but I am not quite clear: situation 1: a single class which do not implements or extends any class or interface In this situation, any none-private methods will be the joinpoints

situation 2: a class implements a interface and implements methods In this sutuation, only method declared in interface will be a joinpoint

situation 3: a class extends a super class and override super class's methods In this sutuation, all sub class's methods will not be a joinpoint.

Is that how spring aop is designed?

Here is the code i use:

JdkProxyInterface.java

package com.example.proxytestdemo;

public interface JdkProxyInterface {

    void function(int i);
}

JdkProxy.java

package com.example.proxytestdemo;
import org.springframework.stereotype.Component;

@Component
public class JdkProxy implements JdkProxyInterface {

    @Override
    @TimeIt
    public void function(int i) {
        System.out.println("JdkProxy function");
    }

    @TimeIt
    public void function1(int i) {
        System.out.println("JdkProxy function");
    }

    @TimeIt
    public void function2(int i) {
        System.out.println("JdkProxy function");
    }

}

SubJdkProxy.java

package com.example.proxytestdemo;

import org.springframework.stereotype.Component;

@Component
public class SubJdkProxy extends JdkProxy {

    @TimeIt
    public void functionSubJdkProxy(int i) {
        System.out.println("functionSubJdkProxy");
    }
}

TimeIt.java

package com.example.proxytestdemo;
import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
@Inherited
public @interface TimeIt {
    boolean enabled() default true;
}

TimePointCut.java

package com.example.proxytestdemo;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;


@Aspect
@Component
public class TimePointCut {

    @Pointcut("execution(* com.example.proxytestdemo..*(..))")
    public void calcTime1() {
    }

    @Around(value = "calcTime1()")
    public Object aspectProcess(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("proxy begin....... ");
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        TimeIt annotation = method.getAnnotation(com.example.proxytestdemo.TimeIt.class);
        if (annotation == null) {
            annotation = pjp.getTarget().getClass().getAnnotation(TimeIt.class);
            if (annotation == null) {
                for (Class<?> cls : pjp.getClass().getInterfaces()) {
                    annotation = cls.getAnnotation(TimeIt.class);
                    if (annotation != null) {
                        break;
                    }
                }
            }
        }
        if (annotation != null) {
            System.out.println(annotation.enabled());
        }
        Object o = null;
        long t1 = 0, t2 = 0;
        try {
            t1 = System.currentTimeMillis();
            o = pjp.proceed();
            t2 = System.currentTimeMillis();
        } catch (Exception e) {
            throw e;
        } finally {
            System.out.println("proxy end....... ");
            System.out.println("time cost: "+ (t2-t1)/1000 + "s");
        }

        return o;
    }
}

I find that JdkProxy.function1() and JdkProxy.function2() and SubJdkProxy.functionSubJdkProxy() can not be advised.

Sorry, I made a mistake because of IDEA's hint. IDEA's hint

Upvotes: 0

Views: 635

Answers (1)

kriegaex
kriegaex

Reputation: 67352

Your application should work. Look, I tried all sorts of combinations, they all work:

Driver application:

package com.example.proxytestdemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class Application {
  public static void main(String[] args) {
    try (ConfigurableApplicationContext context = SpringApplication.run(Application.class, args)) {
      doStuff(context);
    }
  }

  private static void doStuff(ConfigurableApplicationContext context) {
    JdkProxy jdkProxy = (JdkProxy) context.getBean("jdkProxy");
    jdkProxy.function(11);
    jdkProxy.function1(22);
    jdkProxy.function2(33);
    System.out.println("----------");

    JdkProxyInterface jdkProxyInterface = jdkProxy ;
    jdkProxyInterface.function(11);
    System.out.println("==========");

    SubJdkProxy subJdkProxy = (SubJdkProxy) context.getBean("subJdkProxy");
    subJdkProxy.function(11);
    subJdkProxy.function1(22);
    subJdkProxy.function2(33);
    subJdkProxy.functionSubJdkProxy(44);
    System.out.println("----------");

    jdkProxyInterface = subJdkProxy;
    jdkProxyInterface.function(11);
  }
}

Console log:

execution(void com.example.proxytestdemo.JdkProxy.function(int))
JdkProxy function
execution(void com.example.proxytestdemo.JdkProxy.function1(int))
JdkProxy function
execution(void com.example.proxytestdemo.JdkProxy.function2(int))
JdkProxy function
----------
execution(void com.example.proxytestdemo.JdkProxy.function(int))
JdkProxy function
==========
execution(void com.example.proxytestdemo.JdkProxy.function(int))
JdkProxy function
execution(void com.example.proxytestdemo.JdkProxy.function1(int))
JdkProxy function
execution(void com.example.proxytestdemo.JdkProxy.function2(int))
JdkProxy function
execution(void com.example.proxytestdemo.SubJdkProxy.functionSubJdkProxy(int))
functionSubJdkProxy
----------
execution(void com.example.proxytestdemo.JdkProxy.function(int))
JdkProxy function

BTW, in order to concentrate on the basics I simplified your aspect's advice method to:

      @Around(value = "calcTime1()")
      public Object aspectProcess(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println(pjp);
        return pjp.proceed();
      }

Update:

Spring Boot defaults to CGLIB proxy mode and currently cannot easily be reconfigured to use JDK proxies because the corresponding annotations are ignored or superseded, see issue #12194. But I found a way, see my comment on Spring Boot #27375. You need to put this into your application.properties:

spring.aop.proxy-target-class=false

However, plain vanilla Spring defaults JDK proxy mode. You have to set up a classical Spring project without any Boot dependencies on your classpath. But then of course only interface-defined methods are being proxied and you cannot use methods defined outside of interfaces. You can switch Spring to CGLIB mode too, but not Boot to JDK mode.

Because this is a common question and I like to have a playground project to answer related questions anyway, I published this GitHub project for your convenience. Feel free to inspect it, clone it and play around with it.


Update 2022-02-26: Here you can learn how to determine Spring AOP proxy types (JDK vs. CGLIB proxies) using Spring's own AopUtils helper class.

Upvotes: 2

Related Questions