熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java開源技術 >> 正文

Spring AOP 詳解

2022-06-13   來源: Java開源技術 

  此前對於AOP的使用僅限於聲明式事務除此之外在實際開發中也沒有遇到過與之相關的問題最近項目中遇到了以下幾點需求仔細思考之後覺得采用AOP來解決一方面是為了以更加靈活的方式來解決問題另一方面是借此機會深入學習SpringAOP相關的內容本文是權當本人的自己AOP學習筆記以下需求不用AOP肯定也能解決至於是否牽強附會仁者見仁智者見智
    對部分函數的調用進行日志記錄用於觀察特定問題在運行過程中的函數調用情況
    監控部分重要函數若拋出指定的異常需要以短信或郵件方式通知相關人員
    金控部分重要函數的執行時間
    事實上以上需求沒有AOP也能搞定只是在實現過程中比較郁悶擺了
    需要打印日志的函數分散在各個包中只能找到所有的函數體手動添加日志然而這些日志都是臨時的待問題解決之後應該需要清除打印日志的代碼只能再次手動清除^_^!
    類似的情況需要捕獲異常的地方太多如果手動添加時想到很可能明天又要手動清除只能再汗OK該需求相對比較固定屬於長期監控的范疇並不需求臨時添加後再清除然而客戶某天要求把其中%的異常改為短信提醒剩下的%改用郵件提醒改之兩天後客戶抱怨短信太多全部改成郵件提醒…
    該需求通常用於監控某些函數的執行時間用以判斷系統執行慢的瓶頸所在瓶頸被解決之後煩惱同情況
    終於下定決心采用AOP來解決!代碼如下
    切面類TestAspect
    [java]
    package comspringaop;
    /**
    * 切面
    *
    */
    public class TestAspect {
    public void doAfter(JoinPoint jp) {
    Systemoutprintln(log Ending method:
    + jpgetTarget()getClass()getName() +
    + jpgetSignature()getName())
    }
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
    long time = SystemcurrentTimeMillis()
    Object retVal = pjpproceed()
    time = SystemcurrentTimeMillis() time;
    Systemoutprintln(process time: + time + ms
    return retVal;
    }
    public void doBefore(JoinPoint jp) {
    Systemoutprintln(log Begining method:
    + jpgetTarget()getClass()getName() +
    + jpgetSignature()getName())
    }
    public void doThrowing(JoinPoint jp Throwable ex) {
    Systemoutprintln(method + jpgetTarget()getClass()getName()
    + + jpgetSignature()getName() + throw exception
    Systemoutprintln(exgetMessage())
    }
    private void sendEx(String ex) {
    //TODO 發送短信或郵件提醒
    }
    }
    package comspringaop;
    /**
    * 切面
    *
    */
    public class TestAspect {
    public void doAfter(JoinPoint jp) {
    Systemoutprintln(log Ending method:
    + jpgetTarget()getClass()getName() +
    + jpgetSignature()getName())
    }
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
    long time = SystemcurrentTimeMillis()
    Object retVal = pjpproceed()
    time = SystemcurrentTimeMillis() time;
    Systemoutprintln(process time: + time + ms
    return retVal;
    }
    public void doBefore(JoinPoint jp) {
    Systemoutprintln(log Begining method:
    + jpgetTarget()getClass()getName() +
    + jpgetSignature()getName())
    }
    public void doThrowing(JoinPoint jp Throwable ex) {
    Systemoutprintln(method + jpgetTarget()getClass()getName()
    + + jpgetSignature()getName() + throw exception
    Systemoutprintln(exgetMessage())
    }
    private void sendEx(String ex) {
    //TODO 發送短信或郵件提醒
    }
    }
    [java]
    package comspringservice;
    /**
    * 接口A
    */
    public interface AService {
    public void fooA(String _msg)
    public void barA()
    }
    package comspringservice;
    /**
    * 接口A
    */
    public interface AService {
    public void fooA(String _msg)
    public void barA()
    } [java]
    package comspringservice;
    /**
    *接口A的實現類
    */
    public class AServiceImpl implements AService {
    public void barA() {
    Systemoutprintln(AServiceImplbarA()
    }
    public void fooA(String _msg) {
    Systemoutprintln(AServiceImplfooA(msg:+_msg+
    }
    }
    package comspringservice;
    /**
    *接口A的實現類
    */
    public class AServiceImpl implements AService {
    public void barA() {
    Systemoutprintln(AServiceImplbarA()
    }
    public void fooA(String _msg) {
    Systemoutprintln(AServiceImplfooA(msg:+_msg+
    }
    }
    [java]
    package comspringservice;
    /**
    *   Service類B
    */
    public class BServiceImpl {
    public void barB(String _msg int _type) {
    Systemoutprintln(BServiceImplbarB(msg:+_msg+ type:+_type+

  if(_type ==
    throw new IllegalArgumentException(測試異常
    }
    public void fooB() {
    Systemoutprintln(BServiceImplfooB()
    }
    }
    package comspringservice;
    /**
    *   Service類B
    */
    public class BServiceImpl {
    public void barB(String _msg int _type) {
    Systemoutprintln(BServiceImplbarB(msg:+_msg+ type:+_type+
    if(_type ==
    throw new IllegalArgumentException(測試異常
    }
    public void fooB() {
    Systemoutprintln(BServiceImplfooB()
    }
    }
    ApplicationContext
    [java]
    <?xml version= encoding=UTF?>
    <beans xmlns=
    xmlns:xsi=instance
    xmlns:aop=
    xsi:schemaLocation=
   
    beansxsd
   
    aopxsd
    defaultautowire=autodetect>
    <aop:config>
    <aop:aspect id=TestAspect ref=aspectBean>
    <!配置comspringservice包下所有類或接口的所有方法>
    <aop:pointcut id=businessService
    expression=execution(* comspringservice**()) />
    <aop:before pointcutref=businessService method=doBefore/>
    <aop:after pointcutref=businessService method=doAfter/>
    <aop:around pointcutref=businessService method=doAround/>
    <aop:afterthrowing pointcutref=businessService method=doThrowing throwing=ex/>
    </aop:aspect>
    </aop:config>
    <bean id=aspectBean class=comspringaopTestAspect />
    <bean id=aService class=comspringserviceAServiceImpl></bean>
    <bean id=bService class=comspringserviceBServiceImpl></bean>
    </beans>
    <?xml version= encoding=UTF?>
    <beans xmlns=
    xmlns:xsi=instance
    xmlns:aop=
    xsi:schemaLocation=
   
    beansxsd
   
    aopxsd
    defaultautowire=autodetect>
    <aop:config>
    <aop:aspect id=TestAspect ref=aspectBean>
    <!配置comspringservice包下所有類或接口的所有方法>
    <aop:pointcut id=businessService
    expression=execution(* comspringservice**()) />
    <aop:before pointcutref=businessService method=doBefore/>
    <aop:after pointcutref=businessService method=doAfter/>
    <aop:around pointcutref=businessService method=doAround/>
    <aop:afterthrowing pointcutref=businessService method=doThrowing throwing=ex/>
    </aop:aspect>
    </aop:config>
    <bean id=aspectBean class=comspringaopTestAspect />
    <bean id=aService class=comspringserviceAServiceImpl></bean>
    <bean id=bService class=comspringserviceBServiceImpl></bean>
    </beans>
    測試類AOPTest
    [java]
    public class AOPTest extends AbstractDependencyInjectionSpringContextTests {
    private AService aService;
    private BServiceImpl bService;
    protected String[] getConfigLocations() {
    String[] configs = new String[] { /applicationContextxml};
    return configs;
    }
    /**
    * 測試正常調用
    */
    public void testCall()
    {
    Systemoutprintln(SpringTest JUnit test
    aServicefooA(JUnit test fooA
    aServicebarA()
    bServicefooB()
    bServicebarB(JUnit test barB
    }
    /**
    * 測試AfterThrowing
    */
    public void testThrow()
    {
    try {
    bServicebarB(JUnit call barB
    } catch (IllegalArgumentException e) {
    }
    }
    public void setAService(AService service) {
    aService = service;
    }
    public void setBService(BServiceImpl service) {
    bService = service;
    }
    }
    public class AOPTest extends AbstractDependencyInjectionSpringContextTests {
    private AService aService;
    private BServiceImpl bService;
    protected String[] getConfigLocations() {
    String[] configs = new String[] { /applicationContextxml};
    return configs;
    }
    /**
    * 測試正常調用
    */
    public void testCall()
    {
    Systemoutprintln(SpringTest JUnit test
    aServicefooA(JUnit test fooA
    aServicebarA()
    bServicefooB()
    bServicebarB(JUnit test barB
    }
    /**
    * 測試AfterThrowing
    */
    public void testThrow()
    {
    try {
    bServicebarB(JUnit call barB
    } catch (IllegalArgumentException e) {
    }
    }
    public void setAService(AService service) {
    aService = service;
    }
    public void setBService(BServiceImpl service) {
    bService = service;
    }
    }
    運行結果如下
    [java]
    log Begining method: comspringserviceAServiceImplfooA
    AServiceImplfooA(msg:JUnit test fooA)
    log Ending method: comspringserviceAServiceImplfooA
    process time: ms
    log Begining method: comspringserviceAServiceImplbarA
    AServiceImplbarA()
    log Ending method: comspringserviceAServiceImplbarA
    process time: ms
    log Begining method: comspringserviceBServiceImplfooB
    BServiceImplfooB()
    log Ending method: comspringserviceBServiceImplfooB
    process time: ms
    log Begining method: comspringserviceBServiceImplbarB
    BServiceImplbarB(msg:JUnit test barB type:
    log Ending method: comspringserviceBServiceImplbarB
    process time: ms
    log Begining method: comspringserviceBServiceImplbarB
    BServiceImplbarB(msg:JUnit call barB type:
    log Ending method: comspringserviceBServiceImplbarB
    method comspringserviceBServiceImplbarB throw exception
    測試異常
    log Begining method: comspringserviceAServiceImplfooA
    AServiceImplfooA(msg:JUnit test fooA)
    log Ending method: comspringserviceAServiceImplfooA
    process time: ms
    log Begining method: comspringserviceAServiceImplbarA
    AServiceImplbarA()
    log Ending method: comspringserviceAServiceImplbarA
    process time: ms
    log Begining method: comspringserviceBServiceImplfooB
    BServiceImplfooB()
    log Ending method: comspringserviceBServiceImplfooB
    process time: ms
    log Begining method: comspringserviceBServiceImplbarB
    BServiceImplbarB(msg:JUnit test barB type:
    log Ending method: comspringserviceBServiceImplbarB
    process time: ms
    log Begining method: comspringserviceBServiceImplbarB
    BServiceImplbarB(msg:JUnit call barB type:
    log Ending method: comspringserviceBServiceImplbarB
    method comspringserviceBServiceImplbarB throw exception

  測試異常
    《Spring參考手冊》中定義了以下幾個AOP的重要概念結合以上代碼分析如下
    切面(Aspect)官方的抽象定義為一個關注點的模塊化這個關注點可能會橫切多個對象在本例中切面就是類TestAspect所關注的具體行為例如AServiceImplbarA()的調用就是切面TestAspect所關注的行為之一切面在ApplicationContext中<aop:aspect>來配置
    連接點(Joinpoint)程序執行過程中的某一行為例如AServiceImplbarA()的調用或者BServiceImplbarB(String _msg int _type)拋出異常等行為
    通知(Advice)切面對於某個連接點所產生的動作例如TestAspect中對comspringservice包下所有類的方法進行日志記錄的動作就是一個Advice其中一個切面可以包含多個Advice例如TestAspect
    切入點(Pointcut)匹配連接點的斷言在AOP中通知和一個切入點表達式關聯例如TestAspect中的所有通知所關注的連接點都由切入點表達式execution(* comspringservice**())來決定
    目標對象(Target Object)被一個或者多個切面所通知的對象例如AServcieImpl和BServiceImpl當然在實際運行時Spring AOP采用代理實現實際AOP操作的是TargetObject的代理對象
    AOP代理(AOP Proxy)在Spring AOP中有兩種代理方式JDK動態代理和CGLIB代理默認情況下TargetObject實現了接口時則采用JDK動態代理例如AServiceImpl;反之采用CGLIB代理例如BServiceImpl強制使用CGLIB代理需要將 <aop:config> 的 proxytargetclass 屬性設為true
    通知(Advice)類型
    前置通知(Before advice)在某連接點(JoinPoint)之前執行的通知但這個通知不能阻止連接點前的執行ApplicationContext中在<aop:aspect>裡面使用<aop:before>元素進行聲明例如TestAspect中的doBefore方法
    後通知(After advice)當某連接點退出的時候執行的通知(不論是正常返回還是異常退出)ApplicationContext中在<aop:aspect>裡面使用<aop:after>元素進行聲明例如TestAspect中的doAfter方法所以AOPTest中調用BServiceImplbarB拋出異常時doAfter方法仍然執行
    返回後通知(After return advice)在某連接點正常完成後執行的通知不包括拋出異常的情況ApplicationContext中在<aop:aspect>裡面使用<afterreturning>元素進行聲明
    環繞通知(Around advice)包圍一個連接點的通知類似Web中Servlet規范中的Filter的doFilter方法可以在方法的調用前後完成自定義的行為也可以選擇不執行ApplicationContext中在<aop:aspect>裡面使用<aop:around>元素進行聲明例如TestAspect中的doAround方法
    拋出異常後通知(After throwing advice) 在方法拋出異常退出時執行的通知 ApplicationContext中在<aop:aspect>裡面使用<aop:afterthrowing>元素進行聲明例如TestAspect中的doThrowing方法
切入點表達式
    通常情況下表達式中使用execution就可以滿足大部分的要求表達式格式如下
    [java]
    execution(modifierspattern? rettypepattern declaringtypepattern? namepattern(parampattern) throwspattern?)
    execution(modifierspattern? rettypepattern declaringtypepattern? namepattern(parampattern) throwspattern?)modifierspattern:方法的操作權限
    rettypepattern:返回值
    declaringtypepattern:方法所在的包
    namepattern:方法名
    parmpattern:參數名
    throwspattern:異常
    其中除rettypepattern和namepattern之外其他都是可選的上例中execution(* comspringservice**())表示comspringservice包下返回值為任意類型方法名任意參數不作限制的所有方法
通知參數
    可以通過args來綁定參數這樣就可以在通知(Advice)中訪問具體參數了例如<aop:aspect>配置如下
    [java]
    <aop:config>
    <aop:aspect id=TestAspect ref=aspectBean>
    <aop:pointcut id=businessService
    expression=execution(* comspringservice**(String)) and args(msg />
    <aop:after pointcutref=businessService method=doAfter/>
    </aop:aspect>
    </aop:config>
    <aop:config>
    <aop:aspect id=TestAspect ref=aspectBean>
    <aop:pointcut id=businessService
    expression=execution(* comspringservice**(String)) and args(msg />
    <aop:after pointcutref=businessService method=doAfter/>
    </aop:aspect>
    </aop:config>TestAspect的doAfter方法中就可以訪問msg參數但這樣以來AService中的barA()和BServiceImpl中的barB()就不再是連接點因為execution(* comspringservice**(String))只配置第一個參數為String類型的方法其中doAfter方法定義如下
    [java]
    public void doAfter(JoinPoint jpString msg)
    public void doAfter(JoinPoint jpString msg)  訪問當前的連接點
    任何通知(Advice)方法可以將第一個參數定義為 orgaspectjlangJoinPoint 類型JoinPoint 接口提供了一系列有用的方法 比如 getArgs()(返回方法參數)getThis()(返回代理對象)getTarget()(返回目標)getSignature()(返回正在被通知的方法相關信息)和 toString()(打印出正在被通知的方法的有用信息


From:http://tw.wingwit.com/Article/program/Java/ky/201311/28867.html
    推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.