zoukankan      html  css  js  c++  java
  • Spring Framework AOP具体解释

    此前对于AOP的使用仅限于声明式事务,除此之外在实际开发中也没有遇到过与之相关的问题。近期项目中遇到了下面几点需求,细致思考之后,认为採用AOP来解决。一方面是为了以更加灵活的方式来解决这个问题,还有一方面是借此机会深入学习SpringAOP相关的内容。本文是权当本人的自己AOP学习笔记,下面需求不用AOP肯定也能解决,至于是否牵强附会,仁者见仁智者见智。
    1. 对部分函数的调用进行日志记录,用于观察特定问题在执行过程中的函数调用情况
    2. 监控部分重要函数,若抛出指定的异常,须要以短信或邮件方式通知相关人员
    3. 金控部分重要函数的执行时间

        其实,以上需求没有AOP也能搞定,仅仅是在实现过程中比較郁闷摆了。

    1. 须要打印日志的函数分散在各个包中,仅仅能找到所有的函数体,手动加入日志。然而这些日志都是暂时的,待问题解决之后应该须要清除打印日志的代码,仅仅能再次手动清除^_^!
    2. 类似1的情况,须要捕获异常的地方太多,假设手动加入时想到非常可能明天又要手动清除,仅仅能再汗。OK,该需求相对照较固定,属于长期监控的范畴,并不需求暂时加入后再清除。然而,客户某天要求,把当中20%的异常改为短信提醒,剩下的80%改用邮件提醒。改之,两天后,客户抱怨短信太多,所有改成邮件提醒...
    3. 该需求通经常使用于监控某些函数的运行时间,用以推断系统运行慢的瓶颈所在。瓶颈被解决之后,烦恼同情况1


        最终下定决心,採用AOP来解决!代码例如以下:

        切面类TestAspect

    Java代码  收藏代码
    1. package com.spring.aop;  
    2. /** 
    3.  * 切面 
    4.  * 
    5.  */  
    6. public class TestAspect {  
    7.   
    8.     public void doAfter(JoinPoint jp) {  
    9.         System.out.println("log Ending method: "  
    10.                 + jp.getTarget().getClass().getName() + "."  
    11.                 + jp.getSignature().getName());  
    12.     }  
    13.   
    14.     public Object doAround(ProceedingJoinPoint pjp) throws Throwable {  
    15.         long time = System.currentTimeMillis();  
    16.         Object retVal = pjp.proceed();  
    17.         time = System.currentTimeMillis() - time;  
    18.         System.out.println("process time: " + time + " ms");  
    19.         return retVal;  
    20.     }  
    21.   
    22.     public void doBefore(JoinPoint jp) {  
    23.         System.out.println("log Begining method: "  
    24.                 + jp.getTarget().getClass().getName() + "."  
    25.                 + jp.getSignature().getName());  
    26.     }  
    27.   
    28.     public void doThrowing(JoinPoint jp, Throwable ex) {  
    29.         System.out.println("method " + jp.getTarget().getClass().getName()  
    30.                 + "." + jp.getSignature().getName() + " throw exception");  
    31.         System.out.println(ex.getMessage());  
    32.     }  
    33.   
    34.     private void sendEx(String ex) {  
    35.         //TODO 发送短信或邮件提醒  
    36.     }  
    37. }   
    Java代码  收藏代码
    1. package com.spring.service;  
    2. /** 
    3.  * 接口A 
    4.  */  
    5. public interface AService {  
    6.       
    7.     public void fooA(String _msg);  
    8.   
    9.     public void barA();  
    10. }  
     
    Java代码  收藏代码
    1. package com.spring.service;  
    2. /** 
    3.  *接口A的实现类 
    4.  */  
    5. public class AServiceImpl implements AService {  
    6.   
    7.     public void barA() {  
    8.         System.out.println("AServiceImpl.barA()");  
    9.     }  
    10.   
    11.     public void fooA(String _msg) {  
    12.         System.out.println("AServiceImpl.fooA(msg:"+_msg+")");  
    13.     }  
    14. }  
    Java代码  收藏代码
    1. package com.spring.service;  
    2.   
    3. /** 
    4.  *   Service类B 
    5.  */  
    6. public class BServiceImpl {  
    7.   
    8.     public void barB(String _msg, int _type) {  
    9.         System.out.println("BServiceImpl.barB(msg:"+_msg+" type:"+_type+")");  
    10.         if(_type == 1)  
    11.             throw new IllegalArgumentException("測试异常");  
    12.     }  
    13.   
    14.     public void fooB() {  
    15.         System.out.println("BServiceImpl.fooB()");  
    16.     }  
    17.   
    18. }  
     

        ApplicationContext

    Java代码  收藏代码
    1. <?xml version="1.0" encoding="UTF-8"?>  
    2. <beans xmlns="http://www.springframework.org/schema/beans"  
    3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
    4.     xmlns:aop="http://www.springframework.org/schema/aop"  
    5.     xsi:schemaLocation="  
    6.             http://www.springframework.org/schema/beans  
    7.             http://www.springframework.org/schema/beans/spring-beans-2.0.xsd  
    8.             http://www.springframework.org/schema/aop  
    9.             http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"  
    10.     default-autowire="autodetect">  
    11.     <aop:config>  
    12.         <aop:aspect id="TestAspect" ref="aspectBean">  
    13.             <!--配置com.spring.service包下全部类或接口的全部方法-->  
    14.             <aop:pointcut id="businessService"  
    15.                 expression="execution(* com.spring.service.*.*(..))" />  
    16.             <aop:before pointcut-ref="businessService" method="doBefore"/>  
    17.             <aop:after pointcut-ref="businessService" method="doAfter"/>  
    18.             <aop:around pointcut-ref="businessService" method="doAround"/>  
    19.             <aop:after-throwing pointcut-ref="businessService" method="doThrowing" throwing="ex"/>  
    20.         </aop:aspect>  
    21.     </aop:config>  
    22.       
    23.     <bean id="aspectBean" class="com.spring.aop.TestAspect" />  
    24.     <bean id="aService" class="com.spring.service.AServiceImpl"></bean>  
    25.     <bean id="bService" class="com.spring.service.BServiceImpl"></bean>  
    26.   
    27. </beans>  

        測试类AOPTest

    Java代码  收藏代码
    1. public class AOPTest extends AbstractDependencyInjectionSpringContextTests {  
    2.       
    3.     private AService aService;  
    4.       
    5.     private BServiceImpl bService;  
    6.       
    7.     protected String[] getConfigLocations() {  
    8.         String[] configs = new String[] { "/applicationContext.xml"};  
    9.         return configs;  
    10.     }  
    11.       
    12.       
    13.     /** 
    14.      * 測试正常调用 
    15.      */  
    16.     public void testCall()  
    17.     {  
    18.         System.out.println("SpringTest JUnit test");  
    19.         aService.fooA("JUnit test fooA");  
    20.         aService.barA();  
    21.         bService.fooB();  
    22.         bService.barB("JUnit test barB",0);  
    23.     }  
    24.       
    25.     /** 
    26.      * 測试After-Throwing 
    27.      */  
    28.     public void testThrow()  
    29.     {  
    30.         try {  
    31.             bService.barB("JUnit call barB",1);  
    32.         } catch (IllegalArgumentException e) {  
    33.               
    34.         }  
    35.     }  
    36.       
    37.     public void setAService(AService service) {  
    38.         aService = service;  
    39.     }  
    40.       
    41.     public void setBService(BServiceImpl service) {  
    42.         bService = service;  
    43.     }  
    44. }  

        执行结果例如以下:

    Java代码  收藏代码
    1. log Begining method: com.spring.service.AServiceImpl.fooA  
    2. AServiceImpl.fooA(msg:JUnit test fooA)  
    3. log Ending method: com.spring.service.AServiceImpl.fooA  
    4. process time: 0 ms  
    5. log Begining method: com.spring.service.AServiceImpl.barA  
    6. AServiceImpl.barA()  
    7. log Ending method: com.spring.service.AServiceImpl.barA  
    8. process time: 0 ms  
    9. log Begining method: com.spring.service.BServiceImpl.fooB  
    10. BServiceImpl.fooB()  
    11. log Ending method: com.spring.service.BServiceImpl.fooB  
    12. process time: 0 ms  
    13. log Begining method: com.spring.service.BServiceImpl.barB  
    14. BServiceImpl.barB(msg:JUnit test barB type:0)  
    15. log Ending method: com.spring.service.BServiceImpl.barB  
    16. process time: 0 ms  
    17.   
    18. log Begining method: com.spring.service.BServiceImpl.barB  
    19. BServiceImpl.barB(msg:JUnit call barB type:1)  
    20. log Ending method: com.spring.service.BServiceImpl.barB  
    21. method com.spring.service.BServiceImpl.barB throw exception  
    22. 測试异常  
     

        《Spring參考手冊》中定义了下面几个AOP的重要概念,结合以上代码分析例如以下:

    • 切面(Aspect):官方的抽象定义为“一个关注点的模块化,这个关注点可能会横切多个对象”,在本例中,“切面”就是类TestAspect所关注的详细行为,比如,AServiceImpl.barA()的调用就是切面TestAspect所关注的行为之中的一个。“切面”在ApplicationContext中<aop:aspect>来配置。
    • 连接点(Joinpoint):程序执行过程中的某一行为,比如,AServiceImpl.barA()的调用或者BServiceImpl.barB(String _msg, int _type)抛出异常等行为。
    • 通知(Advice):“切面”对于某个“连接点”所产生的动作,比如,TestAspect中对com.spring.service包下全部类的方法进行日志记录的动作就是一个Advice。当中,一个“切面”能够包括多个“Advice”,比如TestAspect
    • 切入点(Pointcut):匹配连接点的断言,在AOP中通知和一个切入点表达式关联。比如,TestAspect中的全部通知所关注的连接点,都由切入点表达式execution(* com.spring.service.*.*(..))来决定
    • 目标对象(Target Object):被一个或者多个切面所通知的对象。比如,AServcieImpl和BServiceImpl,当然在实际执行时,Spring AOP採用代理实现,实际AOP操作的是TargetObject的代理对象。
    • AOP代理(AOP Proxy)在Spring AOP中有两种代理方式,JDK动态代理和CGLIB代理。默认情况下,TargetObject实现了接口时,则採用JDK动态代理,比如,AServiceImpl;反之,採用CGLIB代理,比如,BServiceImpl。强制使用CGLIB代理须要将<aop:config>proxy-target-class 属性设为true

           通知(Advice)类型

    • 前置通知(Before advice):在某连接点(JoinPoint)之前运行的通知,但这个通知不能阻止连接点前的运行。ApplicationContext中在<aop:aspect>里面使用<aop:before>元素进行声明。比如,TestAspect中的doBefore方法
    • 后通知(After advice):当某连接点退出的时候运行的通知(不论是正常返回还是异常退出)。ApplicationContext中在<aop:aspect>里面使用<aop:after>元素进行声明。比如,TestAspect中的doAfter方法,所以AOPTest中调用BServiceImpl.barB抛出异常时,doAfter方法仍然运行
    • 返回后通知(After return advice):在某连接点正常完毕后运行的通知,不包含抛出异常的情况。ApplicationContext中在<aop:aspect>里面使用<after-returning>元素进行声明。
    • 围绕通知(Around advice):包围一个连接点的通知,类似Web中Servlet规范中的Filter的doFilter方法。能够在方法的调用前后完毕自己定义的行为,也能够选择不运行。ApplicationContext中在<aop:aspect>里面使用<aop:around>元素进行声明。比如,TestAspect中的doAround方法。
    • 抛出异常后通知(After throwing advice): 在方法抛出异常退出时运行的通知。 ApplicationContext中在<aop:aspect>里面使用<aop:after-throwing>元素进行声明。比如,TestAspect中的doThrowing方法。

           切入点表达式

    • 通常情况下,表达式中使用”execution“就能够满足大部分的要求。表达式格式例如以下:
    Java代码  收藏代码
    1. execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)  

    modifiers-pattern:方法的操作权限

    ret-type-pattern:返回值

    declaring-type-pattern:方法所在的包

    name-pattern:方法名

    parm-pattern:參数名

    throws-pattern:异常

    当中,除ret-type-pattern和name-pattern之外,其它都是可选的。上例中,execution(* com.spring.service.*.*(..))表示com.spring.service包下,返回值为随意类型;方法名随意;參数不作限制的全部方法。

    • 通知參数

    能够通过args来绑定參数,这样就能够在通知(Advice)中訪问详细參数了。比如,<aop:aspect>配置例如以下

    Java代码  收藏代码
    1. <aop:config>  
    2.     <aop:aspect id="TestAspect" ref="aspectBean">  
    3.         <aop:pointcut id="businessService"  
    4.             expression="execution(* com.spring.service.*.*(String,..)) and args(msg,..)" />  
    5.             <aop:after pointcut-ref="businessService" method="doAfter"/>  
    6.     </aop:aspect>  
    7. </aop:config>  

    TestAspect的doAfter方法中就能够訪问msg參数,但这样以来AService中的barA()和BServiceImpl中的barB()就不再是连接点,由于execution(* com.spring.service.*.*(String,..))仅仅配置第一个參数为String类型的方法。当中,doAfter方法定义例如以下:

    Java代码  收藏代码
    1. public void doAfter(JoinPoint jp,String msg)  
    •   訪问当前的连接点

    不论什么通知(Advice)方法能够将第一个參数定义为 org.aspectj.lang.JoinPoint 类型。JoinPoint 接口提供了一系列实用的方法, 比方getArgs()(返回方法參数)、getThis()(返回代理对象)、getTarget()(返回目标)、getSignature()(返回正在被通知的方法相关信息)和toString()(打印出正在被通知的方法的实用信息。


    原文出自:http://pandonix.iteye.com/blog/336873/

  • 相关阅读:
    Object-C,NSSet,不可变集合
    NYIST 860 又见01背包
    NYIST 1070 诡异的电梯【Ⅰ】
    HDU 1542 Atlantis
    HDU 4756 Install Air Conditioning
    CodeForces 362E Petya and Pipes
    HDU 4751 Divide Groups
    HDU 3081 Marriage Match II
    UVA 11404 Palindromic Subsequence
    UVALIVE 4256 Salesmen
  • 原文地址:https://www.cnblogs.com/zfyouxi/p/4214493.html
Copyright © 2011-2022 走看看