再写完《走进Spring中Bean的世界》,写这篇文章时,本来想法是把AOP的思想、原理以及源码通通囊入其中,最后发现会和上篇文章同样,读起来难以专一。因此仍是本着跳出看全景,转进去看本质的原则,将AOP的文章分为三部分,分别为思想篇、源码篇、应用篇。html
虚拟栈是一个后入先出(LIFO)栈。每个线程建立时,JVM会为这个线程建立一个私有的虚拟机栈,当线程调用某个对象的方法时,JVM会相应地建立一个栈帧(Stack Frame)放到虚拟机中,用来表示某个方法的调用。线程对方法的调用就对应一个栈帧的入栈和出栈的过程。spring
public class TestMethod {
public void method1() {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
}
public static void main(String[] args) {
TestMethod target = new TestMethod();
target.method1();
}
}
复制代码
上面代码咱们在method()1时,打个断点。
上面把程序执行机制以链接点的概念进行了抽象,下面来了解下面向切面以此为基准的编程思想express
上面了解到AOP是对方法调用进行编程,那么AOP如何捕获方法的调用的?其实AOP实现的基础是代理模式的应用。编程
代理模式中,由抽象角色(Subject)、真实角色(TargetSubject)、代理角色(Proxy)。其中:抽象角色(Subject)负责定义真实角色(TargetSubject)和代理角色(Proxy)应该实现的接口;真实角色(TargetSubject)来完成真正的request功能;代理角色(Proxy)负责将自身的request请求调用真实角色(TargetSubject)对应的request功能来实现业务功能,本身不真正作业务,并在调用request先后,插入代码实现具体功能。bash
public interface Subject {
public void request();
}
public class TargetSubject implements Subject {
@Override
public void request() {
System.out.println("TargetSubject#request...");
}
}
public class InvocationHandlerImpl implements InvocationHandler {
private TargetSubject targetSubject;
public InvocationHandlerImpl(TargetSubject targetSubject) {
this.targetSubject = targetSubject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("TargetSubject#request before...");
method.invoke(targetSubject, null);
System.out.println("TargetSubject#request after...");
return null;
}
}
public class Test001 {
public static void main(String[] args) {
TargetSubject targetSubject = new TargetSubject();
// 1.获取对应的ClassLoader
ClassLoader classLoader = targetSubject.getClass().getClassLoader();
// 2.获取targetSubject 所实现的全部接口
Class[] interfaces = targetSubject.getClass().getInterfaces();
// 3.设置一个来自代理传过来的方法调用请求处理器,处理全部的代理对象上的方法调用
InvocationHandler handler = new InvocationHandlerImpl(targetSubject);
/**
* 4.根据上面提供的信息,建立代理对象 在这个过程当中
* a.JDK会经过根据传入的参数信息动态地在内存中建立和.class 文件等同的字节码。
* b.而后根据相应的字节码转换成对应的class。
* 而后调用newInstance()建立实例。
*/
Subject proxy = (Subject)Proxy.newProxyInstance(classLoader, interfaces, handler);
proxy.request();
}
}
复制代码
JDK动态代理提供的生成动态代理类有个特色,真实角色(TargetSubject)必须有实现的定义接口(Subject),而且只能代理该接口定义的方法,因此当某个类没有实现接口,那么这个类就不能使用动态代理了。那该如何去解决这种状况呢?请看cglib代理。ide
public class MethodInterceptorImpl implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("TargetSubject#request before...");
methodProxy.invokeSuper(o, args);
System.out.println("TargetSubject#request after...");
return null;
}
}
public class Test002 {
public static void main(String[] args) {
TargetSubject targetSubject = new TargetSubject();
MethodInterceptorImpl methodInterceptor = new MethodInterceptorImpl();
//cglib 中增强器,用来建立动态代理
Enhancer enhancer = new Enhancer();
//设置要建立动态代理的类
enhancer.setSuperclass(targetSubject.getClass());
// 设置回调,这里至关因而对于代理类上全部方法的调用,都会调用CallBack,而Callback则须要实行intercept()方法进行拦截
enhancer.setCallback(methodInterceptor);
TargetSubject proxy =(TargetSubject)enhancer.create();
proxy.request();
}
}
复制代码
经过上面两种代理模式的实现方式来看,有抽象角色存在时选择JDK也能实现,cglib属于全能型,有无抽象角色都可。Spring中对于“Bean”生成代理对象时,若是该“Bean”实现某个抽象角色(或者接口定义)则选择JDK动态代理生成代理对象,某则选用cglib方式生成代理对象。post