簡單的aop實現日志打印(切入點表達式)


Spring中可以使用注解或XML文件配置的方式實現AOP。
1、導入jar包

  • com.springsource.net.sf.cglib -2.2.0.jar
  • com.springsource.org.aopalliance-1.0.0 .jar
  • com.springsource.org.aspectj.weaver-1.6.8 .RELEASE.jar
  • commons-logging-1.1.3. jar
  • spring-aop-4.0.0.RELEASE.jar
  • spring-aspects-4.0.0.RELEASE.jar
  • spring-beans-4.0.0.RELEASE.jar
  • spring-context-4.0.0.RELEASE.jar
  • spring-core-4.0.0.RELEASE.jar
  • spring-expression-4.0.0.RELEASE.jar

aspectaop相關jar包 ---> 資源目錄--->jar包資源--->AOP日志打印相關jar包(切面類) 

Spring相關jar包 ---> 資源目錄--->jar包資源--->Spring相關jar包 

2、開啟基於注解的AOP功能

在Spring的配置文件中加入

<context:component-scan base-package="com.bwlu.aop"></context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

3、聲明一個切面類,並把這個切面類加入到IOC容器中

 1 @Component//加入IOC容器
 2 @Aspect//表示這是一個切面類
 3 public class LogAspect{
 4     //value中為切入點表達式
 5     @Before(value="execution(public void com.bwlu.common.MathCalculatorImpl.*(..))")//前置通知  6     public void showBeginLog(){
 7         System.out.println("AOP日志開始");
 8     }
 9     @After(value="execution(public void com.bwlu.common.MathCalculatorImpl.*(..))")//后置通知 10     public void showReturnLog(){
11         System.out.println("AOP方法結束");
12     }
13     @AfterThrowing(value="execution(public void com.bwlu.common.MathCalculatorImpl.*(..))")//異常通知 14     public void showExceptionLog(){
15         System.out.println("AOP方法異常");
16     }
17     @AfterReturning(value="execution(public void com.bwlu.common.MathCalculatorImpl.*(..))")//返回通知 18     public void showAfterLog(){
19         System.out.println("AOP方法最終返回");
20     }
21 }

4、被代理的對象也需要加入IOC容器

 1 @Component
 2 public class MathCalculator {
 3     public void add(int i, int j) {
 4         int result=i+j;
 5         System.out.println("目標方法add(int)執行了");
 6     }
 7     public void sub(int i, int j) {
 8         int result=i-j;
 9         System.out.println("目標方法sub執行了");
10     }
11     public void mult(int i, int j) {
12         int result=i*j;
13         System.out.println("目標方法mult執行了");
14     }
15     public void div(int i, int j) {
16         int result=i/j;
17         System.out.println("目標方法div執行了");
18     }
19 }
MathCalculator

5、新建Junit測試類進行測試

 1 ApplicationContext ioc=new ClassPathXmlApplicationContext("applicationContext.xml");
 2 @Test
 3 public void test() {
 4     //需要進行強轉,如果該類實現了一個接口(並且切入點表達式指向這個類),那么獲取到的將不是該類的對象,需要強轉
 5     //MathCalculatorImpl實現了MathCalculator接口,則
 6     //MathCalculator bean = (MathCalculator)ioc.getBean("mathCalculatorImpl");
 7     MathCalculator bean = (MathCalculator)ioc.getBean("mathCalculator");
 8     bean.add(10, 5);
 9     System.out.println();
10     bean.add(10.0, 5);
11     System.out.println();
12     bean.sub(10, 5);
13     System.out.println();
14     bean.mult(10, 5);
15     System.out.println();
16     bean.div(10, 0);
17 }

6、切入點表達式

  • 切入點表達式的語法格式

execution([權限修飾符] [返回值類型] [簡單類名/全類名] [方法名]([參數列表]))

  • 切入點表達式支持通配符
  • 兩種切入表達式
    • 最詳細的切入點表達式:

 

execution(public void com.bwlu.aop.MathCalculator.add(int, int))

 

    • 最模糊的切入點表達式:
execution (* *.*(..))

execution(public void com.bwlu.aop.MathCalculator.add(int, int)):只有add方法加入了4個通知,

execution(public void com.bwlu.aop.MathCalculator.*(int, int)):任意方法,參數為int,int

execution(public void com.bwlu.aop.MathCalculator.*(..)):MathCalculator中的任意方法,任意參數列表

execution(public * com.bwlu.aop.MathCalculator.*(..)):MathCalculator中的任意方法,任意參數列表,任意返回值

execution( * com.bwlu.aop.MathCalculator.*(..)):MathCalculator中的任意方法,任意參數列表,任意返回值,任意訪問修飾符

execution (* *.*(..)):任意匹配

需要注意的是:權限是不支持寫通配符的,當然你可以寫一個*表示所有權限所有返回值!

