国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

徒手擼框架--實現Aop

weij / 3467人閱讀

摘要:只實現了基于方法的攔截器。實現了一個遞歸的調用,直到執行完所有的攔截器。目標對象攔截器列表這個就是我們框架能夠理解的數據結構,這個時候問題就變成了對于哪個目標,增加哪些攔截器。

原文地址:犀利豆的博客

上一講我們講解了Spring 的 IoC 實現。大家可以去我的博客查看點擊鏈接,這一講我們繼續說說 Spring 的另外一個重要特性 AOP。之前在看過的大部分教程,對于Spring Aop的實現講解的都不太透徹,大部分文章介紹了Spring Aop的底層技術使用了動態代理,至于Spring Aop的具體實現都語焉不詳。這類文章看以后以后,我腦子里浮現的就是這樣一個畫面:

我的想法就是,帶領大家,首先梳理 Spring Aop的實現,然后屏蔽細節,自己實現一個Aop框架。加深對Spring Aop的理解。在了解上圖1-4步驟的同時,補充 4 到 5 步驟之間的其他細節。

讀完這篇文章你將會了解:

Aop是什么?

為什么要使用Aop?

Spirng 實現Aop的思路是什么

自己根據Spring 思想實現一個 Aop框架

Aop 是什么?

面向切面的程序設計(aspect-oriented programming,AOP)。通過預編譯方式和運行期動態代理實現程序功能統一維護的一種技術。

為什么需要使用Aop?

面向切面編程,實際上就是通過預編譯或者動態代理技術在不修改源代碼的情況下給原來的程序統一添加功能的一種技術。我們看幾個關鍵詞,第一個是“動態代理技術”,這個就是Spring Aop實現底層技術。第二個“不修改源代碼”,這個就是Aop最關鍵的地方,也就是我們平時所說的非入侵性。。第三個“添加功能”,不改變原有的源代碼,為程序添加功能。

舉個例子:如果某天你需要統計若干方法的執行時間,如果不是用Aop技術,你要做的就是為每一個方法開始的時候獲取一個開始時間,在方法結束的時候獲取結束時間。二者之差就是方法的執行時間。如果對每一個需要統計的方法都做如上的操作,那代碼簡直就是災難。如果我們使用Aop技術,在不修改代碼的情況下,添加一個統計方法執行時間的切面。代碼就變得十分優雅。具體這個切面怎么實現?看完下面的文章你一定就會知道。

Spring Aop 是怎么實現的?

所謂:

計算機程序 = 數據結構 + 算法

在閱讀過Spring源碼之后,你就會對這個說法理解更深入了。

Spring Aop實現的代碼非常非常的繞。也就是說 Spring 為了靈活做了非常深層次的抽象。同時 Spring為了兼容 @AspectJ 的Aop協議,使用了很多 Adapter (適配器)模式又進一步的增加了代碼的復雜程度。
Spring 的 Aop 實現主要以下幾個步驟:

初始化 Aop 容器。

讀取配置文件。

將配置文件裝換為 Aop 能夠識別的數據結構 -- Advisor。這里展開講一講這個advisor。Advisor對象中包又含了兩個重要的數據結構,一個是 Advice,一個是 PointcutAdvice的作用就是描述一個切面的行為,pointcut描述的是切面的位置。兩個數據結的組合就是”在哪里,干什么“。這樣 Advisor 就包含了”在哪里干什么“的信息,就能夠全面的描述切面了。

Spring 將這個 Advisor 轉換成自己能夠識別的數據結構 -- AdvicedSupport。Spirng 動態的將這些方法攔截器織入到對應的方法。

生成動態代理代理。

提供調用,在使用的時候,調用方調用的就是代理方法。也就是已經織入了增強方法的方法。

自己實現一個 Aop 框架

同樣,我也是參考了Aop的設計。只實現了基于方法的攔截器。去除了很多的實現細節。

使用上一講的 IoC 框架管理對象。使用 Cglib 作為動態代理的基礎類。使用 maven 管理 jar 包和 module。所以上一講的 IoC 框架會作為一個 modules 引入項目。

下面我們就來實現我們的Aop 框架吧。

首先來看看代碼的基本結構。

代碼結構比上一講的 IoC 復雜不少。我們首先對包每個包都干了什么做一個簡單介紹。

invocation 描述的就是一個方法的調用。注意這里指的是“方法的調用”,而不是調用這個動作。

