SpringAop也不難

AOP像OOP一樣,是一種獨立于語言的編程范式抠刺,實現AOP協議的方式多種多樣,包括:運行時摘昌、編譯器植入速妖、代理等,而SpringAop的采用的是動態(tài)代理與Cglib靜態(tài)植入聪黎。

添加依賴

IOC容器的基礎上添加Aop相關依賴买优。

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.9</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
</dependency>

編程式Aop演示

ProxyFactory生成代理類是SpringAop底層真正的實現

目標類與接口

interface RunIntf {
    void work();
    void work1();
}
static class SimpleRun implements RunIntf {
    @Override
    public void work() {
        System.out.println("SimpleRun.work()");
    }
    @Override
    public void work1() {
        System.out.println("SimpleRun.work1()");
    }
}

通知

static class SimpleAdvice implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("SimpleAdvice.before");
        return invocation.proceed();
    }
}

切面

static class SimplePointCutAdvisor extends AbstractPointcutAdvisor {
    @Override
    public Advice getAdvice() {
        return new SimpleAdvice();
    }

    @Override
    public Pointcut getPointcut() {
        JdkRegexpMethodPointcut jdkRegexpMethodPointcut = new JdkRegexpMethodPointcut();
        jdkRegexpMethodPointcut.setPattern(".*work1.*");
        return jdkRegexpMethodPointcut;
    }
}

運行

public static void main(String[] args) {
    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.addAdvisors(new SimplePointCutAdvisor());
    proxyFactory.setTarget(new SimpleRun());
    proxyFactory.setInterfaces(RunIntf.class);
    RunIntf runIntf = (RunIntf) proxyFactory.getProxy();
    runIntf.work();
    runIntf.work1();
}

結果

SimpleRun.work()
SimpleAdvice.before
SimpleRun.work1()

可以看到,work1方法被植入了切面邏輯挺举。由此可見杀赢,我們通過提供目標類、目標類接口湘纵、切面邏輯脂崔,通過ProxyFactory生成了帶有切面功能的代理類。

AOP概念:

  • Aspect:切面梧喷,包括切入點和切面砌左,SimplePointCutAdvisor就是一個切面脖咐。
  • Advice:通知,指的是切面提供的處理邏輯汇歹,例如上例中的SimpleAdvice屁擅。
  • Pointcut:切入點,指的是如何定位切面的手段产弹,在Spring中指的是切面表達式派歌。

分析

ProxyFactory是Spring中用來生成代理類的工廠,包裝了生成代理類的細節(jié)痰哨,包括:Cglib方式和Jdk動態(tài)代理方式胶果。

接下來我們說一說通過JDK動態(tài)代理方式生成代理類的JdkDynamicAopProxy,而這是建立在熟悉Jdk動態(tài)代理基礎上的:動態(tài)代理系列(二)JDK動態(tài)代理

調用切面通知鏈

執(zhí)行切面邏輯斤斧,很簡單早抠,如下圖:

說白了,依次執(zhí)行切面邏輯撬讽,最后執(zhí)行目標類方法蕊连。

如果每個切面方法都返回布爾類型,我們可以順序遍歷切面邏輯游昼,根據返回值來判斷是否繼續(xù)傳遞咪奖,這是鏈式處理比較常見的方式,實現也比較簡單酱床,比較容易想到。

但Spring并不是使用的這種方式趟佃,切面方法也并沒有返回布爾類型扇谣。

那么Spring是如何執(zhí)行切面邏輯的呢?

在代理類中闲昭,通過把目標類和切面包裝為MethodInvocation對象罐寨,切面方法的入參正好也是這個對象。
代理類被調用時序矩,會先執(zhí)行MethodInvocation.proceed()鸯绿,執(zhí)行切面邏輯,而切面是否繼續(xù)傳遞就看是否繼續(xù)執(zhí)行MethodInvocation.proceed()方法簸淀,實現如下:

public class SimpleMethodInvocation implements MethodInvocation {
    Object target;
    List<MethodInterceptor> list;
    int i = 0;
    @Override
    public Object proceed() throws Throwable {
        if (i != list.size()) {
            list.get(i++).invoke(this); //調用切面邏輯
        } else {
            return 反射調用(target);
        }
        return null;
    }
}

基于IOC的Aop

先通過例子展示基于IOC的Aop的實現

業(yè)務類

package com.esy.stu.aop;
import org.springframework.stereotype.Component;

@Component
public class Car {
    public void work() {
        System.out.println("Car動了");
    }
}

切面邏輯

package com.esy.stu.aop;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class SimpleAspact {
    @Before("within(com.esy.stu.aop.Car)")
    public void t1() {
        System.out.println("before");
    }
}

啟動類

package com.esy.stu.aop;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@EnableAspectJAutoProxy
@ComponentScan(basePackages = "com.esy.stu.aop")
public class Boot {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Boot.class);
        applicationContext.getBean(Car.class).work();
    }
}

執(zhí)行結果:

before
Car動了

分析

從上面可以發(fā)現瓶蝴,與一般IOC容器啟動相比較,多了一個使用@Aspect標記的切面邏輯的類租幕,并且配置類上面也多了一個注解EnableAspectJAutoProxy舷手,先分析下這個配置注解。

