Spring AOP實現原理(五)手寫一個Spring AOP框架

前言(可跳過)

在開始正文以前,先聊點其它的,本來規劃的《Spring AOP實現原理》系列的最後一章節是講解Spring AOP源碼的。剛開始對此也是信心滿滿的,直到我深刻讀了源碼以後才發現這事情沒有那麼簡單。java

首先,Spring AOP源碼有些多,不夠精簡,這就給書面講解形成很大麻煩。其次,徹底基於Spring AOP源碼講解它的實現彷佛也沒有太大意義。git

所以我決定另闢蹊徑,從Spring AOP的特性和功能發起,而後結合着Spring AOP實現的思路,大體實現一個Spring AOP的架子。github

特別聲明:在實現的過程當中,因爲篇幅緣由,砍掉了很多優化部分,特別是有關工廠,懶加載,緩存,併發等。spring

功能拆分

從上一章節中,咱們得知了Spring AOP的特性以及其要完成功能,咱們抽取出其中的重點列舉一下:express

  • 支持註解和XML兩種配置方式
  • 可以與Spring IoC結合
  • 支持JDK Dynamic Proxy和CGLIB兩種代理方式
  • 集成AspectJ的Annotation註解,包括切面(@Aspect)、切點(@Pointcut)、通知(@Advice)等
  • 在動態代理中實現對Advice的調用

咱們針對這些特性作一個功能分析,大體有以下功能:設計模式

  • AOP,主要專一於基於方法的AOP功能實現,分爲三個方面:緩存

    • 集成AspectJ,包括集成表達式,使用AspectJ定義的Annotation,以及使用AspectJ提供的工具;
    • 抽象層,包括了Spring對AOP概念的封裝和抽象,重點理解一下Advisor,這是Spring獨有的概念;
    • 實現層,自定義方法攔截器MethodInterceptor實現對AOP目標方法以及Advice方法的調用
  • 對象代理,支持JDK和CGLIB兩種代理方式,實現根據目標對象(target)生成對應的代理對象併發

  • 配置解析,支持XML和Annotation兩種配置方式的解析,實現根據配置解析出對應的Advisor等ide

  • IoC集成,集成BeanFactory,實現對IoC容器中bean的訪問;實現BeanPostProcesser,將配置解析、建立代理對象等歸入到bean初始化流程工具

注:咱們看下官網對Advisor的解釋:*

An advisor is like a small self-contained aspect that has a single piece of advice.

*Advisor是具備單個Advice以及可使用Pointcut的組合類,能夠看做是一個特殊的Aspect。

在列出了Spring AOP功能以後,咱們接下來討論下功能實現的流程

流程拆分

我把流程簡單分爲兩大部分,建立代理階段和代理調用階段,其中前者是由Spring IoC初始化觸發的,後者是由程序調用觸發的,詳細流程參考下圖:

建立代理階段

  1. Spring IoC容器觸發Bean初始化,經過BeanPostProcesser接口

  2. 調用BeanPostProcesser接口實現方法postProcessAfterInitialization

  3. 進入構建Advisor的流程,經過反射找到全部匹配的Advisor

  4. 篩選出符合的Advisor

  5. 進入建立代理的流程,將上一個流程中獲得的Advisor集合傳遞給代理對象,而且根據規則判斷使用哪一種代理方式

代理調用階段

  1. 外部的方法調用。以日誌打印切面舉例,當調用日誌切面中的方法時,會觸發代理的調用
  2. 調用回調方法,至關於將目標方法的調用委託給了回調方法。在生成代理對象時,都有對應的回調,好比,CGLIB中設置CallBack,JDK中設置InvocationHandler
  3. 在回調方法中,構建方法攔截器鏈(後面會針對攔截器鏈重點講解)
  4. 進一步將調用權委託給攔截器鏈,由攔截器鏈完成執行

實現部分

在列出了Spring AOP功能以後,咱們接下來討論功能的實現部分功能的實現