interceptor 大家最熟悉的攔截器,攔截器攔截的目標就是 invcation 包里面的調用。

advisor 這個包里的對象,都是用來描述切面的數據結構。

adapter 這個包里面是一些適配器方法。對于"適配器"不了解的同學可以去看看"設計模式"里面的"適配模式"。他的作用就是將 advice 包里的對象適配為 interceptor

bean 描述我們 json 配置文件的對象。

core 我們框架的核心邏輯。

這個時候宏觀的看我們大概梳理出了一條路線, adaperadvisor 適配為 interceptor 去攔截 invoction

下面我們從這個鏈條的最末端講起:

invcation

首先 MethodInvocation 作為所有方法調用的接口。要描述一個方法的調用包含三個方法,獲取方法本身getMethod,獲取方法的參數getArguments,還有執行方法本身proceed()

public interface MethodInvocation {
    Method getMethod();
    Object[] getArguments();
    Object proceed() throws Throwable;
}

ProxyMethodInvocation 看名字就知道,是代理方法的調用,增加了一個獲取代理的方法。

public interface ProxyMethodInvocation extends MethodInvocation {
    Object getProxy();
}
interceptor

AopMethodInterceptor 是 Aop 容器所有攔截器都要實現的接口:

public interface AopMethodInterceptor {
    Object invoke(MethodInvocation mi) throws Throwable;
}

同時我們實現了兩種攔截器BeforeMethodAdviceInterceptorAfterRunningAdviceInterceptor,顧名思義前者就是在方法執行以前攔截,后者就在方法運行結束以后攔截:

public class BeforeMethodAdviceInterceptor implements AopMethodInterceptor {
    private BeforeMethodAdvice advice;
    public BeforeMethodAdviceInterceptor(BeforeMethodAdvice advice) {
        this.advice = advice;
    }
    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        advice.before(mi.getMethod(),mi.getArguments(),mi);
        return mi.proceed();
    }
}
public class AfterRunningAdviceInterceptor implements AopMethodInterceptor {
    private AfterRunningAdvice advice;

    public AfterRunningAdviceInterceptor(AfterRunningAdvice advice) {
        this.advice = advice;
    }

    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        Object returnVal = mi.proceed();
        advice.after(returnVal,mi.getMethod(),mi.getArguments(),mi);
        return returnVal;
    }
}

看了上面的代碼我們發現,實際上 mi.proceed()才是執行原有的方法。而advice我們上文就說過,是描述增強的方法”干什么“的數據結構,所以對于這個before攔截器,我們就把advice對應的增強方法放在了真正執行的方法前面。而對于after攔截器而言,就放在了真正執行的方法后面。

這個時候我們過頭來看最關鍵的 ReflectioveMethodeInvocation

public class ReflectioveMethodeInvocation implements ProxyMethodInvocation {
    public ReflectioveMethodeInvocation(Object proxy, Object target, Method method, Object[] arguments, List interceptorList) {
        this.proxy = proxy;
        this.target = target;
        this.method = method;
        this.arguments = arguments;
        this.interceptorList = interceptorList;
    }

    protected final Object proxy;

    protected final Object target;

    protected final Method method;

    protected Object[] arguments = new Object[0];

    //存儲所有的攔截器
    protected final List interceptorList;

    private int currentInterceptorIndex = -1;

    @Override
    public Object getProxy() {
        return proxy;
    }

    @Override
    public Method getMethod() {
        return method;
    }

    @Override
    public Object[] getArguments() {
        return arguments;
    }

    @Override
    public Object proceed() throws Throwable {

        //執行完所有的攔截器后,執行目標方法
        if(currentInterceptorIndex == this.interceptorList.size() - 1) {
            return invokeOriginal();
        }

        //迭代的執行攔截器。回顧上面的講解,我們實現的攔擊都會執行 im.proceed() 實際上又會調用這個方法。實現了一個遞歸的調用,直到執行完所有的攔截器。
        AopMethodInterceptor interceptor = interceptorList.get(++currentInterceptorIndex);
        return interceptor.invoke(this);

    }

    protected Object invokeOriginal() throws Throwable{
        return ReflectionUtils.invokeMethodUseReflection(target,method,arguments);
    }

}


