Issue
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
Solution
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 be reconfigured to use JDK proxies because the corresponding annotations are ignored or superseded, see issue #12194.
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.
Answered By - kriegaex
Answer Checked By - Cary Denson (JavaFixing Admin)