AOP抽象概念定義

首先對AspectJ中的概念抽象,咱們簡單定義下Aspect、JoinPoint、Advice、Pointcut等類。

public class Aspect {
}
public class JoinPoint {
}
public interface Pointcut {
    public String getExpression();
}
public class Advice {
    private Method adviceMethod;
    private Aspect aspect;
}
複製代碼

SpringAOP中引入了Advisor的概念,咱們同時定義一個Advisor類

public class Advisor {
    private Advice advice;
    private Pointcut pointcut;
    private Aspect aspect;
}
複製代碼

爲了融合AspectJ的表達式,咱們針對Pointcut進一步改造

增長字符串表達式轉換爲AspectJ的表達式(PointcutExpression)

import org.aspectj.weaver.tools.PointcutExpression;
	//....

	/** * 轉換爲AspectJ的切入點表達式 * @return */
    public PointcutExpression buildPointcutExpression();
複製代碼

引入AspectJ解析類(PointcutParser),實現buildPointcutExpression方法

import org.aspectj.weaver.tools.PointcutExpression;
import org.aspectj.weaver.tools.PointcutParser;

public class AspectJPointcut implements Pointcut {

    public String expression;

    public AspectJPointcut(String expression) {
        this.expression = expression;
    }

    @Override
    public String getExpression() {
        return this.expression;
    }
	
    
    @Override
    public PointcutExpression buildPointcutExpression() {
        PointcutParser parser = PointcutParser
      .getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution();
        return parser.parsePointcutExpression(this.expression);
    }
    
}
複製代碼

以上,定義了幾個基本類。有的小夥伴會說,爲何沒有看到BeforeAdvice這些定義呢?這裏先賣個關子,等到後面引入方法攔截器的時候再定義。

建立代理階段

IoC集成

集成自己比較簡單,實現接口BeanPostProcessor和BeanFactoryAware,直接上代碼

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanPostProcessor;

public abstract class AbstractAOPProxyCreator implements BeanPostProcessor, BeanFactoryAware {
    
    //子類可實現
    protected void initBeanFactory(BeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }
    
    //獲取匹配的Advisor
    protected abstract List<Advisor> getMatchedAdvisors();

    //建立代理對象
    protected abstract Object createProxy(List<Advisor> advisors, Object bean);

    @Override
    public void setBeanFactory(BeanFactory arg0) throws BeansException {
        
    }
    
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName){
		return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName){
		return bean;
    }

}
複製代碼

實現了BeanFactoryAware接口的setBeanFactory方法,以及BeanPostProcessor接口的postProcessAfterInitialization方法和postProcessBeforeInitialization方法。

下面咱們引入模板方法設計模式,來制定處理流程:

public Object postProcessAfterInitialization(Object bean, String beanName){
        //構建全部Advisor
        List<Advisor> advisors = buildAdvisors();
        //獲取符合的Advisor
        advisors = this.getMatchedAdvisors();
        //根據獲取的Advisor生成代理對象
        Object object = createProxy(advisors,bean);
        //返回代理對象
		return object == null ? bean : object;
    }
複製代碼

解析配置

解析配置主要就是用到了反射,找到被@Aspect標記的類,進而找到@Advice,@Pointcut等,最終將這些組合成Advisor實例,實現起來並不複雜,再也不贅述

public class AnnotationParser implements ConfigParser {
	//避免重複構建,增長了緩存
    private final Map<String, List<Advisor>> cache = new ConcurrentHashMap<>();

    @Override
    public List<Advisor> parse() {
        if(cache != null) {
            return getAdvisorsFromCache();
        } 
		//獲取全部被@Aspect註解的類
        List<Class> allClasses = getAllAspectClasses();
        for (Class class1 : allClasses) {
            cache.putIfAbsent(class1.getName(), getAdvisorsByAspect(class1));
        }
        
        return getAdvisorsFromCache();
    }
    