在實際的運用中,我們的方法很可能被多個方法的攔截器所增強。所以我們,使用了一個list來保存所有的攔截器。所以我們需要遞歸的去增加攔截器。當處理完了所有的攔截器之后,才會真正調用調用被增強的方法。我們可以認為,前文所述的動態的織入代碼就發生在這里。

public class CglibMethodInvocation extends ReflectioveMethodeInvocation {

    private MethodProxy methodProxy;

    public CglibMethodInvocation(Object proxy, Object target, Method method, Object[] arguments, List interceptorList, MethodProxy methodProxy) {
        super(proxy, target, method, arguments, interceptorList);
        this.methodProxy = methodProxy;
    }

    @Override
    protected Object invokeOriginal() throws Throwable {
        return methodProxy.invoke(target,arguments);
    }
}

CglibMethodInvocation 只是重寫了 invokeOriginal 方法。使用代理類來調用被增強的方法。

advisor

這個包里面都是一些描述切面的數據結構,我們講解兩個重要的。

@Data
public class Advisor {
    //干什么
    private Advice advice;
    //在哪里
    private Pointcut pointcut;

}

如上文所說,advisor 描述了在哪里,干什么。

@Data
public class AdvisedSupport extends Advisor {
    //目標對象
    private TargetSource targetSource;
    //攔截器列表
    private List list = new LinkedList<>();

    public void addAopMethodInterceptor(AopMethodInterceptor interceptor){
        list.add(interceptor);
    }

    public void addAopMethodInterceptors(List interceptors){
        list.addAll(interceptors);
    }

}

這個AdvisedSupport就是 我們Aop框架能夠理解的數據結構,這個時候問題就變成了--對于哪個目標,增加哪些攔截器。

core

有了上面的準備,我們就開始講解核心邏輯了。

@Data
public class CglibAopProxy implements AopProxy{
    private AdvisedSupport advised;
    private Object[] constructorArgs;
    private Class[] constructorArgTypes;
    public CglibAopProxy(AdvisedSupport config){
        this.advised = config;
    }

    @Override
    public Object getProxy() {
        return getProxy(null);
    }
    @Override
    public Object getProxy(ClassLoader classLoader) {
        Class rootClass = advised.getTargetSource().getTagetClass();
        if(classLoader == null){
            classLoader = ClassUtils.getDefultClassLoader();
        }
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(rootClass.getSuperclass());
        //增加攔截器的核心方法
        Callback callbacks = getCallBack(advised);
        enhancer.setCallback(callbacks);
        enhancer.setClassLoader(classLoader);
        if(constructorArgs != null && constructorArgs.length > 0){
            return enhancer.create(constructorArgTypes,constructorArgs);
        }
        return enhancer.create();
    }
    private Callback getCallBack(AdvisedSupport advised) {
        return new DynamicAdvisedIcnterceptor(advised.getList(),advised.getTargetSource());
    }
}

CglibAopProxy就是我們代理對象生成的核心方法。使用 cglib 生成代理類。我們可以與之前ioc框架的代碼。比較發現區別就在于:

    Callback callbacks = getCallBack(advised);
    enhancer.setCallback(callbacks);

callback與之前不同了,而是寫了一個getCallback()的方法,我們就來看看 getCallback 里面的 DynamicAdvisedIcnterceptor到底干了啥。

篇幅問題,這里不會介紹 cglib 的使用,對于callback的作用,不理解的同學需要自行學習。

public class DynamicAdvisedInterceptor implements MethodInterceptor{

    protected final List interceptorList;
    protected final TargetSource targetSource;

    public DynamicAdvisedInterceptor(List interceptorList, TargetSource targetSource) {
        this.interceptorList = interceptorList;
        this.targetSource = targetSource;
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        MethodInvocation invocation = new CglibMethodInvocation(obj,targetSource.getTagetObject(),method, args,interceptorList,proxy);
        return invocation.proceed();
    }
}

這里需要注意,DynamicAdvisedInterceptor這個類實現的 MethodInterceptor 是 gclib的接口,并非我們之前的 AopMethodInterceptor。

我們近距離觀察 intercept 這個方法我們看到:

MethodInvocation invocation = new CglibMethodInvocation(obj,targetSource.getTagetObject(),method, args,interceptorList,proxy);

通過這行代碼,我們的整個邏輯終于連起來了。也就是這個動態的攔截器,把我們通過 CglibMethodInvocation 織入了增強代碼的方法,委托給了 cglib 來生成代理對象。

