在介绍 AOP 之前, 想必很多人都听说 AOP 是基于动态代理和反射来实现的, 那么在看 AOP 之前, 你需要弄懂什么是动态代理和反射及它们又是如何实现的.
想了解 JDK 的动态代理及反射的实现和源码分析, 请参见下面三篇文章
JDK 的动态代理源码分析之一 (http://blog.csdn.net/weililansehudiefei/article/details/73655925)
JDK 的动态代理源码分析之二 (http://blog.csdn.net/weililansehudiefei/article/details/73656923)
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 zsqweilai on 17/6/27.
- */
- public interface AnimalInterface {
- public abstract void eat();
- }
实现类: 作为一个吃货, 实现类里面当然得打印 chi chi chi. 撑死我吧!!!
这个实现类里面, 只有一个方法, 这个方法就是 AOP 的切点. 虽然切点这个概念本身并不一定是 Method, 但在 Spring 中, 所有的切点都是 Method. 我们增强的是方法.
- package com.weili.cn;
- /**
- * Created by zsqweilai 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 zsqweilaion 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("正在吃");
- }
- }
接下来通过 xml 配置的方式, 在 xml 文件里面配置 AOP.
配置 < aop:pointcut > 的时候, 通过 expressi 表达式, 定义了 com.weili.cn 这个包下的所有类的所有方法 为切入点. 也就是说, 这个包下的所有方法, 在调用执行的时候, 会被 Spring 增强. 具体在这里的增强, 就是在执行这些切点方法之前和之后, 会分别执行 animalEmpty 和 animalFull 方法.
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context-4.0.xsd
- http://www.springframework.org/schema/aop
- http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
- <bean id="animal" class="com.weili.cn.Animal"/>
- <bean id="adviceAnimal" class="com.weili.cn.AdviceAnimal"/>
- <aop:config>
- <aop:aspect id="myaop" ref="adviceAnimal">
- <aop:pointcut id="logPointcut" expression="execution(* com.weili.cn.*.*(..))" />
- <aop:before method="animalEmpty" pointcut-ref="logPointcut" />
- <aop:after method="animalFull" pointcut-ref="logPointcut" />
- </aop:aspect>
- </aop:config>
- </beans>
最后就是调用的方法了.
我得说明一点, 在我们进行 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
吃饱了
然后回去执行 invokeJoinpoint 方法,
二, 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<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);// 获取执行链
- if(chain.isEmpty()) {
- Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
- retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
- } else {
- MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
- retVal = invocation.proceed();
- }
- Class<?> returnType = method.getReturnType();
- if(retVal != null && retVal == target && returnType.isInstance(proxy) && !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
- retVal = proxy;
- } else if(retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
- throw new AopInvocationException("Null return value from advice does not match primitive return type for:" + method);
- }
- Object var13 = retVal;
- return var13;
- }
- var10 = Boolean.valueOf(this.equals(args[0]));
- } finally {
- if(target != null && !targetSource.isStatic()) {
- targetSource.releaseTarget(target);
- }
- if(setProxyContext) {
- AopContext.setCurrentProxy(oldProxy);
- }
- }
- return var10;
- }
这个 chain 的内容如下. 通过名字可以看到, 一个是 afterAdvice, 一个是 beforeAdvice. 获取 chain 后, 构造出一个 MethodInvoke 方法, 然后执行 proceed 方法.
进入 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 方法.
来源: http://www.bubuko.com/infodetail-2862646.html