有關AOP相關概念以及Spring AOP相關概念和Spring AOP的使用不再重復。關于AOP在Spring中的地位,不用說相信我們都知道,也都會用,但是對于更深入的東西,還未接觸過,這里就對Spring AOP的相關源碼進行說明一下,看看到底Spring中AOP是怎么實現的。
有關AOP的概念和Spring AOP相關配置,請參考其他兩篇文章:AOP概念,原理,應用介紹 和 Spring中AOP的配置從1.0到5.0的演進
另外,本文使用的源碼是Spring1.1.1版本的,之所以使用這么老的版本,是覺得相對來說簡單一些,并且無關的東西更少,這樣更容易去理解。對于后續版本新增功能可以在此基礎上進行對比,理解的效果會更好。
示例程序
首先我們還是先使用一個實例來看一下怎么使用,再從實例中一步一步跟進到源碼中。
先定義業務接口和實現:
LoginService:
package me.cxis.spring.aop;
/**
* Created by cheng.xi on 2017-03-29 12:02.
*/
public interface LoginService {
String login(String userName);
}
LoginServiceImpl:
package me.cxis.spring.aop;
/**
* Created by cheng.xi on 2017-03-29 10:36.
*/
public class LoginServiceImpl implements LoginService {
public String login(String userName){
System.out.println("正在登錄");
return "success";
}
}
接著是三個通知類:
//這里只是在登錄方法調用之前打印一句話
public class LogBeforeLogin implements MethodBeforeAdvice {
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("有人要登錄了。。。");
}
}
package me.cxis.spring.aop.proxyfactory;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
/**
* Created by cheng.xi on 2017-03-29 10:56.
*/
public class LogAfterLogin implements AfterReturningAdvice {
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("有人已經登錄了。。。");
}
}
package me.cxis.spring.aop.proxyfactory;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
/**
* Created by cheng.xi on 2017-03-30 23:36.
*/
public class LogAroundLogin implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("有人要登錄。。。");
Object result = invocation.proceed();
System.out.println("登錄完了");
return result;
}
}
測試方法:
package me.cxis.spring.aop.proxyfactory;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Created by cheng.xi on 2017-03-29 10:34.
*/
public class Main {
public static void main(String[] args) {
ProxyFactory proxyFactory = new ProxyFactory();//創建代理工廠
proxyFactory.setTarget(new LoginServiceImpl());//設置目標對象
proxyFactory.addAdvice(new LogBeforeLogin());//前置增強
proxyFactory.addAdvice(new LogAfterLogin());//后置增強
//proxyFactory.addAdvice(new LogAroundLogin());//環繞增強
LoginService loginService = (LoginService) proxyFactory.getProxy();//從代理工廠中獲取代理
loginService.login("x");
}
}
關于實例中要說明的:我們看到在使用的時候,直接獲取的是一個代理,不是要使用的實現類,這也很好懂,之前文章都說過AOP其實就是代理模式,在編譯期或者運行期,給我們原來的代碼增加一些功能,變成一個代理。當我們調用的時候,實際就是調用的代理類。
源碼解析
對于源碼的解析,我們這里使用的是代碼的方式,沒有選擇xml配置文件的方式。關于xml配置的方式,后面再講解。
創建AOP代理
首先我們要明白,Spring中實現AOP,就是生成一個代理,然后在使用的時候調用代理。
創建代理工廠
代碼中首先創建一個代理工廠實例ProxyFactory proxyFactory = new ProxyFactory();
代理工廠的作用就是使用編程的方式創建AOP代理。ProxyFactory繼承自AdvisedSupport,AdvicedSupport是AOP代理的配置管理器。
設置目標對象
然后是設置要代理的目標對象proxyFactory.setTarget(new LoginServiceImpl());
,看下setTarget方法:
public void setTarget(Object target) {
//先根據給定的目標實現類,創建一個單例的TargetSource
//然后設置TargetSource
setTargetSource(new SingletonTargetSource(target));
}
TargetSource
TargetSource用來獲取當前的Target,也就是TargetSource中會保存著我們的的實現類。
public interface TargetSource {
//返回目標類的類型
Class getTargetClass();
//查看TargetSource是否是static的
//靜態的TargetSource每次都返回同一個Target
boolean isStatic();
//獲取目標類的實例
Object getTarget() throws Exception;
//釋放目標類
void releaseTarget(Object target) throws Exception;
}
SingletonTargetSource
TargetSource的默認實現,是一個單例的TargetSource,isStatic方法直接返回true。
public final class SingletonTargetSource implements TargetSource, Serializable {
//用來保存目標類
private final Object target;
//構造方法
public SingletonTargetSource(Object target) {
this.target = target;
}
//直接返回目標類的類型
public Class getTargetClass() {
return target.getClass();
}
//返回目標類
public Object getTarget() {
return this.target;
}
//釋放目標類,這里啥也沒做
public void releaseTarget(Object o) {
// Nothing to do
}
//直接返回true
public boolean isStatic() {
return true;
}
//equals方法
public boolean equals(Object other) {
//相等,返回true
if (this == other) {
return true;
}
//不是SingletonTargetSource類型的返回false
if (!(other instanceof SingletonTargetSource)) {
return false;
}
SingletonTargetSource otherTargetSource = (SingletonTargetSource) other;
//判斷目標類是否相等
return ObjectUtils.nullSafeEquals(this.target, otherTargetSource.target);
}
//toString方法
public String toString() {
return "SingletonTargetSource: target=(" + target + ")";
}
}
上面是有關TargetSource和SingletonTargetSource的說明,接著往下一步就是設置目標類setTargetSource方法,在AdvisedSupport類中:
public void setTargetSource(TargetSource targetSource) {
if (isActive() && getOptimize()) {
throw new AopConfigException("Can't change target with an optimized CGLIB proxy: it has its own target");
}
//么有做什么處理,只是將我們構建的TargetSource緩存起來
this.targetSource = targetSource;
}
添加通知
上面設置了要代理的目標類之后,接著是添加通知,也就是添加增強類,proxyFactory.addAdvice()
方法是添加增強類的方法。我們在例子中是這么使用的:
proxyFactory.addAdvice(new LogBeforeLogin());//前置增強
proxyFactory.addAdvice(new LogAfterLogin());//后置增強
//proxyFactory.addAdvice(new LogAroundLogin());//環繞增強
addAdvice方法的參數是一個Advice類型的類,也就是通知或者叫增強,可以去我們的增強類中查看,我們都繼承了各種Advice,比如MethodBeforeAdvice
,AfterReturningAdvice
,MethodInterceptor
,這里先講一下有關通知Advice的代碼,然后再繼續說明addAdvice方法。
Advice接口
Advice不屬于Spring,是AOP聯盟定義的接口。Advice接口并沒有定義任何方法,是一個空的接口,用來做標記,實現了此接口的的類是一個通知類。Advice有幾個子接口:
- BeforeAdvice,前置增強,意思是在我們的目標類之前調用的增強。這個接口也沒有定義任何方法。
- AfterReturningAdvice,方法正常返回前的增強,該增強可以看到方法的返回值,但是不能更改返回值,該接口有一個方法afterReturning
- ThrowsAdvice,拋出異常時候的增強,也是一個標志接口,沒有定義任何方法。
- Interceptor,攔截器,也沒有定義任何方法,表示一個通用的攔截器。不屬于Spring,是AOP聯盟定義的接口
- DynamicIntroductionAdvice,動態引介增強,有一個方法implementsInterface。
MethodBeforeAdvice
MethodBeforeAdvice接口,是BeforeAdvice的子接口,表示在方法前調用的增強,方法前置增強不能阻止方法的調用,但是能拋異常來使目標方法不繼續執行。
public interface MethodBeforeAdvice extends BeforeAdvice {
//在給定的方法調用前,調用該方法
//參數method是被代理的方法
//參數args是被代理方法的參數
//參數target是方法調用的目標,可能為null
void before(Method m, Object[] args, Object target) throws Throwable;
}
MethodInterceptor
MethodInterceptor不屬于Spring,是AOP聯盟定義的接口,是Interceptor的子接口,我們通常叫做環繞增強。
public interface MethodInterceptor extends Interceptor {
//在目標方法調用前后做一些事情
//返回的是invocation.proceed()方法的返回值
Object invoke(MethodInvocation invocation) throws Throwable;
}
參數MethodInvocation是一個方法調用的連接點,接下來先看看MethodInvocation相關的代碼。
Joinpoint連接點
JointPoint接口,是一個通用的運行時連接點,運行時連接點是在一個靜態連接點發生的事件。
public interface Joinpoint {
//開始調用攔截器鏈中的下一個攔截器
Object proceed() throws Throwable;
//
Object getThis();
//
AccessibleObject getStaticPart();
}
Invocation接口
Invocation接口是Joinpoint的子接口,表示程序的調用,一個Invocation就是一個連接點,可以被攔截器攔截。
public interface Invocation extends Joinpoint {
//獲取參數
Object[] getArguments();
}
MethodInvocation接口
MethodInvocation接口是Invocation的子接口,用來描述一個方法的調用。
public interface MethodInvocation extends Invocation
{
//獲取被調用的方法
Method getMethod();
}
另外還有一個ConstructorInvocation接口,也是Invocation的子接口,描述的是構造器的調用。
上面介紹完了Advice的相關定義,接著看往代理工廠中添加增強的addAdvice方法,addAdvice方法在AdvisedSupport類中:
public void addAdvice(Advice advice) throws AopConfigException {
//advisors是Advice列表,是一個LinkedList
//如果被添加進來的是一個Interceptor,會先被包裝成一個Advice
//添加之前現獲取advisor的大小,當做添加的Advice的位置
int pos = (this.advisors != null) ? this.advisors.size() : 0;
//添加Advice
addAdvice(pos, advice);
}
接著看addAdvice(pos, advice)方法:
public void addAdvice(int pos, Advice advice) throws AopConfigException {
//只能處理實現了AOP聯盟的接口的攔截器
if (advice instanceof Interceptor && !(advice instanceof MethodInterceptor)) {
throw new AopConfigException(getClass().getName() + " only handles AOP Alliance MethodInterceptors");
}
//IntroductionInfo接口類型,表示引介信息
if (advice instanceof IntroductionInfo) {
//不需要IntroductionAdvisor
addAdvisor(pos, new DefaultIntroductionAdvisor(advice, (IntroductionInfo) advice));
}
//動態引介增強的處理
else if (advice instanceof DynamicIntroductionAdvice) {
//需要IntroductionAdvisor
throw new AopConfigException("DynamicIntroductionAdvice may only be added as part of IntroductionAdvisor");
}
else {
//添加增強器,需要先把我們的增強包裝成增強器,然后添加
addAdvisor(pos, new DefaultPointcutAdvisor(advice));
}
}
我們看到添加增強的時候,實際調用添加增強器這個方法,首先需要把我們的Advice包裝成一個PointCutAdvisor,然后在添加增強器。這里先了解一下有關PointCutAdvisor的相關信息。
Advisor接口
Advisor,增強器,它持有一個增強Advice,還持有一個過濾器,來決定Advice可以用在哪里。
public interface Advisor {
//判斷Advice是不是每個實例中都有
boolean isPerInstance();
//返回持有的Advice
Advice getAdvice();
}
PointcutAdvisor
是一個持有Pointcut切點的增強器,PointcutAdvisor現在就會持有一個Advice和一個Pointcut。
public interface PointcutAdvisor extends Advisor {
//獲取Pointcut
Pointcut getPointcut();
}
Pointcut接口
切入點,定義了哪些連接點需要被織入橫切邏輯。可以
public interface Pointcut {
//類過濾器,可以知道哪些類需要攔截
ClassFilter getClassFilter();
//方法匹配器,可以知道哪些方法需要攔截
MethodMatcher getMethodMatcher();
// could add getFieldMatcher() without breaking most existing code
Pointcut TRUE = TruePointcut.INSTANCE;
}
ClassFilter接口
public interface ClassFilter {
//判斷給定的類是不是要攔截
boolean matches(Class clazz);
ClassFilter TRUE = TrueClassFilter.INSTANCE;
}
MethodMatcher接口
public interface MethodMatcher {
/ 靜態方法匹配
boolean matches(Method m, Class targetClass);
//是否是運行時動態匹配
boolean isRuntime();
//運行是動態匹配
boolean matches(Method m, Class targetClass, Object[] args);
MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
}
看完相關的定義之后,接著看方法new DefaultPointcutAdvisor(advice),將Advice包裝成一個DefaultPointcutAdvisor。其實就是將advice和默認的Pointcut包裝進DefaultPointcutAdvisor。
DefaultPointcutAdvisor是Advisor的最常用的一個實現,可以使用任意類型的Pointcut和Advice,但是不能使用Introduction。
構造完成了DefaultPointcutAdvisor只有,接著就是添加增強器方法addAdvisor:
public void addAdvisor(int pos, Advisor advisor) throws AopConfigException {
//引介增強器處理
if (advisor instanceof IntroductionAdvisor) {
addAdvisor(pos, (IntroductionAdvisor) advisor);
}
else {
//其他的增強器處理
addAdvisorInternal(pos, advisor);
}
}
首先看下非引介增強器的添加方法addAdvisorInternal:
private void addAdvisorInternal(int pos, Advisor advice) throws AopConfigException {
if (isFrozen()) {
throw new AopConfigException("Cannot add advisor: config is frozen");
}
//把Advice添加到LinkedList中指定位置
this.advisors.add(pos, advice);
//同時更新一下Advisors數組
updateAdvisorArray();
//通知監聽器
adviceChanged();
}
然后看下關于引介增強器的添加addAdvisor,我們知道引介就是對目標類增加新的接口,所以引介增強,也就是對接口的處理:
public void addAdvisor(int pos, IntroductionAdvisor advisor) throws AopConfigException {
//對接口進行校驗
advisor.validateInterfaces();
// 遍歷要添加的接口,添加
for (int i = 0; i < advisor.getInterfaces().length; i++) {
//就是添加到interfaces集合中,interfaces是一個HashSet
addInterface(advisor.getInterfaces()[i]);
}
//然后添加到advisors中
addAdvisorInternal(pos, advisor);
}
對于添加增強的步驟,就是把我們的增強器添加進代理工廠中,保存在一個LinkedList中,順序是添加進來的順序。
獲取代理
到目前為止,我們看到的都還是在組裝代理工廠,并沒有看到代理的生成,接下來proxyFactory.getProxy()
這一步就是獲取代理的過程,我們繼續看ProxyFactory的getProxy方法:
public Object getProxy() {
//創建一個AOP代理
AopProxy proxy = createAopProxy();
//返回代理
return proxy.getProxy();
}
我們知道一般創建代理會有兩種方式,一種是JDK動態代理,另外一種是CGLIB動態代理,而這里的創建AOP代理就是生成這兩種代理中的一種。先看createAopProxy()方法,在AdvisedSupport類中:
protected synchronized AopProxy createAopProxy() {
if (!this.isActive) {
activate();
}
//獲取AOP代理工廠,然后創建代理
return getAopProxyFactory().createAopProxy(this);
}
獲取代理工廠這一步,這里就是默認獲取一個DefaultAopProxyFactory實例,然后調用createAopProxy創建AOP代理:
public AopProxy createAopProxy(AdvisedSupport advisedSupport) throws AopConfigException {
//對于指定了使用CGLIB方式,或者代理的是類,或者代理的不是接口,就使用CGLIB的方式來創建代理
boolean useCglib = advisedSupport.getOptimize() || advisedSupport.getProxyTargetClass() || advisedSupport.getProxiedInterfaces().length == 0;
if (useCglib) {
return CglibProxyFactory.createCglibProxy(advisedSupport);
}
else {
//使用JDK動態代理來創建代理
return new JdkDynamicAopProxy(advisedSupport);
}
}
獲取完AOP代理之后返回,然后就是調用getProxy方法獲取代理,這里分為CGLIB的獲取方式和JDK動態代理的獲取方式兩種。
JDK動態代理方式獲取代理
JDK動態代理方式獲取代理,實現在JdkDynamicAopProxy中:
public Object getProxy() {
return getProxy(Thread.currentThread().getContextClassLoader());
}
public Object getProxy(ClassLoader cl) {
//JDK動態代理只能代理接口類型,先獲取接口
//就是從AdvisedSupport中獲取保存在interfaces中的接口
Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advisedSupport);
//使用Java的反射機制創建一個代理實例
return Proxy.newProxyInstance(cl, proxiedInterfaces, this);
}
關于JDK反射創建代理之類的,這里不做解析。
CGLIB方式獲取代理
CGLIB獲取方式,實現在Cglib2AopProxy中:
public Object getProxy() {
//使用CGLIB的方式來獲取,CGLIB這里不做解析
return getProxy(Thread.currentThread().getContextClassLoader());
}
使用代理
上面獲取代理之后,就剩最后一步,使用,當我們調用業務方法的時候,實際上是調用代理中的方法,對于CGLIB生成的代理,調用的是DynamicAdvisedInterceptor的intercept方法;JDK動態代理生成的代理是調用invoke方法。
JDK動態代理
看下JDK動態代理的方式,對于方法的調用,實際上調用的是代理類的invoke方法,在JdkDynamicAopProxy中:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation invocation = null;
Object oldProxy = null;
boolean setProxyContext = false;
//代理的目標對象
TargetSource targetSource = advisedSupport.targetSource;
Class targetClass = null;
Object target = null;
try {
//equals方法
if (method.getDeclaringClass() == Object.class && "equals".equals(method.getName())) {
return equals(args[0]) ? Boolean.TRUE : Boolean.FALSE;
}
else if (Advised.class == method.getDeclaringClass()) {
//???
return AopProxyUtils.invokeJoinpointUsingReflection(this.advisedSupport, method, args);
}
Object retVal = null;
//代理目標對象
target = targetSource.getTarget();
if (target != null) {
targetClass = target.getClass();
}
//???
if (this.advisedSupport.exposeProxy) {
// Make invocation available if necessary
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
//獲取配置的通知Advicelian
List chain = this.advisedSupport.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
this.advisedSupport, proxy, method, targetClass);
//沒有配置通知
if (chain.isEmpty()) {
//直接調用目標對象的方法
retVal = AopProxyUtils.invokeJoinpointUsingReflection(target, method, args);
}
else {
//配置了通知,創建一個MethodInvocation
invocation = new ReflectiveMethodInvocation(proxy, target,
method, args, targetClass, chain);
//執行通知鏈,沿著通知器鏈調用所有的通知
retVal = invocation.proceed();
}
//返回值
if (retVal != null && retVal == target) {
//返回值為自己
retVal = proxy;
}
//返回
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
}
if (setProxyContext) {
AopContext.setCurrentProxy(oldProxy);
}
}
}
CGLIB動態代理
CGLIB的是調用DynamicAdvisedInterceptor的intercept方法對目標對象進行處理,具體暫先不解析。
使用ProxyFactoryBean創建AOP代理
ProxyFactoryBean對Pointcut和Advice提供了完全的控制,還包括應用的順序。ProxyFactoryBean的getObject方法會返回一個AOP代理,包裝了目標對象。
Spring在初始化的過程中,createBean的時候,如果是FactoryBean的話,會調用((BeanFactoryAware)bean).setBeanFactory(this);
:
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
//創建通知器鏈
this.createAdvisorChain();
if(this.singleton) {
//刷新目標對象
this.targetSource = this.freshTargetSource();
//獲取單例實例
this.getSingletonInstance();
this.addListener(this);
}
}
看下獲取單例實例的方法:
private Object getSingletonInstance() {
if(this.singletonInstance == null) {
this.singletonInstance = this.createAopProxy().getProxy();
}
return this.singletonInstance;
}
createAopProxy方法在AdvisedSupport類中,下面創建的流程跟上面解析的都一樣了。
到這里AOP的一個流程的源碼算是走完了,這只是其中一小部分,還有很多的沒有涉及到,包括AOP標簽的解析,CGLIB生成代理以及調用代理等等。其中有些還沒明白的已經畫上了問號,慢慢的在研究下。