至此我們的 Aop 的核心功能就實現了。

AopBeanFactoryImpl
public class AopBeanFactoryImpl extends BeanFactoryImpl{

    private static final ConcurrentHashMap aopBeanDefinitionMap = new ConcurrentHashMap<>();

    private static final ConcurrentHashMap aopBeanMap = new ConcurrentHashMap<>();

    @Override
    public Object getBean(String name) throws Exception {
        Object aopBean = aopBeanMap.get(name);

        if(aopBean != null){
            return aopBean;
        }
        if(aopBeanDefinitionMap.containsKey(name)){
            AopBeanDefinition aopBeanDefinition = aopBeanDefinitionMap.get(name);
            AdvisedSupport advisedSupport = getAdvisedSupport(aopBeanDefinition);
            aopBean = new CglibAopProxy(advisedSupport).getProxy();
            aopBeanMap.put(name,aopBean);
            return aopBean;
        }

        return super.getBean(name);
    }
    protected void registerBean(String name, AopBeanDefinition aopBeanDefinition){
        aopBeanDefinitionMap.put(name,aopBeanDefinition);
    }

    private AdvisedSupport getAdvisedSupport(AopBeanDefinition aopBeanDefinition) throws Exception {

        AdvisedSupport advisedSupport = new AdvisedSupport();
        List interceptorNames = aopBeanDefinition.getInterceptorNames();
        if(interceptorNames != null && !interceptorNames.isEmpty()){
            for (String interceptorName : interceptorNames) {

                Advice advice = (Advice) getBean(interceptorName);

                Advisor advisor = new Advisor();
                advisor.setAdvice(advice);

                if(advice instanceof BeforeMethodAdvice){
                    AopMethodInterceptor interceptor = BeforeMethodAdviceAdapter.getInstants().getInterceptor(advisor);
                    advisedSupport.addAopMethodInterceptor(interceptor);
                }

                if(advice instanceof AfterRunningAdvice){
                    AopMethodInterceptor interceptor = AfterRunningAdviceAdapter.getInstants().getInterceptor(advisor);
                    advisedSupport.addAopMethodInterceptor(interceptor);
                }

            }
        }

        TargetSource targetSource = new TargetSource();
        Object object = getBean(aopBeanDefinition.getTarget());
        targetSource.setTagetClass(object.getClass());
        targetSource.setTagetObject(object);
        advisedSupport.setTargetSource(targetSource);
        return advisedSupport;

    }

}

AopBeanFactoryImpl是我們產生代理對象的工廠類,繼承了上一講我們實現的 IoC 容器的BeanFactoryImpl。重寫了 getBean方法,如果是一個切面代理類,我們使用Aop框架生成代理類,如果是普通的對象,我們就用原來的IoC容器進行依賴注入。
getAdvisedSupport就是獲取 Aop 框架認識的數據結構。

剩下沒有講到的類都比較簡單,大家看源碼就行。與核心邏輯無關。

寫個方法測試一下

我們需要統計一個方法的執行時間。面對這個需求我們怎么做?

public class StartTimeBeforeMethod implements BeforeMethodAdvice{
    @Override
    public void before(Method method, Object[] args, Object target) {
        long startTime = System.currentTimeMillis();
        System.out.println("開始計時");
        ThreadLocalUtils.set(startTime);
    }
}
public class EndTimeAfterMethod implements AfterRunningAdvice {
    @Override
    public Object after(Object returnVal, Method method, Object[] args, Object target) {
        long endTime = System.currentTimeMillis();
        long startTime = ThreadLocalUtils.get();
        ThreadLocalUtils.remove();
        System.out.println("方法耗時:" + (endTime - startTime) + "ms");
        return returnVal;
    }
}

方法開始前,記錄時間,保存到 ThredLocal里面,方法結束記錄時間,打印時間差。完成統計。

目標類:

public class TestService {
    public void testMethod() throws InterruptedException {
        System.out.println("this is a test method");
        Thread.sleep(1000);
    }
}

配置文件:

