package com.haogrgr.test.interceptor;
import java.lang.reflect.Field;
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.springframework.aop.framework.ReflectiveMethodInvocation;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class CGlibInvokerSuperTestIntercepter {
@Pointcut("execution(* com.haogrgr.test.event.CGlibInvokerSuperTestService.*(..))")
public void logPointcut() {}
@Around(value = "logPointcut()")
public void v(ProceedingJoinPoint pjp) {
System.err.println("CGlibInvokerSuperTestIntercepter " + pjp);
try {
//这个属性是用来最后调用目标对象方法的包装类, 这里具体的类型是子类CglibMethodInvocation
Field declaredField = pjp.getClass().getDeclaredField("methodInvocation");
System.out.println(declaredField);
declaredField.setAccessible(true);
ReflectiveMethodInvocation object = (ReflectiveMethodInvocation) declaredField.get(pjp);
//CglibMethodInvocation是对cglib代理目标对象调用的一个封装, methodProxy是cglib的类, 提供方法的调用
Field declaredField2 = object.getClass().getDeclaredField("methodProxy");
declaredField2.setAccessible(true);
MethodProxy xx = (MethodProxy) declaredField2.get(object);
//invokeSuper表示调用父类, 因为cglib是继承实现的代理, 所以这里是调用被代理对象的方法
//这里这么调用后, 会发现, 拦截方法内部的this调用也走拦截了.
xx.invokeSuper(object.getProxy(), pjp.getArgs());
//和上面对应, 直接调用target的方法(直接调用fastInvoke).
//xx.invoke(object.getProxy(), pjp.getArgs());
//而, 如果target对象被调用的方法内部又通过this.xxx()调用了方法, 则会调用到调用类的xxx方法, 进而又被拦截一次.
//所以, spring的config类是通过invokeSuper调用来实现@Bean注解的方法被this调用也能拦截, 避免多次调用.
//然而, Spring的配置类有一个说法叫light模式(如没有标注为@Config(有多个条件的...)), 这个模式就会产生多次调用的问题.
//而, 一般的AOP之所以this调用会不走拦截, 是因为内部实现不是invokeSuper, 而是直接反射调用target方法(jdk proxy), 或者直接调用target方法(cglib fastinvoke)
//思考, 为什么AOP可以实现this调用拦截, 而spring却选择不实现呢?
//有可能是因为, 可能this调用的方法并不需要拦截, 但是通过invokeSuper的实现, 一定会走拦截,
//某些场景可能会有问题, 比如拦截请求记录, 通过AOP拦截来记录某一方法外部请求数量, 当这个方法中有this调用, 那么也会走AOP拦截, 如果AOP没有处理好的话, 那么计数可能会错误.
//可能会破坏@Pointcut语义, 比如我本意是这个方法不走拦截, 但是因为其他被拦截的方法内部this调用了这个方法, 那么就会导致原本不应该被拦截的方法走了拦截逻辑.
} catch (Throwable e) {
e.printStackTrace();
}
// try {
// pjp.proceed();
// } catch (Throwable e) {
// e.printStackTrace();
// }
}
}