Spring AOP 源码解读
[TOC]
AOP Alliance项目
AOP Alliance 提供了AOP标准接口。org.aopalliance下面有两个包:aop、interceptor。
aop包
aop包下面只有标记接口:Advice 和 定义的运行时异常:AspectException
Advice源码
如下,无方法无属性,属于标记接口。当一个类实现了标记接口后相当于‘打标记’,‘归类’的动作。标记接口属于面向对象的最高层,
package org.aopalliance.aop;
/**
* Tag interface for Advice. Implementations can be any type
* of advice, such as Interceptors.
*
* @author Rod Johnson
* @version $Id: Advice.java,v 1.1 2004/03/19 17:02:16 johnsonr Exp $
*/
public interface Advice {
}
AspectException
如下,AspectException 是自定义的一个运行时异常,是所有AOP基础架构异常的超类。
package org.aopalliance.aop;
/**
* Superclass for all AOP infrastructure exceptions.
* Unchecked, as such exceptions are fatal and end user
* code shouldn't be forced to catch them.
*
* @author Rod Johnson
* @author Bob Lee
* @author Juergen Hoeller
*/
@SuppressWarnings("serial")
public class AspectException extends RuntimeException {
//...
}
可以说,aop包总体定义了AOP的类下的‘标记’-Advice,和AOP基础异常-AspectException
interceptor包
由上图interceptor包下定义的都是接口,主要分为两大接口:Interceptor(拦截器)和 JoinPoint(连接点) 。
JoinPoint
package org.aopalliance.intercept;
import java.lang.reflect.AccessibleObject;
/**
* This interface represents a generic runtime joinpoint (in the AOP
* terminology).
*
* 在AOP术语中,JoinPoint 接口表示通用运行时连接点。
*
* <p>A runtime joinpoint is an <i>event</i> that occurs on a static
* joinpoint (i.e. a location in a the program). For instance, an
* invocation is the runtime joinpoint on a method (static joinpoint).
* The static part of a given joinpoint can be generically retrieved
* using the {@link #getStaticPart()} method.
*
* 运行时连接点是发生在静态连接点(即程序中的位置)上的事件。例如,invocation
(调用)就是方法(静态连接点)上的运行时连接点。可以使用getStaticPart()方法检索给定连接点的静态部分。
* <p>In the context of an interception framework, a runtime joinpoint
* is then the reification of an access to an accessible object (a
* method, a constructor, a field), i.e. the static part of the
* joinpoint. It is passed to the interceptors that are installed on
* the static joinpoint.
*
* 在拦截器框架的上下文中,运行时连接点是对可访问对象(方法,构造函数,字段,即连接点的静态部分)
的具化。它被传递给interceptors(安装在静态连接点上的拦截器)。
* @author Rod Johnson
* @see Interceptor
*/
public interface Joinpoint {
/**
* Proceed to the next interceptor in the chain.
* <p>The implementation and the semantics of this method depends
* on the actual joinpoint type (see the children interfaces).
* @return see the children interfaces' proceed definition
* @throws Throwable if the joinpoint throws an exception
* 运行拦截器链上的下一个拦截器
* 该方法的实现和意义取决于实际连接点的类型。
*/
Object proceed() throws Throwable;
/**
* Return the object that holds the current joinpoint's static part.
* <p>For instance, the target object for an invocation.
* @return the object (can be null if the accessible object is static)
*
* 返回保存当前连接点静态部分的对象。
例如,调用的目标对象。如果可访问的对象是静态的,可以为null
*/
Object getThis();
/**
* Return the static part of this joinpoint.
* <p>The static part is an accessible object on which a chain of
* interceptors are installed.
*返回连接点的静态部分。静态部分是可访问对象, 对象上绑定了一系列拦截器。
*/
AccessibleObject getStaticPart();
}
- 静态连接点/可访问对象:包含Field属性, Method方法 and Constructor构造函数。
- JointPoint 指对可访问对象访问触发的事件,方法的调用,构造函数的构造,属性的getter/setter。(或者说运行时触发可访问对象件,表现在虚拟机栈上的就是栈帧)
Invocation
package org.aopalliance.intercept;
/**
* This interface represents an invocation in the program.
*在程序中,该接口表示调用。
* <p>An invocation is a joinpoint and can be intercepted by an
* interceptor.
调用是连接点,并且可以被拦截器拦截。
*
* @author Rod Johnson
*/
public interface Invocation extends Joinpoint {
/**
* Get the arguments as an array object.
获取参数数组
* It is possible to change element values within this
* array to change the arguments.
可以改变参数的值来改变参数。
* @return the argument of the invocation
*/
Object[] getArguments();
}
- Invocation 继承了Joinpoint,也就是一种连接点,能够被拦截。
- 调用接口增加了一个方法,获取参数数组。
- 其子接口又分构造器调用接口ConstructorInvocation和方法调用接口MethodInvocation
ConstructorInvocation
package org.aopalliance.intercept;
import java.lang.reflect.Constructor;
/**
* Description of an invocation to a constuctor, given to an
* interceptor upon constructor-call.
*描述构造器的调用,在构造函数被调用时给予拦截器
* <p>A constructor invocation is a joinpoint and can be intercepted
* by a constructor interceptor.
*构造器调用是连接点, 并且能够被构造器拦截器拦截。
* @author Rod Johnson
* @see ConstructorInterceptor
*/
public interface ConstructorInvocation extends Invocation {
/**
* Get the constructor being called.
* <p>This method is a friendly implementation of the
* {@link Joinpoint#getStaticPart()} method (same result).
* @return the constructor being called
获取被构造的构造器。这个方法友好地实现了Joinpoint#getStaticPart()方法(同样的结果)。
*/
Constructor<?> getConstructor();
}
- ConstructorInvocation 将构造器的调用具体化了,这个接口继承了Invocation,是JointPoint。
- 其方法getConstructor()能获取被调用的构造器,在调用前后扔给ConstructorInterceptor拦截。
MethodInvocation
package org.aopalliance.intercept;
import java.lang.reflect.Method;
/**
* Description of an invocation to a method, given to an interceptor
* upon method-call.
*描述方法的调用,在方法被调用时给予拦截器
* <p>A method invocation is a joinpoint and can be intercepted by a
* method interceptor.
* 方法调用是连接点, 并且能够被方法拦截器拦截。
* @author Rod Johnson
* @see MethodInterceptor
*/
public interface MethodInvocation extends Invocation {
/**
* Get the method being called.
* <p>This method is a frienly implementation of the
* {@link Joinpoint#getStaticPart()} method (same result).
* @return the method being called
获取被调用的方法。改方法友好地实现了Joinpoint#getStaticPart()方法(同样的结果)。
*/
Method getMethod();
}
- MethodInvocation 是针对方法的连接点。
- 其方法getMethod()能获取被调用的方法,在调用前后扔给MethodInterceptor拦截。
Interceptor
package org.aopalliance.intercept;
import org.aopalliance.aop.Advice;
/**
* This interface represents a generic interceptor.
这是通用拦截器接口。
*
* <p>A generic interceptor can intercept runtime events that occur
* within a base program. Those events are materialized by (reified
* in) joinpoints. Runtime joinpoints can be invocations, field
* access, exceptions...
*通用拦截器能够拦截程序中正在运行的事件。这些事件被具体化为连接点。
运行时连接点能够被调用,字段访问限制,异常捕捉等等。
* <p>This interface is not used directly. Use the sub-interfaces
* to intercept specific events. For instance, the following class
* implements some specific interceptors in order to implement a
* debugger:
*这个接口并不是直接使用,而是用子接口去拦截特定的事件。
例如,下面类中为了完成调试而实现了一些特定的拦截器。
* <pre class=code>
* class DebuggingInterceptor implements MethodInterceptor,
* ConstructorInterceptor, FieldInterceptor {
*
* Object invoke(MethodInvocation i) throws Throwable {
* debug(i.getMethod(), i.getThis(), i.getArgs());
* return i.proceed();
* }
*
* Object construct(ConstructorInvocation i) throws Throwable {
* debug(i.getConstructor(), i.getThis(), i.getArgs());
* return i.proceed();
* }
*
* Object get(FieldAccess fa) throws Throwable {
* debug(fa.getField(), fa.getThis(), null);
* return fa.proceed();
* }
*
* Object set(FieldAccess fa) throws Throwable {
* debug(fa.getField(), fa.getThis(), fa.getValueToSet());
* return fa.proceed();
* }
*
* void debug(AccessibleObject ao, Object this, Object value) {
* ...
* }
* }
* </pre>
*
* @author Rod Johnson
* @see Joinpoint
*/
public interface Interceptor extends Advice {
}
- Interceptor:拦截器顾名思义,就是能够拦截连接点的接口。
- 这是一个继承了Advice的接口,同样无属性无方法,具体的拦截器需要其子接口完成。
ConstructorInterceptor
package org.aopalliance.intercept;
/**
* Intercepts the construction of a new object.
拦截new对象的构造器。
*
* <p>The user should implement the {@link
* #construct(ConstructorInvocation)} method to modify the original
* behavior. E.g. the following class implements a singleton
* interceptor (allows only one unique instance for the intercepted
* class):
*用户实现这个方法去改变原来的行为。例如,下面类中实现了一个单例拦截器(被拦截的类只允许唯一一个实例)
* <pre class=code>
* class DebuggingInterceptor implements ConstructorInterceptor {
* Object instance=null;
*
* Object construct(ConstructorInvocation i) throws Throwable {
* if(instance==null) {
* return instance=i.proceed();
* } else {
* throw new Exception("singleton does not allow multiple instance");
* }
* }
* }
* </pre>
*
* @author Rod Johnson
*/
public interface ConstructorInterceptor extends Interceptor {
/**
* Implement this method to perform extra treatments before and
* after the construction of a new object. Polite implementations
* would certainly like to invoke {@link Joinpoint#proceed()}.
* @param invocation the construction joinpoint
* @return the newly created object, which is also the result of
* the call to {@link Joinpoint#proceed()}; might be replaced by
* the interceptor
* @throws Throwable if the interceptors or the target object
* throws an exception
在构建一个的新对象前后, 通过实现这个方法来做额外的处理。规范的做法肯定会调用Joinpoint#proceed()方法
参数为 调用构造器的连接点
返回 新建的对象,可能是唤起Joinpoint#proceed()的结果,可能被拦截器替换后的结果。
拦截器或者目标对象会抛出Throwable异常
*/
Object construct(ConstructorInvocation invocation) throws Throwable;
}
- ConstructorInterceptor入参是ConstructorInvocation,拦截构造器的调用,返回的是新建的对象。
MethodInterceptor
package org.aopalliance.intercept;
/**
* Intercepts calls on an interface on its way to the target. These
* are nested "on top" of the target.
*拦截器在到达目标的路上调用接口。 这些拦截器被嵌套在目标的“顶部”上。
* <p>The user should implement the {@link #invoke(MethodInvocation)}
* method to modify the original behavior. E.g. the following class
* implements a tracing interceptor (traces all the calls on the
* intercepted method(s)):
*用户需要实现#invoke(MethodInvocation)方法去修改原来的行为。例如下面类中实现了拦截器的跟踪(跟踪被拦截的方法上调用的所有拦截器)
* <pre class=code>
* class TracingInterceptor implements MethodInterceptor {
* Object invoke(MethodInvocation i) throws Throwable {
* System.out.println("method "+i.getMethod()+" is called on "+
* i.getThis()+" with args "+i.getArguments());
* Object ret=i.proceed();
* System.out.println("method "+i.getMethod()+" returns "+ret);
* return ret;
* }
* }
* </pre>
*
* @author Rod Johnson
*/
public interface MethodInterceptor extends Interceptor {
/**
* Implement this method to perform extra treatments before and
* after the invocation. Polite implementations would certainly
* like to invoke {@link Joinpoint#proceed()}.
* @param invocation the method invocation joinpoint
* @return the result of the call to {@link Joinpoint#proceed()};
* might be intercepted by the interceptor
* @throws Throwable if the interceptors or the target object
* throws an exception
在调用前后, 通过实现这个方法来做额外的处理。规范的做法肯定会调用Joinpoint#proceed()方法
参数为 调用方法的连接点
返回 Joinpoint#proceed()的结果,可能被拦截器替换后的结果。
拦截器或者目标对象会抛出Throwable异常
*/
Object invoke(MethodInvocation invocation) throws Throwable;
}
- MethodInterceptor入参是MethodInvocation,拦截方法的调用,返回的是Joinpoint#proceed()方法执行后的结果。
总结
全文其实重点在设计和注释上面。 Rod Johnson大神注释写得很明白了。(英译中较渣) 现自己总结如下:
-
AOP Alliance 包下除了AspectException异常类外,其他全为通用接口。也正如该项目介绍的一样,提供标准统一的接口来处理面向切面编程(Aspect-Oriented Programming -AOP)的问题。
-
其接口主要分两种类型: JoinPoint 和 Advice(也可说Interceptor,Advice只是个标记接口)。
- JoinPoint 为连接点,确切是运行时连接点。是对可访问对象(Filed、Method、Constructor)的调用事件,在虚拟机栈上表现为栈帧,换句话就是方法的调用,构造器的构造,字段的访问这些动作。
- JoinPoint 子接口为Invocation,Invocation本身具有获取参数数组的getArguments()方法,并且扩展出两个子接口,针对构造器的ConstructorInvocation和针对方法的MethodInvocation。
-
Interceptor 即为拦截器, Advice的子接口。Interceptor亦是无属性无方法的接口,只作为定义,因而扩展出子接口ConstructorInterceptor和MethodInterceptor
-
拦截器是根据传入的连接点进行拦截,且是须一一对应(构造器拦截器拦截构造器的调用,方法拦截器拦截方法的调用),执行需要添加的处理。
-
拦截器可以有多个,都处在target的头部,通过Joinpoint#proceed()方法一个个执行。
综上,aop原理即为将连接点JoinPoint传给对应的拦截器Interceptor拦截,并且可以在拦截前后做出额外的‘处理’。AOP不会对原来的代码进行更改,而是通过拦截的方式,在执行前后插入其他的处理,运行结果可能被改变,以达到想要的结果。
此外,再补一下java.lang.reflect包