Spring_AOP源碼分析


title: Spring_AOP源碼分析
date: 2016-11-03 01:15:11
categories: Spring
tags: [Spring,AOP]


Spring AOP 源碼分析

零.Spring aop的使用

想要分析aop源碼。總要先知道spring aop怎么使用吧。要不然,分析個orz...

使用Spring AOP可以基于兩種方式,一種是比較方便和強大的注解方式,另一種則是中規中矩的xml配置方式。

0.1 基于注解的使用

第一步xml配置:

<?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:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
    
    <!-- 激活組件掃描功能,在包cn.ysh.studio.spring.aop及其子包下面自動掃描通過注解配置的組件 -->
    <context:component-scan base-package="cn.ysh.studio.spring.aop"/>
    <!-- 激活自動代理功能 -->
    <aop:aspectj-autoproxy proxy-target-class="true"/>
    
    <!-- 用戶服務對象 -->
    <bean id="userService" class="cn.ysh.studio.spring.aop.service.UserService" />
    
</beans>

第二步是為Aspect切面類添加注解:

@Component
//聲明這是一個切面Bean
@Aspect
public class ServiceAspect {

    private final static Log log = LogFactory.getLog(ServiceAspect.class);
    
    //配置切入點,該方法無方法體,主要為方便同類中其他方法使用此處配置的切入點
    @Pointcut("execution(* cn.ysh.studio.spring.aop.service..*(..))")
    public void aspect(){    }
    
    /*
     * 配置前置通知,使用在方法aspect()上注冊的切入點
     * 同時接受JoinPoint切入點對象,可以沒有該參數
     */
    @Before("aspect()")
    public void before(JoinPoint joinPoint){
        if(log.isInfoEnabled()){
            log.info("before " + joinPoint);
        }
    }
    
    //配置后置通知,使用在方法aspect()上注冊的切入點
    @After("aspect()")
    public void after(JoinPoint joinPoint){
        if(log.isInfoEnabled()){
            log.info("after " + joinPoint);
        }
    }
    
    //配置環繞通知,使用在方法aspect()上注冊的切入點
    @Around("aspect()")
    public void around(JoinPoint joinPoint){
        long start = System.currentTimeMillis();
        try {
            ((ProceedingJoinPoint) joinPoint).proceed();
            long end = System.currentTimeMillis();
            if(log.isInfoEnabled()){
                log.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms!");
            }
        } catch (Throwable e) {
            long end = System.currentTimeMillis();
            if(log.isInfoEnabled()){
                log.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms with exception : " + e.getMessage());
            }
        }
    }
    
    //配置后置返回通知,使用在方法aspect()上注冊的切入點
    @AfterReturning("aspect()")
    public void afterReturn(JoinPoint joinPoint){
        if(log.isInfoEnabled()){
            log.info("afterReturn " + joinPoint);
        }
    }
    
    //配置拋出異常后通知,使用在方法aspect()上注冊的切入點
    @AfterThrowing(pointcut="aspect()", throwing="ex")
    public void afterThrow(JoinPoint joinPoint, Exception ex){
        if(log.isInfoEnabled()){
            log.info("afterThrow " + joinPoint + "\t" + ex.getMessage());
        }
    }
    
}

第三步測試:

  public class Tester {

     private final static Log log = LogFactory.getLog(Tester.class);
    
     public static void main(String[] args) {
         //啟動Spring容器
         ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
         //獲取service組件
          UserService service = (UserService) context.getBean("userService");
        //以普通的方式調用UserService對象的三個方法
         User user = service.get(1L);
         service.save(user);
         try {
              service.delete(1L);
        } catch (Exception e) {
            if(log.isWarnEnabled()){
                log.warn("Delete user : " + e.getMessage());
            }
        }
    }
}

0.2 xml配置