@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {

EnableAspectJAutoProxy注解其實是引入AspectJAutoProxyRegistrar注冊代理類創(chuàng)建器劲绪,如下圖:

它Aop的關鍵男窟,這張圖也把AnnotationAwareAspectJAutoProxyCreator分為兩個部分:

  1. BeanPostProcessor(AOP就是對業(yè)務類進行代理盆赤,是由Bean處理器來觸發(fā)的)
  2. ProxyCreator(代理類生成工廠)

創(chuàng)建代理

如果了解IOC實例化過程,就會知道歉眷,Bean完全初始化之前可以作為半成品被使用牺六,并且能夠被其他bean注入。由此可以推測汗捡,在引用加入半成品之前淑际,對象就已經被代理對象所替換了,不然其他bean注入就該是目標類了凉唐。

下面是核心的源碼:

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
      throws BeanCreationException {
   // 反射生成Bean
   BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
   
//這里將完成AOP替換庸追,將目標類的引用替換成代理類,并存儲到半成品容器中
      addSingletonFactory(beanName, new ObjectFactory<Object>() {
         @Override
         public Object getObject() throws BeansException {
            return getEarlyBeanReference(beanName, mbd, bean);
         }
      });
   
   // 裝配Bean
   populateBean(beanName, mbd, instanceWrapper);
   return exposedObject;
}

getEarlyBeanReference方法中調用組件台囱,其中就有AbstractAutoProxyCreator#getEarlyBeanReference
使用的ProxyFactory生成代理類淡溯。

關鍵點

找到切面聲明

BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors
遍歷所有的bean定義,找到Aspect注解標注的類

獲取advice

通過Around簿训、Before咱娶、After、AfterReturning强品、AfterThrowing這幾種不同的注解包裝方法成為不同的通知對象膘侮。

注解 通知對象
Around AspectJAroundAdvice
Before AspectJMethodBeforeAdvice
After AspectJAfterAdvice
AfterReturning AspectJAfterReturningAdvice
AfterThrowing AspectJAfterThrowingAdvice

如果研究就會發(fā)現AspectJMethodBeforeAdvice、AspectJAfterReturningAdvice的榛、AspectJAfterThrowingAdvice并不滿足MethodInterceptor接口琼了,需要進一部包裝成AfterReturningAdviceAdapter、MethodBeforeAdviceAdapter夫晌、ThrowsAdviceAdapter雕薪。

構造Advisor

ReflectiveAspectJAdvisorFactory#getAdvisors
包裝通知和切面表達式為InstantiationModelAwarePointcutAdvisorImpl。

可以說整個Aop的核心就是ProxyFactory晓淀,而IOC容器提供的功能就是構造advice和pointcut所袁。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市凶掰,隨后出現的幾起案子燥爷,更是在濱河造成了極大的恐慌,老刑警劉巖懦窘,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件前翎,死亡現場離奇詭異,居然都是意外死亡畅涂,警方通過查閱死者的電腦和手機鱼填,發(fā)現死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來毅戈,“玉大人苹丸,你說我怎么就攤上這事愤惰。” “怎么了赘理?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵宦言,是天一觀的道長。 經常有香客問我商模,道長奠旺,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任施流,我火速辦了婚禮响疚,結果婚禮上,老公的妹妹穿的比我還像新娘瞪醋。我一直安慰自己忿晕,他們只是感情好,可當我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布银受。 她就那樣靜靜地躺著践盼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪宾巍。 梳的紋絲不亂的頭發(fā)上咕幻,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天,我揣著相機與錄音顶霞,去河邊找鬼肄程。 笑死,一個胖子當著我的面吹牛选浑,可吹牛的內容都是我干的蓝厌。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼鲜侥,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了诸典?” 一聲冷哼從身側響起描函,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎狐粱,沒想到半個月后舀寓,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡肌蜻,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年互墓,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蒋搜。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡篡撵,死狀恐怖判莉,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情育谬,我是刑警寧澤券盅,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站膛檀,受9級特大地震影響锰镀,放射性物質發(fā)生泄漏。R本人自食惡果不足惜咖刃,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一泳炉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧嚎杨,春花似錦花鹅、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至自脯,卻和暖如春之景,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背膏潮。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工锻狗, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人焕参。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓轻纪,卻偏偏與公主長得像,于是被迫代替她去往敵國和親叠纷。 傳聞我的和親對象是個殘疾皇子刻帚,可洞房花燭夜當晚...
    茶點故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內容

  • 本文是我自己在秋招復習時的讀書筆記,整理的知識點涩嚣,也是為了防止忘記崇众,尊重勞動成果,轉載注明出處哦航厚!如果你也喜歡顷歌,那...
    波波波先森閱讀 12,294評論 6 86
  • 1.1 spring IoC容器和beans的簡介 Spring 框架的最核心基礎的功能是IoC(控制反轉)容器,...
    simoscode閱讀 6,717評論 2 22
  • 1.1 Spring IoC容器和bean簡介 本章介紹了Spring Framework實現的控制反轉(IoC)...
    起名真是難閱讀 2,584評論 0 8
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理幔睬,服務發(fā)現眯漩,斷路器,智...
    卡卡羅2017閱讀 134,659評論 18 139
  • 這年頭摹芙,正流行一句話灼狰,說: 你心里有什么,你就能看見什么浮禾。是敖慌摺!因這梧桐風情盈电,真的想賴著不走蝴簇,你看那風很輕,很柔匆帚,...
    師榕曼溆閱讀 245評論 0 3