授人以鱼不如授人以渔,《手把手教你深入理解Spring源码》专栏教你如何学习、思考、阅读Spring框架,并应对其它开源框架不再畏惧。
接着上篇的文章讲,上篇的文章讲述了什么是IOC,这篇讲述什么又是AOP? 一样的在看这篇文章之前,大家不妨先花点时间思考一下。
1、AOP的设计原理
1:在Spring中,AOP的实现有两种方式,如果一个 Bean 是基于接口实现的,则Spring会采用JDK的动态代理,否则使用Cglib的动态代理。
2:在Spring中,代理方法被调用时,会被多个“方法拦截器”拦截,方法拦截器的拦截分为两层,外层是由Spring的内核实现,内层是用户自定义的,拦截器链路的实现使用的是责任链模式实现的。
2、什么是AOP
AOP是一种编程思想,它的作用实际上就是两个字"解耦",降低业务逻辑和功能模块(日志统计、事务)之间的耦合性!做过项目的都知道,当一个项目复杂的时候,程序员所写的业务逻辑是不可预估的。
比如我们要统计一个方法执行的时间,最简单的方式应该是如下
1、待统计的方法:
public void studyAop() {
log.info("手把手教你深入理解Spring源码");
}
2、实现统计方法运行时间
static public void studyAop() {
long start = System.currentTimeMillis();
log.info("手把手教你深入理解Spring源码");
long end = System.currentTimeMillis();
log.info("method run time:" + (end - start));
}
3、总结:以上代码确实实现了统计一个方法的运行时间,可是当用上面方法统计整个项目方法的运行时间时,衍生出来的代码是爆炸性的,那这种方法肯定不符合规范。于是jdk提供了api供程序员定制自己的AOP编程思想,Spring实现AOP时其中的一种就是采用jdk的动态代理。
下面看看什么是jdk的动态代理
public class A implements OneInterface {
private static final MicroLogUtil log = MicroLogFactory.getLogger();
public void studyAop() {
log.info("A Class 手把手教你深入理解Spring源码");
}
}
public class B implements OneInterface {
private static final MicroLogUtil log = MicroLogFactory.getLogger();
@Override
public void studyAop() {
log.info("B Class 手把手教你深入理解Spring源码");
}
}
public interface OneInterface {
public void studyAop();
}
以上有两个类,分别为 A,B 类,他们都实现了OneInterface接口,我们现在没有在studyAop()方法中直接硬编码,而是准备利用jdk的动态代理实现统计一个方法的运行时间。
public class TimeHandler implements InvocationHandler {
//目标对象实现的接口
private OneInterface target;
private static final MicroLogUtil log = MicroLogFactory.getLogger();
//封装的获取JDK动态代理生成的代理对象
public Object getProxyBean(OneInterface target) {
this.target = target;
Class clazz = target.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
}
@Override
//重写jdk 的invoke 方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long start = System.currentTimeMillis();
//目标对象的具体实现
target.studyAop();
long end = System.currentTimeMillis();
log.info("method run time:" + (end - start));
return null;
}
}
上面代码是自己写了个TimeHandler类实现jdk动态代理的InvocationHandler接口,并重写invoke方法,从而实现方法运行时间统计功能,注意我们现在统计方法运行时间的代码是现在invoke方法中。
重写invoke方法后,通过调用jdk提供的,Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this)方法;jdk动态代理内部会进行一系列处理,从而生成一个代理对象并返回。
现在我们分别运行A,B类中的 studyAop()方法,测试统计运行时间功能是否实现
public static void main(String args[]) {
A a = new A();
OneInterface aProxyBean = (OneInterface) new TimeHandler().getProxyBean(a);
aProxyBean.studyAop();
// B b = new B();
// OneInterface bProxyBean = (OneInterface) new TimeHandler().getProxyBean(b);
// bProxyBean.studyAop();
}
将A类作为被代理对象,传入getProxyBean()方法中。在没有改动A类中的待统计方法的代码实现了,运行时间统计功能
public static void main(String args[]) {
// A a = new A();
// OneInterface aProxyBean = (OneInterface) new TimeHandler().getProxyBean(a);
// aProxyBean.studyAop();
B b = new B();
OneInterface bProxyBean = (OneInterface) new TimeHandler().getProxyBean(b);
bProxyBean.studyAop();
}
将B类作为被代理对象,传入getProxyBean()方法中。在没有改动B类中的待统计方法的代码实现了,运行时间统计功能
从以上分析可以知道,被代理对象其实就是我们项目中写的一些增删查改业务逻辑,具体的执行代码写在实现jdk动态代理提供的api:InvocationHandler接口后的,invoke方法中。通过调用jdk动态代理提供的api:Proxy.newProxyInstance()方法,内部生成一个代理对象。从而达到业务逻辑和功能模块的解耦并实现了统计方法运行时间的功能。
Spring的事务,用户自定义的前置、后置通知,都可以通过jdk的动态代理实现。具体的源码分析将会在下面章节介绍。
大家对本篇文章感兴趣的朋友可以加 Q群:837348118 一起讨论源码,面试题,算法,公司文化。