    /** * 根據Aspect類生成Advisor類 * @param class1 * @return */
    private List<Advisor> getAdvisorsByAspect(Class class1) {
        List<Advisor> advisors = new ArrayList<>();
        for (Method method : getAdvisorMethods(class1)) {
            Advisor advisor = getAdvisor(method, class1.newInstance());
            advisors.add(advisor);
        }
        return advisors;
    }
}
複製代碼

篩選符合的Advisor

咱們要從全部的Advisor中過濾出來與代理目標Bean相關的,以Bean的方法和Advisor的Pointcut做爲過濾條件,這裏利用了AspectJ的表達式以及比對工具

import org.aspectj.weaver.tools.ShadowMatch;
	
	/** * 從全部的Advisor中獲取匹配的 * @param advisors * @return */
    public static List<Advisor> getMatchedAdvisors(Class cls, List<Advisor> advisors) {
        List<Advisor> aList = new ArrayList<>();
        for (Method method : cls.getDeclaredMethods()) {
            for (Advisor advisor : advisors) {
                ShadowMatch match = advisor.getPointcut()
                                    .buildPointcutExpression()
                                    .matchesMethodExecution(method);
                if(match.alwaysMatches()) {
                    aList.add(advisor);
                }
            }
        }
        return aList;
    }
複製代碼

建立代理對象

咱們定義了一個工廠,代理對象轉交給由工廠建立

public class AOPProxyFactory {

    public Object getProxyObject(List<Advisor> advisors, Object bean) {
        if(isInterface()) {
           return new CglibProxyImpl(advisors,bean).getProxyObject();
        } else {
            return new JdkDynamicProxyImpl(advisors,bean).getProxyObject();
        }
    }

    private boolean isInterface() {
        return false;
    }
    
}
複製代碼

同時也實現了兩種代理方式,JDK Dynamic Proxy和CGLIB,下面逐一講解下

注:能夠先忽略掉方法攔截器鏈

JDK 實現方式,須要實現InvocationHandler接口,而且在接口方法invoke中實現方法攔截器鏈的調用

public class JdkDynamicProxyImpl extends AOPProxy implements InvocationHandler {

    public JdkDynamicProxyImpl(List<Advisor> advisors, Object bean) {
        super(advisors, bean);
    }

    @Override
    protected Object getProxyObject() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), ReflectHelper.getInterfaces(this.getTarget().getClass()), this);
    }


    /** * 實現InvocationHandler的接口方法,將具體的調用委託給攔截器鏈MethodInterceptorChain */
	@Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        
        MyMethodInterceptor[] iterceptors = 
            AdvisorHelper.getMethodInterceptors(this.getAdvisors(), method);
        
        Object obj = new MethodInterceptorChain(iterceptors)
                        .intercept(method,args,proxy);
        return obj;
    }

}
複製代碼

CGLIB是經過回調(Callback)實現的,須要實現CGLIB的MethodInterceptor

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class CglibProxyImpl extends AOPProxy {

    public CglibProxyImpl(List<Advisor> advisors, Object bean) {
        super(advisors, bean);
    }

    @Override
    protected Object getProxyObject() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.getTarget().getClass());
        enhancer.setCallback(new AOPInterceptor());
        return enhancer.create();
    }

    /** * 實現cglib的攔截器,在intercept中將攔截器調用委託給攔截器鏈MethodInterceptorChain */
    private class AOPInterceptor implements MethodInterceptor {

        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            MyMethodInterceptor[] iterceptors = AdvisorHelper.getMethodInterceptors(CglibProxyImpl.this.getAdvisors(), method);
            
            Object o = new MethodInterceptorChain(iterceptors)
                .intercept(method, args, obj);
            return o;
        }
    }
}
複製代碼

以上,基本上實現了建立代理對象的流程,那麼咱們思考一個問題

在調用代理方法的時候,是如何實現調用咱們定義的Advice的呢?

思考:Advice方法調用的實現

