Spring學(xué)習(xí)筆記(二)后處理器與AOP

1.后處理器

后處理器是對IOC容器功能的擴(kuò)展蹂风。按我的理解辜膝,擴(kuò)展的原理是在某動作結(jié)束后再次調(diào)用指定的方法進(jìn)行擴(kuò)展處理。有點(diǎn)類似于AOP拗馒。

后處理器分兩種:Bean后處理器容器后處理器汛骂。

1.1 Bean后處理器

Bean后處理器會在Bean實(shí)例化結(jié)束后(注意罕模,該實(shí)例化應(yīng)該是指Bean類的實(shí)例化,還沒有進(jìn)行Spring中的注入等操作香缺,并不是Spring最終返回的Bean)手销,對其進(jìn)行近一步的增強(qiáng)處理,例如返回一個Bean的代理類图张。

Bean后處理器需要實(shí)現(xiàn)BeanPostProcessor接口锋拖,該接口包含的postProcessBeforeInitializationpostProcessAfterInitialization分別在Bean初始化之前和之后回調(diào)祸轮。


如上圖兽埃,增強(qiáng)處理與init-methodInitializingBean适袜,destory-method柄错,DisposableBean的執(zhí)行順序,增強(qiáng)處理永遠(yuǎn)在最外圍的苦酱。

下面給出Bean后處理器的Demo:

  • 首先實(shí)現(xiàn)創(chuàng)建一個實(shí)現(xiàn)BeanPostProcessor的后處理器類
/**
 * Bean后處理器Demo類,該處理類會對容器里面的所有Bean進(jìn)行后處理
 * @author wangsz
 */
public class BeanPostProc implements BeanPostProcessor{
    /**
     * 在Bean初始化后售貌,對容器中的bean進(jìn)行后處理,返回處理后的bean
     */
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("bean后處理器在["+beanName+"]初始化后對其進(jìn)行增強(qiáng)處理");
        if(bean instanceof Person){
            ((Person) bean).setName("akq");
        }
        //該bean可以與舊bean截然不同,如返回一個該Bean的代理類
        return bean;
    }
    /**
     * 在Bean初始化前疫萤,對容器中的bean進(jìn)行后處理颂跨,返回處理后的bean
     */
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("bean后處理器在["+beanName+"]初始化前對其進(jìn)行增強(qiáng)處理");
        //該bean可以與舊bean截然不同
        return bean;
    }

}
  • 然后在Spring配置文件中加上這個Bean。這樣扯饶,該后處理類就會對容器里面的所有Bean進(jìn)行后處理恒削。
<!--bean后處理器-->
    <bean id="beanproc" class="test.wsz.spring.postprocessor.BeanPostProc"  />

1.2 容器后處理器

Bean后處理器是對Bean實(shí)例化后進(jìn)行后處理的,而容器后處理器尾序,顧名思義钓丰,就是對Spring容器進(jìn)行后處理,通常用于Spring容器實(shí)例化Bean之前每币,讀取配置文件元數(shù)據(jù)携丁,對其進(jìn)行修改。

容器后處理器需要實(shí)現(xiàn)BeanFactoryPostProcessor接口兰怠,重寫該接口包含的postProcessBeanFactory方法则北。

Spring中已提供了幾個常用的容器后處理器:

  • PropertyPlaceholderConfigurer:屬性占位符配置器
  • PropertyOverrideConfigurer:重寫占位符配置器
  • CustomAutowireConfigurer:自定義自動裝配的配置器
  • CustomScopeConfigurer:自定義作用域的配置器

下面給出容器后處理器的Demo:

  • 首先實(shí)現(xiàn)創(chuàng)建一個實(shí)現(xiàn)BeanFactoryPostProcessor的容器后處理器類
/**
 * 容器后處理器Demo類,在容器實(shí)例化bean之前痕慢,讀取配置文件的元數(shù)據(jù)尚揣,并修改
 * @author wangsz
 */
public class BeanFactoryProc implements BeanFactoryPostProcessor{

    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("Spring的容器是:"+beanFactory);
        System.out.println("容器后處理器并沒有對BeanFactory的初始化做修改");
    }

}
  • 然后在Spring配置文件中加上這個Bean。
<!--容器后處理器-->
    <bean id="beanfactoryproc" class="test.wsz.spring.postprocessor.BeanFactoryProc"  />