<?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:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">


    <!-- 系統服務組件的切面Bean -->
    <bean id="serviceAspect" class="cn.ysh.studio.spring.aop.aspect.ServiceAspect"/>
    <!-- AOP配置 -->
    <aop:config>
        <!-- 聲明一個切面,并注入切面Bean,相當于@Aspect -->
        <aop:aspect id="simpleAspect" ref="serviceAspect">
            <!-- 配置一個切入點,相當于@Pointcut -->
            <aop:pointcut expression="execution(* cn.ysh.studio.spring.aop.service..*(..))" id="simplePointcut"/>
            <!-- 配置通知,相當于@Before、@After、@AfterReturn、@Around、@AfterThrowing -->
            <aop:before pointcut-ref="simplePointcut" method="before"/>
            <aop:after pointcut-ref="simplePointcut" method="after"/>
            <aop:after-returning pointcut-ref="simplePointcut" method="afterReturn"/>
            <aop:after-throwing pointcut-ref="simplePointcut" method="afterThrow" throwing="ex"/>
        </aop:aspect>
    </aop:config>

</beans>

AOP用起來還是很簡單的。就把xml配置好就算完工了。有Advisor和aspect兩種方式來完成。如果是用Advisor的話需要實現AfterReturningAdvice,MethodBeforeAdvice,ThrowsAdvice等接口。而如果用aspect的話則不用繼承或者實現其他的類,一個普通的類即可。

一.AOP介紹

軟件開發經歷了從匯編語言到高級語言和從過程化編程到面向對象編程;前者是為了提高開發效率,而后者則使用了歸納法,把具有共性的東西進行歸類并使之模塊化,達到便于維護和擴展的目的;如果說面向對象編程可以對業務需求進行很好的分解使之模塊化;那么面向切面編程AOP(Aspect-Oriented Programming)則可以對系統需求進行很好的模軟件開發經歷了從匯編語言到高級語言和從過程化編程到面向對象編程;前者是為了提高開發效率,而后者則使用了歸納法,把具有共性的東西進行歸類并使之模塊化,達到便于維護和擴展的目的;如果說面向對象編程可以對業務需求進行很好的分解使之模塊化;那么面向切面編程AOP(Aspect-Oriented Programming)則可以對系統需求進行很好的模塊組織,簡化系統需求和實現之間的對比關系,是對OOP思想的一種補充;塊組織,簡化系統需求和實現之間的對比關系,是對OOP思想的一種補充。

舉個例子來說明一下吧!現在系統中有很多的業務方法,如上傳產品信息、修改產品信息、發布公司庫等;現在需要對這些方法的執行做性能監控,看每個業務方法的執行時間;在不改變原業務代碼的基礎上,也許我們會這么做。

Offer接口:

package edu.zju.cs.lyp.Spring_aop;

public interface IOffer {
    public void postOffer();
    public void modifyOffer();
}

Offer實現:

package edu.zju.cs.lyp.Spring_aop;

public class OfferImpl implements IOffer {
    public void postOffer() {
        System.out.println("post offer");
    }

    public void modifyOffer() {
        System.out.println("modify offer");
    }
}

工具類:

package edu.zju.cs.lyp.Spring_aop;

public class PerformanceUtil {
    public static long start=0;
    public static long end=0;
    public static void startPerformance(){
        start=System.currentTimeMillis();
    }
    public static void endPerformance(){
        end=System.currentTimeMillis();
        System.out.println("method use:"+(end-start));
    }
}

Offer代理:

package edu.zju.cs.lyp.Spring_aop;

public class OfferProxy implements IOffer {

    private IOffer delegate;
    
    public OfferProxy(IOffer delegate){
        this.delegate=delegate;
    }
    public void postOffer() {
        PerformanceUtil.startPerformance();
        delegate.postOffer();
        PerformanceUtil.endPerformance();
    }

    public void modifyOffer() {
        PerformanceUtil.startPerformance();
        delegate.modifyOffer();
        PerformanceUtil.endPerformance();
    }

}

Offer測試:

package edu.zju.cs.lyp.Spring_aop;

public class TestProxy {
    public static void main(String[] args) {
        IOffer offer= new OfferProxy(new OfferImpl());
        offer.postOffer();
        offer.modifyOffer();
    }
}

輸出:

post offer
method use:1
modify offer
method use:0

上面的例子中,OfferProxy實現了IOffer,而所有的業務實現均委托給其成員offer;可以想像,這應該就是最簡單的AOP的實現了;但這種方式會存在一個問題:如果有非常多的這種業務對象需要性能監控,我們就需要寫同樣多的XyzProxy來滿足需求,這也是非常巨大的工作量。

二. 代理模式