簡單實現示例

咱們先用一種簡單的實現方式說明一下,以JDK代理方式爲例:

@Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return obj;
    }
複製代碼

咱們知道,執行代理對象的任何方法都會進入到invoke裏(對動態代理還不清楚的同窗能夠回看第三章動態代理的實現),那麼進入到invoke裏面以後咱們須要作以下判斷:

  1. 找到該代理相關的全部的Advisor(這個不難,代理類有advisors屬性)
  2. 遍歷Advisor集合,逐一判斷是否匹配。判斷規則爲method和pointcut(這裏一樣能夠採用AspectJ工具類)
  3. 找出匹配的Advisor,進一步找出Advice,而後執行Advice

咱們以BeforeAdvice爲例,展現一下如何實現Advice調用

@Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        BeforeAdvice beforeAdvice = getBeforeAdvice(method);
		beforeAdvice.before(proxy, method, args);//調用Advice處
        return method.invoke(this.bean, args);
    }
	
	
	/** * 以返回BeforeAdvice爲例 * @return */
    private BeforeAdvice getBeforeAdvice(Method method) {
        for (Advisor advisor : this.getAdvisors()) {
            if(AdvisorHelper.isMatch(advisor, method) 
               && advisor.getAdvice() instanceof BeforeAdvice) {
                return (BeforeAdvice) advisor.getAdvice();
            }
        }
        return null;
    }
複製代碼

那麼,問題來了,若是咱們獲取到匹配的Advice中還有AfterAdvice呢?咱們向invoke方法中增長代碼

@Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        BeforeAdvice beforeAdvice = getBeforeAdvice(method);
		beforeAdvice.before(proxy, method, args);//調用Advice處
        
        Object o = method.invoke(this.bean, args);
        
        AfterAdvice afterAdvice = getAfterAdvice(method);
		afterAdvice.after(proxy, method, args);//調用Advice處
        return o;
    }
複製代碼

那麼,若是咱們得到到兩個或者多個相同類型的Advice呢?而且相同類型的Advice間有執行順序需求。上面這種簡單實現就沒法知足了,咱們須要引入方法攔截器鏈

職責鏈設計模式

這裏作一個簡單的擴展,不少攔截器(interceptor),過濾器(filter)的實現都是基於職責鏈模式實現的,在定義方法攔截器鏈以前,咱們先看看Tomcat是如何實現過濾器(filter)的。

注:確切來講是,Tomcat基於JavaEE標準實現的

Java Sevlet接口

public interface FilterChain {
    public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException;

}

public interface Filter {
    public void init(FilterConfig filterConfig) throws ServletException;

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException;

    public void destroy();
}
複製代碼

Tomcat過濾器鏈實現

public final class ApplicationFilterChain implements FilterChain {
    
    private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];
    
    void addFilter(ApplicationFilterConfig filterConfig) {
        //..
    }
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
        // Call the next filter if there is one
        //C-1
        if (pos < n) {
            ApplicationFilterConfig filterConfig = filters[pos++];
            Filter filter = filterConfig.getFilter();
			filter.doFilter(request, response, this);
            return;
        }

        // We fell off the end of the chain -- call the servlet instance
        servlet.service(request, response);
        
    }
}
複製代碼

Session初始化過濾器

public class SessionInitializerFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        ((HttpServletRequest)request).getSession();
        //C-2
        chain.doFilter(request, response);
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // NO-OP
    }

    @Override
    public void destroy() {
        // NO-OP
    }
}
複製代碼

以上實現有幾個關鍵點:

  • ApplicationFilterChain控制着職責鏈的執行,這裏也能夠實現對元素的排序或者規則匹配等,參考C-1代碼段
  • ApplicationFilterChain經過遞歸調用實現了鏈式調用
  • 每一個Filter均可以註冊/添加到執行鏈當中,待本身執行完以後,再將執行轉交給ApplicationFilterChain(chain.doFilter)

