在介绍AOP之前,想必很多人都听说AOP是基于动态代理和反射来实现的,那么在看AOP之前,你需要确实弄懂动态代理和反射。
巧了,我这里就有。
JDK的动态代理的实现和源码分析,参见我的这两篇文章
JDK的动态代理源码分析之一 ( http://blog.csdn.net/weililansehudiefei/article/details/73655925 )
JDK的动态代理源码分析之二( http://blog.csdn.net/weililansehudiefei/article/details/73656923 )
上面的两个看懂了,那么就可以来看本篇文章了。至于Java反射知道就行,但是更巧的是我博客里正好有一篇介绍Java反射的。
Java反射的实现和基本原理,参见我的这篇文章
Java反射机制 (http://blog.csdn.net/weililansehudiefei/article/details/70194940)
那么接下里进入AOP的环节。
AOP即面向切面编程,刚学AOP的时候,单是各种AOP的概念都搞的有点懵,什么切面,切点,通知,织入、连接点、目标对象。。。。AOP的原理都没看呢,这些词语的意思就已经上人不想看了。本人将在实现AOP的时候,讲解我理解的这些AOP的术语,对应的AOP的代码和动作。
本文将先先从代码实现AOP入手,然后分析AOP的底层代码及其原理。
后期会把所有的工程实现代码,放在我的GitHub上,到时候我会更新文章。
一、AOP的Demo
如果我们对象的继承关系看成纵向关系,就像一棵树,多个不同类的多个继承关系就相当于有一排的树。AOP的好处就在于,你想对这些树进行相同的操作,这个时候,不用纵向的为每个树定义操作方法,你只需要横向的一刀切,给他们给个共有的操作方法。
Spring的AOP是支持JDK的动态代理和Cglib的动态代理的。JDK的动态代理是针对接口的,而Cglib是针对类的。本文针对JDK的动态代理。
首先定义一个接口:起名字时候特意给这个接口名,带上了Interface,这样后面会更引人注意一些。接口很简单,里面一个抽象方法eat()
package com.weili.cn;
/**
* Created by weili on 17/6/27.
*/
public interface AnimalInterface {
public abstract void eat();
}
这个实现类里面,只有一个方法,这个方法就是AOP的切点。虽然切点这个概念本身并不一定是Method,但在Spring中,所有的切点都是Method。我们增强的是方法。
package com.weili.cn;
/**
* Created by weili on 17/6/27.
*/
public class Animal implements AnimalInterface{
public void eat() {
System.out.println("Animal类中 chi chi chi");
}
}
切面类,又称增强类。因为我们是要用这个类的方法,来给原先的切点方法增强。切面类中,我们要去执行的方法,称为通知。所谓织入通知,就是将切面类里面的方法,和切点的方法进行联系。
package com.weili.cn;
import org.aopalliance.intercept.Joinpoint;
/**
* Created by weili on 17/6/27.
*/
public class AdviceAnimal {
public void animalEmpty(){
//System.out.println("joint before "+ joinPoint.getClass().getName());
System.out.println("我饿了");
}
public void animalFull(){
System.out.println("吃饱了");
}
public void animalEat(){
System.out.println("正在吃");
}
}
配置
我得说明一点,在我们进行spring-aop.xml解析的时候,aop还没实现呢。在第二行getBean的时候,才真正进行aop。具体的源码那里 会说明。
package com.weili.cn;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Hello world!
*
*/
public class App
{
public static void main( String[] args )
{
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring-aop.xml");
AnimalInterface animal = (AnimalInterface) ctx.getBean("animal");
animal.eat();
}
}
紧接着就是Output了。所以,我们可以看到,获取的bean,确实是增强后的bean。那么就赶紧看看源码吧。
我饿了
Animal类中 chi chi chi
吃饱了
二、AOP源码分析
源码解析这块,首先就是bean加载。之前也说了,AOP标签也是自定义标签,它的解析也和我们之前自定义标签一样,走自定义标签的解析流程。不同的是,AOP调用的是AOP自己的解析器。由于在 Spring源码解析之二 ------ 自定义标签的解析和注册 中已经很详细的描述了自定义标签的解析流程,所以这里我们就不再去一一看bean标签的解析注册。
所以AOP的源码分析,我们将从调用类里面的第二行,ctx.getBean("animal")开始。在你调试走到这里的时候,在ctx中可以看到解析和注册的bean,我们不妨先来看一下。
如下图,这个是在第一行代码执行完毕后,ctx的各个属性。可以在下图看到,singlentonObjects中,已经存放了代理生成的animal。生层bean的过程在之前的里面已经讲的比较清楚了,这里就不再说明。毕竟AOP嘛,我们需要知道,它是如何在我们需要执行的方法前后将我们需要执行的方法执行完成的。
ctx.getBean("animal")获取完animal bean后,接下来调用eat()方法。这个时候,会进入JdkDynamicAopProxy类的invoke方法。
在这个invoke方法中,先是获取代理类targetClass,然后根据method和targetClass获取此方法对应的拦截器执行链chain。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Class> targetClass = null;
Object target = null;
Boolean var10;
try {
if(this.equalsDefined || !AopUtils.isEqualsMethod(method)) {
if(!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
Integer var18 = Integer.valueOf(this.hashCode());
return var18;
}
Object retVal;
if(!this.advised.opaque && method.getDeclaringClass().isInterface() && method.getDeclaringClass().isAssignableFrom(Advised.class)) {
retVal = AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
return retVal;
}
if(this.advised.exposeProxy) {
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
target = targetSource.getTarget();
if(target != null) {
targetClass = target.getClass();
}
List
进入proceed方法。currentInterceptorIndex的初始化值为-1.紧接着就如invoke方法。这里的this是我们的eat方法。
public Object proceed() throws Throwable {
if(this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return this.invokeJoinpoint();
} else {
Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if(interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher)interceptorOrInterceptionAdvice;
return dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)?dm.interceptor.invoke(this):this.proceed();
} else {
return ((MethodInterceptor)interceptorOrInterceptionAdvice).invoke(this);
}
}
}
在invoke方法里,这里的mi是我们的interface里面的eat方法。然后执行mi的proceed()方法。
public Object invoke(MethodInvocation mi) throws Throwable {
MethodInvocation oldInvocation = (MethodInvocation)invocation.get();
invocation.set(mi);
Object var3;
try {
var3 = mi.proceed();
} finally {
invocation.set(oldInvocation);
}
return var3;
}
这个时候,会继续回到开始时候的proceed方法。这个时候获取到的是
interceptorOrInterceptionAdvice,也就是前面拦截器的list里面的第二个,after的那个方法。然后继续递归调用,会到链表的最后一个before方法。
最终会调用before里面的方法,
public Object invoke(MethodInvocation mi) throws Throwable {
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
return mi.proceed();
}
然后回去执行invokeJoinpoint方法,
public static Object invokeJoinpointUsingReflection(Object target, Method method, Object[] args) throws Throwable {
try {
ReflectionUtils.makeAccessible(method);
return method.invoke(target, args);
} catch (InvocationTargetException var4) {
throw var4.getTargetException();
} catch (IllegalArgumentException var5) {
throw new AopInvocationException("AOP configuration seems to be invalid: tried calling method [" + method + "] on target [" + target + "]", var5);
} catch (IllegalAccessException var6) {
throw new AopInvocationException("Could not access method [" + method + "]", var6);
}
}
}
最后执行after方法。