代理模式中,存在一個稱為ProxyObject的代理對象和RealObject的真實對象,它們都實現了相同的接口;在調用的地方持有ProxyObject的實例,當調用request()方法時,ProxyObject可以在執行RealObject.request()前后做一些特定的業務,甚至不調用RealObject.request()方法。

目前實現代理模式的方式有兩種:基于JDK的動態代理和基于CGLIB字節碼的代理。

2.1 JDK動態代理

JDK動態代理,顧名思義,是基于JDK的反射(reflect)機制;在JDK中,提供了InvocationHandler這個接口。

注釋如下:

InvocationHandler is the interface implemented by the invocation handler of a proxy instance.
Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.

意思是說:該接口由被代理對象的handler所實現;當調用代理對象的方法時,該方法調用將被編碼,然后交給代理對象的invoke方法去執行。
因此上面的代碼可以改寫成如下所示:

實現:

package edu.zju.cs.lyp.Spring_aop;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyFactory implements InvocationHandler {
    private Object delegate;
    
    public Object bind(Object delegate){
        this.delegate= delegate;
        return Proxy.newProxyInstance(delegate.getClass().getClassLoader(), 
                delegate.getClass().getInterfaces(), this);
    }
    
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        PerformanceUtil.startPerformance();
        Object result=null;
        try {
            result=method.invoke(delegate, args);
        } catch (Exception e) {
            // TODO: handle exceptions
        }
        PerformanceUtil.endPerformance();
        return result;
    }
    
}

測試:

package edu.zju.cs.lyp.Spring_aop;

public class TestJDKProxy {
    public static void main(String[] args) {
        IOffer offer = (IOffer) new ProxyFactory().bind(new OfferImpl());
        offer.postOffer();
        offer.modifyOffer();
    }
}

結果:

post offer
method use:1
modify offer
method use:0

通過這種方式,你不需要為針對每一個業務寫一個代理對象,就可以很輕松地完成你的需求;但也許你已經注意到了,JDK的動態代理,在創建代理對象(上面紅色代碼部分)時,被代理的對象需要實現接口(即面向接口編程);

2.2CGLIB代理方式

如果目標對象沒有實現任何接口,那怎么辦呢?不用擔心,你可以用CGLIB來實現代理。

實現:

package edu.zju.cs.lyp.Spring_aop;

import java.lang.reflect.Method;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;


public class CglibProxyFactory  implements MethodInterceptor{
    
    private Object delegate;
    
    public Object bind(Object delegate){
        this.delegate=delegate;
        Enhancer enhancer= new Enhancer();
        enhancer.setSuperclass(delegate.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }
    public Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        PerformanceUtil.startPerformance();
        Object o =proxy.invoke(this.delegate, args);
        PerformanceUtil.endPerformance();
        return o;
    }
    
}

測試類:

package edu.zju.cs.lyp.Spring_aop;

public class TestCglibProxy {
    public static void main(String[] args) {
        DefaultOffer defaultOffer= (DefaultOffer) new CglibProxyFactory().bind(new DefaultOffer());
        defaultOffer.postOffer();
        defaultOffer.modifyOffer();
    }
}

測試結果:

post offer
method use:20
modify offer
method use:0

使用CGLIB創建的代理對象,其實就是繼承了要代理的目標類,然后對目標類中所有非final方法進行覆蓋,但在覆蓋方法時會添加一些攔截代碼(上面CglibProxyFactory類中的intercept方法)。

三. Spring AOP 實現

3.1 Spring AOP 幾個基本概念

Spring AOP jar包:spring-aop-4.2.5.release.jar

Spring AOP中的幾個基本概念,每次學習AOP都被這幾個概念折騰的很不爽,我們在這里再把這幾個概念描述一遍,力爭把這幾個概念搞清,在每次review這塊內容的時候可以很快上手。

  1. 切面(Aspect):切面就是一個關注點的模塊化,如事務管理、日志管理、權限管理等;
  2. 連接點(Joinpoint):程序執行時的某個特定的點,在Spring中就是一個方法的執行;
  3. 通知(Advice):通知就是在切面的某個連接點上執行的操作,也就是事務管理、日志管理等;
  4. 切入點(Pointcut):切入點就是描述某一類選定的連接點,也就是指定某一類要織入通知的方法;
  5. 目標對象(Target):就是被AOP動態代理的目標對象;

用一張圖來形象地表達AOP的概念及其關系如下:

3.2 Spring AOP 中切入點、通知、切面的實現

理解了上面的幾個概念后,我們分別來看看Spring AOP是如何實現這些概念的;

3.2.1.切入點(Pointcut)

它定義了哪些連接點需要被織入橫切邏輯;在Java中,連接點對應哪些類(接口)的方法。因此,我們都能猜到,所謂的切入點,就是定義了匹配哪些婁的哪些方法的一些規則,可以是靜態的基于類(方法)名的值匹配,也可以是基于正則表達式的模式匹配。

來看看Spring AOP Pointcut相關的類圖:

在Pointcut接口的定義中,也許你已經想到了,ClassFilter是類過濾器,它定義了哪些類名需要攔截;典型的兩個實現類為TypePatternClassFilter和TrueClassFilter(所有類均匹配);而MethodMatcher為方法匹配器,定義哪些方法需要攔截。

在上面的類圖中:

  • StaticMethodMatch與DynamicMethodMatch的區別是后者在運行時會依據方法的參數值進行匹配。
  • NameMatchMethodPointCut根據指定的mappedNames來匹配方法。
  • AbstractRegexpMethodPointCut根據正則表達式來匹配方法

類圖中部分代碼實現:

MethodMatcher.class:

package org.springframework.aop;

import java.lang.reflect.Method;

public abstract interface MethodMatcher {
    public static final MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;

    public abstract boolean matches(Method paramMethod, Class<?> paramClass);

    public abstract boolean isRuntime();

    public abstract boolean matches(Method paramMethod, Class<?> paramClass, Object[] paramArrayOfObject);
}

StaticMethodMatcher.class:

package org.springframework.aop.support;

import java.lang.reflect.Method;
import org.springframework.aop.MethodMatcher;

public abstract class StaticMethodMatcher implements MethodMatcher {
    public final boolean isRuntime() {
        return false;
    }

    public final boolean matches(Method method, Class<?> targetClass, Object[] args) {
        throw new UnsupportedOperationException("Illegal MethodMatcher usage");
    }
}

DynamicMethodMatcher.class:

package org.springframework.aop.support;

import java.lang.reflect.Method;
import org.springframework.aop.MethodMatcher;

public abstract class DynamicMethodMatcher implements MethodMatcher {
    public final boolean isRuntime() {
        return true;
    }

    public boolean matches(Method method, Class<?> targetClass) {
        return true;
    }
}

Pointcut.class:

package org.springframework.aop;

public abstract interface Pointcut {
    public static final Pointcut TRUE = TruePointcut.INSTANCE;

    public abstract ClassFilter getClassFilter();

    public abstract MethodMatcher getMethodMatcher();
}

3.2.2.通知(Advice)

通知定義了具體的橫切邏輯。在Spring中,存在兩種類型的Advice,即per-class和per-instance的Advice。

所謂per-class,即該類型的Advice只提供方法攔截,不會為目標對象保存任何狀態或者添加新的特性,它也是我們最常見的Advice。下面是per-class的類圖:

  • BeforeAdvice:在連接點前執行的橫切邏輯。
  • AfterReturningAdvice:在連接點執行后,再執行橫切邏輯。
  • AfterAdvice:一般由程序自己實現,當拋出異常后,執行橫切邏輯。
  • AroundAdvice:Spring AOP中并沒有提供這個接口,而是采用了AOP Alliance的MethodInteceptor接口;通過看AfterReturningAdvice的源碼我們知道,它是不能更改連接點所在方法的返回值的(更改引用);但使用的MethodInteceptor,所有的事情,都不在話下。

部分源碼介紹:

AfterAdvice,Advice兩個接口是空的

AfterReturningAdvice.class

package org.springframework.aop;

import java.lang.reflect.Method;

public abstract interface AfterReturningAdvice extends AfterAdvice {
    public abstract void afterReturning(Object paramObject1, Method paramMethod, Object[] paramArrayOfObject,
            Object paramObject2) throws Throwable;
}

MethodBeforeAdvice.class

import java.lang.reflect.Method;

public abstract interface MethodBeforeAdvice extends BeforeAdvice {
    public abstract void before(Method paramMethod, Object[] paramArrayOfObject, Object paramObject) throws Throwable;
}