2.AOP

Aspect Orient Pragramming:面向切面編程掖举。

2.1 AOP的概念

這個術(shù)語不太好理解快骗,下面我們用圖來一步步闡述它演變的過程。

現(xiàn)在有三個方法塔次,我要在里面添加同一段代碼方篮,比較low的方式,是將同一段代碼復(fù)制粘貼三遍:


改進(jìn)的方式是励负,我把這段代碼抽離到一個方法中藕溅,然后在三個方法中手動調(diào)用這個抽離方法:


但是上面的方法仍然有些不方便。如果不是三個方法继榆,是十個巾表,二十個汁掠,那一個個的在里面寫方法的調(diào)用很麻煩。而且集币,如果增加需求考阱,例如再次為方法一、二鞠苟、三增加日志打印乞榨,再次為方法一、二当娱、三增加參數(shù)檢驗吃既,那么每次都得加個抽離方法,然后在方法一二三里面加調(diào)用跨细。

AOP就是針對這些不便的進(jìn)一步優(yōu)化鹦倚。我們將方法一二三看成一個切面,然后在這個切面上進(jìn)行增強(qiáng)處理扼鞋。不需要方法一二三手動調(diào)用抽離方法申鱼,抽離方法“自動”進(jìn)行了調(diào)用:


通過上面的圖我們可以進(jìn)行一個總結(jié):AOP其實(shí)就是代理模式的一種體現(xiàn),將程序運(yùn)行過程看成一個切面云头,在切面上進(jìn)行公共的處理捐友。

2.2 AOP的應(yīng)用

現(xiàn)在版本的Spring的AOP一般都是整合的AspectJ實(shí)現(xiàn)的。AspectJ框架是最早溃槐,功能比較強(qiáng)大的AOP實(shí)現(xiàn)之一匣砖,Spring中只是用到了它部分的功能,有興趣的朋友可以百度了解一下昏滴。

值得注意的是猴鲫,AspectJ和Spring的實(shí)現(xiàn)方式的不同,AspectJ是編譯時對目標(biāo)類進(jìn)行增強(qiáng)(反編譯目標(biāo)類可發(fā)現(xiàn)多了內(nèi)容)谣殊,而Spring是生成一個代理類進(jìn)行增強(qiáng)拂共。

下面我們開始在Spring中配置AOP

  • 首先在Maven中增加AspectJ的支持jar包,注意版本要和jdk符合姻几,我之前用的jar包過老宜狐,導(dǎo)致aop測試時莫名出現(xiàn)一系列找不到包的異常。
<!--aop支持jar包 -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.8.9</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.9</version>
        </dependency>
  • 在Spring的配置文件中增加內(nèi)容:
<!--beans中增加如下三個配置-->
<beans  xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/aop 
    http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
     
    <!--aspect配置
    如果proxy-target-class 屬性值被設(shè)置為true蛇捌,那么基于類的代理將起作用(這時需要cglib庫)抚恒。
    如果proxy-target-class屬值被設(shè)置為false或者這個屬性被省略,那么標(biāo)準(zhǔn)的JDK 基于接口的代理將起作用-->
    <aop:aspectj-autoproxy proxy-target-class="true"/> 
    <!--Aspect Demo類-->
    <bean class="test.wsz.spring.aop.AspectDemo"  />
 </beans>

如果不采用Spring的XML Schema的方法络拌,也可以去除<beans ……>對應(yīng)配置俭驮,增加:

<!--啟動AspectJ支持-->
    <bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"  />
  • 然后我們創(chuàng)建一個Aspect的測試Demo類:
@Aspect //聲明該類為切面類,在spring配置中加入該類的bean春贸,ApplicationContext會自動加載混萝,將該Bean作為切面處理
public class AspectDemo {
    /**
     * 在方法執(zhí)行前進(jìn)行調(diào)用,value指定切入點(diǎn)
     */
    @Before(value = "execution(* test.wsz.spring.bean..StoneAxe.useAxe(..))")
    public void beforeTest() {
        System.out.println("-----before stoneAxe.useAxe()-----");
    }