實現方法攔截器鏈

咱們引入職責鏈模式,將Advice抽象成一個MethodInterceptor。這對功能實現上有以下好處:

  • 能夠動態添加Advice
  • 能夠針對Advice進行排序,規則匹配等
  • 各個Advice能夠單獨實現本身的業務邏輯(例如BeforeAdvice),後續也很容易擴展新的Advice

定義攔截器,爲了和CGLIB的攔截器區分開,咱們命名爲MyMethodInterceptor

public interface MyMethodInterceptor {

    public Object intercept(Method method, Object[] arguments, Object target, MethodInterceptorChain chain);
    
}
複製代碼

定義BeforeAdvice和AfterAdvice

public class BeforeAdvice extends Advice implements MyMethodInterceptor {
    public BeforeAdvice(Method adviceMethod, Aspect aspect) {
        super(adviceMethod, aspect);
    }

    public void before(final Object target, final Method method, final Object[] args) {
        this.invokeAspectMethod(target, method, args);
        ;
    }

    @Override
    public Object intercept(Method method, Object[] arguments, Object target, MethodInterceptorChain chain) {
        this.before(target, method, arguments);
        return chain.intercept(method, arguments, target);
    }
}

public class AfterAdvice extends Advice implements MyMethodInterceptor {
    
    public AfterAdvice(Method adviceMethod, Aspect aspect) {
        super(adviceMethod, aspect);
    }

    public void after(final Object target, final Method method, final Object[] args) {
        this.invokeAspectMethod(target, method, args);
    }

    @Override
    public Object intercept(Method method, Object[] arguments, Object target, MethodInterceptorChain chain) {
        Object obj = chain.intercept(method, arguments, target);
        this.after(target, method, arguments);
        return obj;
    }
}
複製代碼

實現方法攔截器鏈

public class MethodInterceptorChain {

    private MyMethodInterceptor[] methodInterceptors;

    public MethodInterceptorChain(MyMethodInterceptor[] methodInterceptors) {
        this.methodInterceptors = methodInterceptors;
    }

    private int index = 0;

    public Object intercept(Method method, Object[] arguments, Object target) {
        if (index == methodInterceptors.length) {
            // call method
            return method.invoke(target, arguments);
        } else {
            return methodInterceptors[index++]
                	.intercept(method, arguments, target, this);
        }
        return null;
    }
}
複製代碼

那麼回到咱們最開始的思考題,咱們能夠把本來簡單的實現替換成MethodInterceptorChain,以下:

@Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        MyMethodInterceptor[] iterceptors = 
            AdvisorHelper.getMethodInterceptors(this.getAdvisors(), method);
        
        Object obj = new MethodInterceptorChain(iterceptors)
                        .intercept(method,args,proxy);
        return obj;
    }
複製代碼

這樣,咱們就將代理方法的調用轉移到了MethodInterceptorChain

最後,這裏面還隱藏一個小問題,就是代理對象中的Advisor是全部和這個類相關的,咱們仍然須要根據method和pointcut找到與方法相匹配的攔截器,這和前面篩選Advisor的實現是同樣的,都是基於AspectJ具

最後

在講完方法攔截器鏈以後,代理調用的流程也就清晰了,也就再也不贅述。

咱們本次實現僅僅是基於AspectJ的Annotation配置,Spring AOP同時也支持基於Schema配置。時間與篇幅緣由,就再也不作深刻探討。

除了本文重點提到的職責鏈模式,Spring AOP還運用了大量的工廠模式、模板方法模式、適配器模式等。特別是大量使用工廠(好比Aspect工廠,Advisor工廠等)同時配合Spring IoC的狀況下,可以支持類和對象(Aspect、Advisor等)強大的管理,包括了加載策略,好比單例,多例,懶加載等。筆者認爲,這些值得你們深刻學習和研究的。

附錄

代碼地址:github.com/wanghe9011/…

相關文章
相關標籤/搜索