MethodInterceptor.class

package org.aopalliance.intercept;

public abstract interface MethodInterceptor extends Interceptor {
    public abstract Object invoke(MethodInvocation paramMethodInvocation) throws Throwable;
}

在上面的類圖中,還有兩種類沒有介紹,那就是 ***AdviceAdapter 和 ***AdviceInteceptor.結構如圖所示


我們以AfterReturningAdviceInterceptor為例來說明:

package org.springframework.aop.framework.adapter;

import java.io.Serializable;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.AfterAdvice;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.util.Assert;

public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable {
    private final AfterReturningAdvice advice;

    public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) {
        Assert.notNull(advice, "Advice must not be null");
        this.advice = advice;
    }

    public Object invoke(MethodInvocation mi) throws Throwable {
        Object retVal = mi.proceed();
        this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
        return retVal;
    }
}

該類實現了MethodInterceptor和AfterAdvice接口,同時構造函數中還有一個AfterReturningAdvice實例的參數;這個類存在的作用是為了什么呢?Spring AOP把所有的Advice都適配成了MethodInterceptor,統一的好處是方便后面橫切邏輯的執行(參看下一節),適配的工作即由***AdviceAdapter完成;

Spring AOP所謂的AfterReturningAdvice,通過適配成MethodInterceptor后,其實就是在invoke方法中,先執行目標對象的方法,再執行的AfterReturningAdvice所定義的橫切邏輯。

對于per-instance的Advice,目前只有一種實現,就是Introduction,使用的場景比較少。

3.2.3.切面(Aspect)

在Spring中,Advisor就是切面;但與通常的Aspect不同的是,Advisor通常只有一個Pointcut和一個Advice,而Aspect則可以包含多個Pointcut和多個Advice,因此Advisor是一種特殊的Aspect。

接下來看下per-class Advisor的類圖:

繼承關系如下:

Advisor包含一個Pointcut和一個Advisor;在AbstractGenericPointcutAdvisor中,持有一個Advice的引用;下面的幾個實現,均是針對前面提到的幾種不同的Pointcut的實現。

3.3 Spring AOP實現基本線索

我們選擇ProxyFactoryBean作為入口點和分析的開始。ProxyFactoryBean是在Spring IoC環境中,創建AOP應用的最底層方法,從中,可以看到一條實現AOP的基本線索。

所有的邏輯從以下的方法開始,我們主要針對單例的代理對象的生成:

public Object getObject() throws BeansException {
    //這里初始化通知器鏈
    initializeAdvisorChain();
    if (isSingleton()) {
        //根據定義需要生成單例的proxy
        return getSingletonInstance();
    }

    if (this.targetName == null) {
        this.logger.warn(
                "Using non-singleton proxies with singleton targets is often undesirable. Enable prototype proxies by setting the 'targetName' property.");
    }
    //這里根據定義需要生成prototype類型的proxy
    return newPrototypeInstance();
}

下面我們深入到SpringAOP核心代碼的內部,看看代理對象的生成機制,攔截器橫切邏輯以及織入的實現。

private synchronized Object getSingletonInstance() {
        if (this.singletonInstance == null) {
            //返回具體的目標對象,就是被代理的對象
            this.targetSource = freshTargetSource();
            if ((this.autodetectInterfaces) && (getProxiedInterfaces().length == 0) && (!(isProxyTargetClass()))) {
                //從targetsource中獲取目標對象的class
                Class targetClass = getTargetClass();
                if (targetClass == null) {
                    throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");
                }
                //這里設置代理對象的借口
                setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
            }
            //這里使用proxyfactory來生成我們需要的proxy。
            super.setFrozen(this.freezeProxy);
            this.singletonInstance = getProxy(createAopProxy());
        }
        return this.singletonInstance;
    }

ProxyFactoryBean是AdvisedSupport的子類,Spring使用AopProxy接口把AOP代理的實現與框架的其他部分分離開來。在AdvisedSupport中通過這樣的方式來得到AopProxy,當然這里需要得到AopProxyFactory的幫助 ,從JDK或者cglib中得到想要的代理對象:

protected final synchronized AopProxy createAopProxy() {
        if (!(this.active)) {
            activate();
        }
        return getAopProxyFactory().createAopProxy(this);
    }