[
  {
    "name":"beforeMethod",
    "className":"com.xilidou.framework.aop.test.StartTimeBeforeMethod"
  },
  {
    "name":"afterMethod",
    "className":"com.xilidou.framework.aop.test.EndTimeAfterMethod"
  },
  {
    "name":"testService",
    "className":"com.xilidou.framework.aop.test.TestService"
  },
  {
    "name":"testServiceProxy",
    "className":"com.xilidou.framework.aop.core.ProxyFactoryBean",
    "target":"testService",
    "interceptorNames":[
      "beforeMethod",
      "afterMethod"
    ]
  }
]

測試類:

public class MainTest {
    public static void main(String[] args) throws Exception {
        AopApplictionContext aopApplictionContext = new AopApplictionContext("application.json");
        aopApplictionContext.init();
        TestService testService = (TestService) aopApplictionContext.getBean("testServiceProxy");
        testService.testMethod();
    }
}

最終我們的執行結果:

開始計時
this is a test method
方法耗時:1015ms

Process finished with exit code 0

至此 Aop 框架完成。

后記

Spring 的兩大核心特性 IoC 與 Aop 兩大特性就講解完了,希望大家通過我寫的兩篇文章能夠深入理解兩個特性。

Spring的源碼實在是復雜,閱讀起來常常給人極大的挫敗感,但是只要能夠堅持,并采用一些行之有效的方法。還是能夠理解Spring的代碼。并且從中汲取營養。

下一篇文章,我會給大家講講閱讀開源代碼的一些方法和我自己的體會,敬請期待。

最后

github:https://github.com/diaozxin00...

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/68353.html

相關文章

  • 徒手框架--高并發環境下的請求合并

    摘要:我們就可以將這些請求合并,達到一定數量我們統一提交。總結一個比較生動的例子給大家講解了一些多線程的具體運用。學習多線程應該多思考多動手,才會有比較好的效果。地址徒手擼框架系列文章地址徒手擼框架實現徒手擼框架實現 原文地址:https://www.xilidou.com/2018/01/22/merge-request/ 在高并發系統中,我們經常遇到這樣的需求:系統產生大量的請求,但是這...

    劉東 評論0 收藏0
  • 徒手框架--實現IoC

    摘要:從而能夠進一步深入了解框架。至此我們框架開發完成。雖然說閱讀源碼是了解框架的最終手段。但是框架作為一個生產框架,為了保證通用和穩定,源碼必定是高度抽象,且處理大量細節。下一篇文章應該會是徒手擼框架實現。 原文地址:https://www.xilidou.com/2018/... Spring 作為 J2ee 開發事實上的標準,是每個Java開發人員都需要了解的框架。但是Spring 的...

    rottengeek 評論0 收藏0
  • JAVA 中的 CAS

    摘要:我們繼續看代碼的意思是這個是一段內嵌匯編代碼。也就是在語言中使用匯編代碼。就是匯編版的比較并交換。就是保證在多線程情況下,不阻塞線程的填充和消費。微觀上看匯編的是實現操作系統級別的原子操作的基石。 原文地址:https://www.xilidou.com/2018/02/01/java-cas/ CAS 是現代操作系統,解決并發問題的一個重要手段,最近在看 eureka 的源碼的時候。...

    CocoaChina 評論0 收藏0
  • 徒手一個簡單的RPC框架

    摘要:徒手擼一個簡單的框架之前在牛逼哄哄的框架,底層到底什么原理得知了遠程過程調用簡單來說就是調用遠程的服務就像調用本地方法一樣,其中用到的知識有序列化和反序列化動態代理網絡傳輸動態加載反射這些知識點。 徒手擼一個簡單的RPC框架 之前在牛逼哄哄的 RPC 框架,底層到底什么原理得知了RPC(遠程過程調用)簡單來說就是調用遠程的服務就像調用本地方法一樣,其中用到的知識有序列化和反序列化、動態...

    Gemini 評論0 收藏0
  • 徒手一個 Spring Boot 中的 Starter ,解密自動化配置黑魔法!

    摘要:先來看代碼吧,一會松哥再慢慢解釋關于這一段自動配置,解釋如下首先注解表明這是一個配置類。本文的案例,松哥已經上傳到上了,地址。我們使用 Spring Boot,基本上都是沉醉在它 Stater 的方便之中。Starter 為我們帶來了眾多的自動化配置,有了這些自動化配置,我們可以不費吹灰之力就能搭建一個生產級開發環境,有的小伙伴會覺得這個 Starter 好神奇呀!其實 Starter 也都...

    xiaochao 評論0 收藏0

發表評論

0條評論

weij

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<