    /**
     * 在方法正常執(zhí)行完成后進(jìn)行調(diào)用,value指定切入點(diǎn)遗遵,也可用pointcut。returning指定返回形參
     */
    @AfterReturning(returning = "returnValue", pointcut = "execution(* test.wsz.spring.bean..StoneAxe.useAxeTest(..))")
    public void afterReturnTest(Object returnValue) {
        System.out.println("-----after stoneAxe.useAxe()-----");
        System.out.println("返回值是:" + returnValue);
    }

    /**
     * 無論方法是否正常結(jié)束譬圣,只要完成后調(diào)用該方法
     */
    @After(value = "execution(* test.wsz.spring.bean..StoneAxe.useAxeTest(..))")
    public void afterTest() {
        System.out.println("方法執(zhí)行完成瓮恭,無論是正常完成還是異常終止執(zhí)行");
    }

    /**
     * 在方法異常后調(diào)用雄坪,但并不能像catch一樣捕獲異常厘熟,異常仍然會拋給上級進(jìn)行處理
     * 
     * @param e
     *            方法中拋出的異常
     */
    @AfterThrowing(throwing = "e", pointcut = "execution(* test.wsz.spring.bean..StoneAxe.useAxeTest(..))")
    public void afterThrowingTest(Throwable e) {
        System.out.println("方法拋出異常:" + e.getMessage());
    }

    /**
     * 功能較強(qiáng)的增強(qiáng)方法,類似before和afterReturning的集合
     * @param pjp 方法信息對象
     * @return
     * @throws Throwable
     */
    @Around("execution(* test.wsz.spring.bean..StoneAxe.useAxeTest(..))")
    public Object aroundTest(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("-----around-------");
        System.out.println("方法執(zhí)行前");
        Object object = pjp.proceed();
        System.out.println("方法執(zhí)行后");
        return object;
    }
}

這個Demo類中演示了幾種切面的注解方法维哈。xml的配置方法就不貼出來了绳姨,可自行百度。

為了方便阔挠,我們還可設(shè)定一個切點(diǎn)飘庄,然后進(jìn)行引用:

// 定義一個切入點(diǎn),該切入點(diǎn)方法體中的代碼無效
    @Pointcut("execution(* test.wsz.spring.bean..IronAxe.useAxe(..))") // 方法體中的代碼無效
    public void mypointcut() {
        System.out.println("-----pointcout-----");
    }

    /**
     * 在方法執(zhí)行前進(jìn)行調(diào)用
     */
    @Before(value = "mypointcut()")
    public void before() {
        System.out.println("-----before-----");
    }

注意购撼,幾種切面方法的執(zhí)行順序如下:

最后補(bǔ)充下切面execution的規(guī)則
execution(<修飾符模式>?<返回類型模式><方法名模式>(<參數(shù)模式>)<異常模式>?)
舉一個例子說明:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末跪削,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子迂求,更是在濱河造成了極大的恐慌碾盐,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件揩局,死亡現(xiàn)場離奇詭異毫玖,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)凌盯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進(jìn)店門付枫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人驰怎,你說我怎么就攤上這事阐滩。” “怎么了县忌?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵掂榔,是天一觀的道長。 經(jīng)常有香客問我芹枷,道長衅疙,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任鸳慈,我火速辦了婚禮饱溢,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘走芋。我一直安慰自己绩郎,他們只是感情好潘鲫,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著肋杖,像睡著了一般溉仑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上状植,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天浊竟,我揣著相機(jī)與錄音,去河邊找鬼津畸。 笑死振定,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的肉拓。 我是一名探鬼主播后频,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼暖途!你這毒婦竟也來了卑惜?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤驻售,失蹤者是張志新(化名)和其女友劉穎露久,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體芋浮,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡抱环,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了纸巷。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片镇草。...
    茶點(diǎn)故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖瘤旨,靈堂內(nèi)的尸體忽然破棺而出梯啤,到底是詐尸還是另有隱情,我是刑警寧澤存哲,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布因宇,位于F島的核電站,受9級特大地震影響祟偷,放射性物質(zhì)發(fā)生泄漏察滑。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一修肠、第九天 我趴在偏房一處隱蔽的房頂上張望贺辰。 院中可真熱鬧,春花似錦、人聲如沸饲化。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽吃靠。三九已至硫眨,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間巢块,已是汗流浹背礁阁。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留夕冲,地道東北人氮兵。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓裂逐,卻偏偏與公主長得像歹鱼,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子卜高,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評論 2 355

推薦閱讀更多精彩內(nèi)容