getAopProxyFactory()獲取ProxyCreatorSupport的屬性aopProxyFactory

public AopProxyFactory getAopProxyFactory() {
        return this.aopProxyFactory;
    }

該屬性被默認初始化為DefaultAopProxyFactory對象。

public ProxyCreatorSupport() {
    this.aopProxyFactory = new DefaultAopProxyFactory();
}
public ProxyCreatorSupport(AopProxyFactory aopProxyFactory) {
        Assert.notNull(aopProxyFactory, "AopProxyFactory must not be null");
        this.aopProxyFactory = aopProxyFactory;
    }

這個DefaultAopProxyFactory是Spring用來生成AopProxy的地方,它包含JDK和Cglib兩種實現方式。讓我接著往里面看:

/*** Eclipse Class Decompiler plugin, copyright (c) 2016 Chen Chao (cnfree2000@hotmail.com) ***/
package org.springframework.aop.framework;

import java.io.Serializable;
import java.lang.reflect.Proxy;
import org.springframework.aop.SpringProxy;

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        //做一些判斷操作。isoptimize()是指是否采取進一步的優化,true采用cglib來生成代理。
        //isproxytargetclass決定是否采用基于接口的代理。
        if ((config.isOptimize()) || (config.isProxyTargetClass()) || (hasNoUserSuppliedProxyInterfaces(config))) {
            Class targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException(
                        "TargetSource cannot determine target class: Either an interface or a target is required for proxy creation.");
            }
            //如果目標對象實現的接口,則采用jdk動態代理來生成proxy
            if ((targetClass.isInterface()) || (Proxy.isProxyClass(targetClass))) {
                return new JdkDynamicAopProxy(config);
            }
            //如果target不是接口的實現的話,返回cglib類型的aopproxy
            return new ObjenesisCglibAopProxy(config);
        }
        //不滿足最開始的判斷 直接使用jdk動態代理
        return new JdkDynamicAopProxy(config);
    }

    private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
        Class[] ifcs = config.getProxiedInterfaces();
        return ((ifcs.length == 0) || ((ifcs.length == 1) && (SpringProxy.class.isAssignableFrom(ifcs[0]))));
    }
}

可以看到其中的代理對象可以由JDK或者Cglib來生成,JdkDynamicAopProxy類和Cglib2AopProxy都實現的是AopProxy的接口,我們進入JdkDynamicAopProxy實現中看看Proxy是怎樣生成的:

public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
        Assert.notNull(config, "AdvisedSupport must not be null");
        if ((config.getAdvisors().length == 0) && (config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE)) {
            throw new AopConfigException("No advisors and no TargetSource specified");
        }
        this.advised = config;
    }

    public Object getProxy() {
        return getProxy(ClassUtils.getDefaultClassLoader());
    }

    public Object getProxy(ClassLoader classLoader) {
        if (logger.isDebugEnabled()) {
            logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
        }
        //根據advised中的配置信息,獲取proxy需要代理的接口、放入proxiedInterfaces中。
        Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
        findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
        //這里我們調用jdk proxy 來生成需要的proxy實例
        return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
    }

再來看cglib代理

class ObjenesisCglibAopProxy extends CglibAopProxy {
    private static final Log logger = LogFactory.getLog(ObjenesisCglibAopProxy.class);

    private static final SpringObjenesis objenesis = new SpringObjenesis();

    public ObjenesisCglibAopProxy(AdvisedSupport config) {
        super(config);
    }

    protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
        Class proxyClass = enhancer.createClass();
        Object proxyInstance = null;

        if (objenesis.isWorthTrying()) {
            try {
                proxyInstance = objenesis.newInstance(proxyClass, enhancer.getUseCache());
            } catch (Throwable ex) {
                logger.debug("Unable to instantiate proxy using Objenesis, falling back to regular proxy construction",
                        ex);
            }

        }

        if (proxyInstance == null) {
            try {
                proxyInstance = (this.constructorArgs != null)
                        ? proxyClass.getConstructor(this.constructorArgTypes).newInstance(this.constructorArgs)
                        : proxyClass.newInstance();
            } catch (Throwable ex) {
                throw new AopConfigException(
                        "Unable to instantiate proxy using Objenesis, and regular proxy instantiation via default constructor fails as well",
                        ex);
            }

        }

        ((Factory) proxyInstance).setCallbacks(callbacks);
        return proxyInstance;
    }
}

繼承了CglibAopProxy。下面的和最開始的例子比較,是不是很熟悉。

public Object getProxy(ClassLoader classLoader) {
        if (logger.isDebugEnabled()) {
            logger.debug("Creating CGLIB proxy: target source is " + this.advised.getTargetSource());
        }
        try {
            Class rootClass = this.advised.getTargetClass();
            Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");

            Class proxySuperClass = rootClass;
            if (ClassUtils.isCglibProxyClass(rootClass)) {
                proxySuperClass = rootClass.getSuperclass();
                Class[] additionalInterfaces = rootClass.getInterfaces();
                for (Class additionalInterface : additionalInterfaces) {
                    this.advised.addInterface(additionalInterface);
                }

            }

            validateClassIfNecessary(proxySuperClass, classLoader);

            Enhancer enhancer = createEnhancer();
            if (classLoader != null) {
                enhancer.setClassLoader(classLoader);
                if ((classLoader instanceof SmartClassLoader)
                        && (((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass))) {
                    enhancer.setUseCache(false);
                }
            }
            enhancer.setSuperclass(proxySuperClass);
            enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
            enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
            enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));

            Callback[] callbacks = getCallbacks(rootClass);
            Class[] types = new Class[callbacks.length];
            int k;
            for (int k = 0; k < types.length; ++k) {
                types[k] = callbacks[k].getClass();
            }

            enhancer.setCallbackFilter(new ProxyCallbackFilter(this.advised.getConfigurationOnlyCopy(),
                    this.fixedInterceptorMap, this.fixedInterceptorOffset));
            enhancer.setCallbackTypes(types);

            return createProxyClassAndInstance(enhancer, callbacks);
        } catch (CodeGenerationException ex) {
            throw new AopConfigException("Could not generate CGLIB subclass of class [" + this.advised.getTargetClass()
                    + "]: " + "Common causes of this problem include using a final class or a non-visible class", ex);
        } catch (IllegalArgumentException ex) {
            throw new AopConfigException("Could not generate CGLIB subclass of class [" + this.advised.getTargetClass()
                    + "]: " + "Common causes of this problem include using a final class or a non-visible class", ex);
        } catch (Exception ex) {
            throw new AopConfigException("Unexpected AOP exception", ex);
        }
    }

用Proxy包裝target之后,通過ProxyFactoryBean得到對其方法的調用就被Proxy攔截了, ProxyFactoryBean的getObject()方法得到的實際上是一個Proxy了,target對象已經被封裝了。對 ProxyFactoryBean這個工廠bean而言,其生產出來的對象是封裝了目標對象的代理對象。

3.4攔截器的作用

前面分析了SpringAOP實現中得到Proxy對象的過程,接下來我們去探尋Spring AOP中攔截器鏈是怎樣被調用的,也就是Proxy模式是怎樣起作用的。
還記得在JdkDynamicAopProxy中生成Proxy對象的時候,有一句這樣的代碼嗎?

return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);

這里我們的JdkDynamicAopProxy實現了InvocationHandler這個接口,final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable.

this參數對應的是InvocationHandler對象,也就是說當 Proxy對象的函數被調用的時候,InvocationHandler的invoke方法會被作為回調函數調用.