7、優化

用@PointCut注解統一聲明,然后在其它通知中引用該統一聲明即可!

 1 @Component
 2 @Aspect
 3 public class LogAspect{
 4     @Pointcut(value="execution(* *.*(..))")
 5     public void showLog(){}
 6     @Before(value="showLog()")
 7     public void showBeginLog(){
 8         System.out.println("AOP日志開始");
 9     }
10     @After(value="showLog()")
11     public void showReturnLog(){
12         System.out.println("AOP方法結束");
13     }
14     @AfterThrowing(value="showLog()")
15     public void showExceptionLog(){
16         System.out.println("AOP方法異常");
17     }
18     @AfterReturning(value="showLog()")
19     public void showAfterLog(){
20         System.out.println("AOP方法最終返回");
21     }
22 }
LogAspect

8、通知方法的細節

(1)在通知中獲取目標方法的方法名和參數列表

  • 在通知方法中聲明一個JoinPoint類型的形參
  • 調用JoinPoint對象的getSignature()方法獲取目標方法的簽名
  • 調用JoinPoint對象的getArgs()方法獲取目標方法的實際參數列表

(2)在返回通知中獲取方法的返回值

  • 在@AfterReturning注解中添加returning屬性:@AfterReturning (value="showLog()", returning= "result")
  • 在返回通知的通知方法中聲明一個形參,形參名和returning屬性的值一致:showReturnLog(JoinPoint joinPoint, Object result)

(3)在異常通知中獲取異常對象

  • 在@ AfterThrowing注解中添加throwing屬性:@AfterThrowing (value="showLog()",throwing= "throwable" )
  • 在異常通知的通知方法中聲明一個形參,形參名和throwing屬性值一致:showExceptinLog(JoinPoint joinPoint, Throwable throwable)
 1 @Component
 2 @Aspect
 3 public class LogAspect{
 4     @Pointcut(value="execution(* *.*(..))")
 5     public void showLog(){}
 6     @Before(value="showLog()")
 7     public void showBeginLog(JoinPoint jPoint){
 8         Object[] args = jPoint.getArgs();
 9         List<Object> asList = Arrays.asList(args);
10         Signature signature = jPoint.getSignature();
11         String name = signature.getName();
12         System.out.println("AOP日志開始");
13         System.out.println("目標方法名:"+name+",參數列表:"+asList);
14     }
15     @After(value="showLog()")
16     public void showReturnLog(){
17         System.out.println("AOP方法結束");
18     }
19     @AfterThrowing(value="showLog()",throwing="ex")
20     public void showExceptionLog(Exception ex){
21         System.out.println("AOP方法異常");
22         System.out.println("異常信息:"+ex.getMessage());
23     }
24     @AfterReturning(value="showLog()",returning="result")
25     public void showAfterLog(Object result){
26         System.out.println("方法返回值:"+result);
27         System.out.println("AOP方法最終返回");
28     }
29 }
LogAspect

9、環繞通知@Around

環繞通知需要在方法的參數中指定JoinPoint的子接口類型ProceedingJoinPoint為參數:

public void around(ProceedingJoinPoint joinPoint){}

環繞通知能夠替代其他4個通知。
注意:@Around修飾的方法一定要將方法的返回值返回!本身相當於代理!

 1 @Component
 2 @Aspect
 3 public class LogAspect{
 4     @Around(value="execution(* *.*(..))")
 5     public Object showLog(ProceedingJoinPoint point){
 6         Object[] args = point.getArgs();
 7         List<Object> asList = Arrays.asList(args);
 8         Signature signature = point.getSignature();
 9         String name = signature.getName();
10         Object result = null;
11         try {
12             try {
13                 //目標方法之前要執行的操作,相當於@before
14                 System.out.println("[環繞日志]"+name+"開始了,參數為:"+asList);
15                 //調用目標方法
16                 result = point.proceed(args);
17             } finally {
18                 //方法最終結束時執行的操作,相當於@after
19                 System.out.println("[環繞日志]"+name+"結束了!");
20             }
21             //目標方法正常執行之后的操作,相當於@AfterReturning
22             System.out.println("[環繞日志]"+name+"返回了,返回值為:"+result);
23         } catch (Throwable e) {
24             //目標方法拋出異常信息之后的操作,相當於@AfterThrowing
25             System.out.println("[環繞日志]"+name+"出異常了,異常對象為:"+e);
26             throw new RuntimeException(e.getMessage());
27         }
28         return result;
29     }
30 }
LogAspect

 10、切面的優先級

對於同一個代理對象,可以同時有多個切面共同對它進行代理。
可以在切面類上通過@Order (value=50)注解來進行設置,值越小優先級越高!

@Aspect

@Component

@Order(value=40)

public class LogAspect {}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM