转:Spring AOP 介绍
AOP原理、动态代理(JDK、CGLib)
CGLib源码分析
静态代理
@Aspect
@Component
public class TestAdvice {
// 1. 定义 PointCut
@Pointcut("execution(* com.example.demo.api.TestServiceImpl.eatCarrot())")
private void eatCarrot(){}
// 2. 定义应用于 JoinPoint 中所有满足 PointCut 条件的 advice, 这里我们使用 around advice,在其中织入增强逻辑
@Around("eatCarrot()")
public void handlerRpcResult(ProceedingJoinPoint point) throws Throwable {
System.out.println("吃萝卜前洗手");
// 原来的 TestServiceImpl.eatCarrot 逻辑,可视情况决定是否执行
point.proceed();
System.out.println("吃萝后买单");
}
}
public interface Subject {
public void request();
}
public class RealSubject implements Subject {
@Override
public void request() {
// 卖房
System.out.println("卖房");
}
}
public class Proxy implements Subject {
private RealSubject realSubject;
public Proxy(RealSubject subject) {
this.realSubject = subject;
}
@Override
public void request() {
// 执行代理逻辑
System.out.println("卖房前");
// 执行目标对象方法
realSubject.request();
// 执行代理逻辑
System.out.println("卖房后");
}
public static void main(String[] args) {
// 被代理对象
RealSubject subject = new RealSubject();
// 代理
Proxy proxy = new Proxy(subject);
// 代理请求
proxy.request();
}
}
JDK动态代理
// 委托类
public class RealSubject implements Subject {
@Override
public void request() {
// 卖房
System.out.println("卖房");
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyFactory {
private Object target;// 维护一个目标对象
public ProxyFactory(Object target) {
this.target = target;
}
// 为目标对象生成代理对象
public Object getProxyInstance() {
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("计算开始时间");
// 执行目标对象方法
method.invoke(target, args);
System.out.println("计算结束时间");
return null;
}
});
}
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
System.out.println(realSubject.getClass());
Subject subject = (Subject) new ProxyFactory(realSubject).getProxyInstance();
System.out.println(subject.getClass());
subject.request();
}
}```
打印结果如下:
```shell
原始类:class com.example.demo.proxy.staticproxy.RealSubject
代理类:class com.sun.proxy.$Proxy0
计算开始时间
卖房
计算结束时间
我们注意到代理类的 class 为 com.sun.proxy.$Proxy0,它是如何生成的呢,注意到 Proxy 是在 java.lang.reflect 反射包下的,注意看看 Proxy 的 newProxyInstance 签名
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h);
loader: 代理类的ClassLoader,最终读取动态生成的字节码,并转成 java.lang.Class 类的一个实例(即类),通过此实例的 newInstance() 方法就可以创建出代理的对象
interfaces: 委托类实现的接口,JDK 动态代理要实现所有的委托类的接口
InvocationHandler: 委托对象所有接口方法调用都会转发到 InvocationHandler.invoke(),在 invoke() 方法里我们可以加入任何需要增强的逻辑 主要是根据委托类的接口等通过反射生成的
那么 Spring AOP 的实现为啥却不用它呢?
- 如果要用 JDK 代理,委托类必须实现接口
- 性能问题
CGLib 动态代理
AOP 就是用的 CGLib 的形式来生成的,JDK 动态代理使用 Proxy 来创建代理类,增强逻辑写在 InvocationHandler.invoke() 里,CGlib 动态代理也提供了类似的 Enhance 类,增强逻辑写在 MethodInterceptor.intercept() 中,也就是说所有委托类的非 final 方法都会被方法拦截器拦截,在说它的原理之前首先来看看它怎么用的
public class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("目标类增强前!!!");
//注意这里的方法调用,不是用反射哦!!!
Object object = proxy.invokeSuper(obj, args);
System.out.println("目标类增强后!!!");
return object;
}
}
public class CGlibProxy {
public static void main(String[] args) {
//创建Enhancer对象,类似于JDK动态代理的Proxy类,下一步就是设置几个参数
Enhancer enhancer = new Enhancer();
//设置目标类的字节码文件
enhancer.setSuperclass(RealSubject.class);
//设置回调函数
enhancer.setCallback(new MyMethodInterceptor());
//这里的creat方法就是正式创建代理类
RealSubject proxyDog = (RealSubject) enhancer.create();
//调用代理类的eat方法
proxyDog.request();
}
}
代理类:class com.example.demo.proxy.staticproxy.RealSubject$$EnhancerByCGLIB$$889898c5
目标类增强前!!!
卖房
目标类增强后!!!
可以看到主要就是利用 Enhancer 这个类来设置委托类与方法拦截器,这样委托类的所有非 final 方法就能被方法拦截器拦截,从而在拦截器里实现增强
底层实现原理
- 它并不要求委托类实现任何接口,而且 CGLIB 是高效的代码生成包,底层依靠 ASM(开源的 java 字节码编辑类库)操作字节码实现的,性能比 JDK 强,所以 Spring AOP 最终使用了 CGlib 来生成动态代理
- 反射的效率比较低,所以 CGlib 采用了FastClass 的机制来实现对被拦截方法的调用。FastClass 机制就是对一个类的方法建立索引,通过索引来直接调用相应的方法,建议参考下https://www.cnblogs.com/cruze/p/3865180.html 这个链接好好学学