我們來看一下動態代理中invoke函數的實現:

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;
        try {
            Object localObject1;
            //目標對象未實現equals方法
            if ((!(this.equalsDefined)) && (AopUtils.isEqualsMethod(method))) {
                localObject1 = Boolean.valueOf(equals(args[0]));
                return localObject1;
            }
            //目標對象未實現hashcode方法
            if ((!(this.hashCodeDefined)) && (AopUtils.isHashCodeMethod(method))) {
                localObject1 = Integer.valueOf(hashCode());
                return localObject1;
            }
            //opaque順序控制生成代理對象是否可以強制轉換類型為advised,默認為false。
            //z這里針對opaque為true且代理的為借口自身,并且代理類為advised借口的子接口,不進行代理操作。
            if ((!(this.advised.opaque)) && (method.getDeclaringClass().isInterface())
                    && (method.getDeclaringClass().isAssignableFrom(Advised.class))) {
            //這里就是目標對象的調用
                localObject1 = AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
                return localObject1;
            }
            //設置exposeproxy為true,讓springaop框架將生成的當前代理對象綁定到threadlocal
            if (this.advised.exposeProxy) {
                oldProxy = AopContext.setCurrentProxy(proxy);
                setProxyContext = true;
            }
            //這里是得到目標對象,目標對象可能來自一個示例池或者一個簡單的java對象。
            target = targetSource.getTarget();
            if (target != null) {
                targetClass = target.getClass();
            }
            //重要!!!:::這里獲得定義好的攔截器鏈
            List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
            Object retVal;
            Object retVal;
            //如果沒有攔截器,直接調用目標的對象方法,不創建methodinvocation
            if (chain.isEmpty()) {
                Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
            } else {
                //如果有攔截器的設定,那么需要調用攔截器之后才能調用目標對象的相應的方法。
                //通過構造一個ReflectiveMethodInvocation來實現
                MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass,
                        chain);
                //通過ReflectiveMethodInvocation來調用攔截器連和相應的目標方法。
                //在proceed方法內部實現了自身的遞歸調用來便利整個攔截器鏈。

                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 localObject2 = retVal;

            return localObject2;
        } finally {
            if ((target != null) && (!(targetSource.isStatic()))) {
                //釋放gettarget方法獲取的target對象,和targetsource實現有關
                targetSource.releaseTarget(target);
            }
            if (setProxyContext) {
                AopContext.setCurrentProxy(oldProxy);
            }
        }
    }

上面所說的目標對象方法的調用,是通過AopUtils的方法調用,使用反射機制來對目標對象的方法進行的;

public static Object invokeJoinpointUsingReflection(Object target, Method method, Object[] args) throws Throwable {
    try {
        ReflectionUtils.makeAccessible(method);
        return method.invoke(target, args);
    } catch (InvocationTargetException ex) {
        throw ex.getTargetException();
    } catch (IllegalArgumentException ex) {
        throw new AopInvocationException("AOP configuration seems to be invalid: tried calling method [" + method
                + "] on target [" + target + "]", ex);
    } catch (IllegalAccessException ex) {
        throw new AopInvocationException("Could not access method [" + method + "]", ex);
    }
}

接下來,我們來看具體的ReflectiveMethodInvocation中proceed()方法的實現,也就是攔截器鏈的實現機制:

public Object proceed() throws Throwable {
        //重點!!!currentInterceptorIndex初始化值為-1,首先判斷長度是否為0,為0直接調用目標對象的方法。
        if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
            return invokeJoinpoint();
        }

        Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers
                .get(++this.currentInterceptorIndex);

        if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
            //匹配邏輯,只要方法匹配就調用攔截器,不匹配,跳過這個攔截器,調用下一個。
            InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
            //需要注意一點,我們這里雖然反悔了,但是匹配到的攔截器自身的invoke方法還是會調用的,
            //繼續遍歷攔截器鏈
            if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
                return dm.interceptor.invoke(this);
            }
            //沒匹配, 調用下一個攔截器,重復上面邏輯
            return proceed();
        }
        //如果是MethodInterceptor,我們調用invoke方法,主要為了兼容原始aop聯盟的東西,
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }

從上面的分析我們看到了Spring AOP攔截機制的基本實現,比如Spring怎樣得到Proxy,怎樣利用JAVA Proxy以及反射機制對用戶定義的攔截器鏈進行處理。

3.5織入的實現

在上面調用攔截器的時候,經過一系列的注冊,適配的過程以后,攔截器在攔截的時候,會調用到預置好的一個通知適配器,設置通知攔截器,這是一系列Spring設計好為通知服務的類的一個,是最終完成通知攔截和實現的地方,例如對 MethodBeforeAdviceInterceptor的實現是這樣的:

public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {
    private MethodBeforeAdvice advice;

    public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
        Assert.notNull(advice, "Advice must not be null");
        this.advice = advice;
    }

    public Object invoke(MethodInvocation mi) throws Throwable {
        this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
        //這個invoke方法是攔截器的回調方法,會在代理對象的方法被調用的時候出發回調
        return mi.proceed();
    }
}

可以看到通知適配器將advice適配成Interceptor以后,會調用advice的before方法去執行橫切邏輯。這樣就成功的完成了before通知的織入。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容