博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring(十八):Spring AOP(二):通知(前置、后置、返回、异常、环绕)
阅读量:5966 次
发布时间:2019-06-19

本文共 10702 字,大约阅读时间需要 35 分钟。

AspectJ支持5种类型的通知注解:

  • @Before:前置通知,在方法执行之前执行;
  • @After:后置通知,在方法执行之后执行;
  • @AfterRunning:返回通知,在方法返回结果之后执行(因此该通知方法在方法抛出异常时,不能执行);
  • @AfterThrowing:异常通知,在方法抛出异常之后执行;
  • @Around:环绕通知,围绕着方法执行。

示例项目新建:

第一步:新建spring aop项目

第二步:添加spring-aop.xml Spring配置文件:

View Code

第三步:添加接口类IArithmeticCalculator.java,ArithmeticCalculatorImpl.java Spring组件

package com.dx.spring.beans.aop;/** * Description:Addition, subtraction, multiplication, and division */public interface IArithmeticCalculator {    int add(int i, int j);    int sub(int i, int j);    int multi(int i, int j);    int div(int i, int j);}
View Code
package com.dx.spring.beans.aop;import org.springframework.stereotype.Component;@Component("arithmeticCalculator")public class ArithmeticCalculatorImpl implements IArithmeticCalculator {    @Override    public int add(int i, int j) {        int result = i + j;        return result;    }    @Override    public int sub(int i, int j) {        int result = i - j;        return result;    }    @Override    public int multi(int i, int j) {        int result = i * j;        return result;    }    @Override    public int div(int i, int j) {        int result = i / j;        return result;    }}
View Code

第四步:添加LoggingAspect.java切面

package com.dx.spring.beans.aop;import java.util.List;import java.util.Arrays;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.annotation.After;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.springframework.stereotype.Component;//把这个类声明为一个切面:需要把该类放入到IOC容器中、再声明为一个切面。@Aspect@Componentpublic class LoggingAspect {    // 在这里注册通知方法。}

第五步:添加测试类Client.java

package com.dx.spring.beans.aop;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class Client {    public static void main(String[] args) {        // 1:创建Spring的IOC容器;        ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-aop.xml");        // 2.从IOC容器中获取Bean的实例        IArithmeticCalculator arithmeticCalculator = (IArithmeticCalculator) ctx.getBean("arithmeticCalculator");        // 3.使用Bean    }}

@Before:前置通知

在切面类LoggingAspect中添加前置通知:

// 声明该方法为一个前置通知:在目标方法开始之前执行    @Before("execution(public int com.dx.spring.beans.aop.IArithmeticCalculator.*(int, int))")    public void beforeMethod(JoinPoint joinpoint) {        String methodName = joinpoint.getSignature().getName();        List args = Arrays.asList(joinpoint.getArgs());        System.out.println("before method " + methodName + " with " + args);    }

Client.java添加测试代码&执行测试:

int result = arithmeticCalculator.add(1, 3);        System.out.println(result);        result = arithmeticCalculator.div(4, 1);        System.out.println(result);

before method add with [1, 3]

before method div with [4, 0]

@After:后置通知

在切面类LoggingAspect中添加后置通知方法:

// 声明该方法为一个后置通知:在目标方法结束之后执行(无论方法是否抛出异常)。    // 但是因为当方法抛出异常时,不能返回值为null,因此这里无法获取到异常值。    @After(value = "execution(public int com.dx.spring.beans.aop.IArithmeticCalculator.*(int, int))")    public void afterMethod(JoinPoint joinpoint) {        String methodName = joinpoint.getSignature().getName();        List args = Arrays.asList(joinpoint.getArgs());        System.out.println("after method " + methodName);    }

Client.java添加测试代码&执行测试:

int result = arithmeticCalculator.add(1, 3);        System.out.println(result);        result = arithmeticCalculator.div(4, 0);        System.out.println(result);

执行结果:

备注:从执行结果中我们可以看出,即使方法抛出异常,后置通知方法也会执行。

@AfterRunning:返回通知

 修改切面类LoggingAspect,添加返回通知方法:

/**     * 声明该方法为一个返回通知:在目标方法返回结果时后执行(当方法抛出异常时,无法执行)。     * 但是因为当方法抛出异常时,类似代码     * {
@code * try
* {
* // before 前置通知
* // do action method
* // after returning 返回通知,可以访问到方法的返回值
* } * catch(Exception e){ * e.printStackTrace(); * // after throwing 异常通知,可以访问到方法出现的异常 * } * // after 后置通知,因为方法可以抛出异常,所以访问不到方法的返回值 * } * */ @AfterReturning(value = "execution(public int com.dx.spring.beans.aop.IArithmeticCalculator.*(int, int))", returning = "result") public void afterReturningMethod(JoinPoint joinpoint, Object result) { String methodName = joinpoint.getSignature().getName(); List args = Arrays.asList(joinpoint.getArgs()); System.out.println( "after method " + methodName + " with returning " + (result == null ? "NULL" : result.toString())); }

注意:返回通知注解中,参数是多一个了returning参数,该参数定义通知方法接收返回值的别名。

在测试类Client.java中追加测试代码:

int result = arithmeticCalculator.add(1, 3);        System.out.println(result);        result = arithmeticCalculator.div(4, 0);        System.out.println(result);

@AfterThrowing:异常通知

 修改切面类LoggingAspect,添加异常通知方法:

/**     * 定义一个异常通知函数:     * 只有在方法抛出异常时,该方法才会执行,而且可以接受异常对象。     * */    @AfterThrowing(value = "execution(public int com.dx.spring.beans.aop.IArithmeticCalculator.*(int, int))", throwing = "ex")    public void afterThrowingMethod(JoinPoint joinpoint, Exception ex) {        String methodName = joinpoint.getSignature().getName();        List args = Arrays.asList(joinpoint.getArgs());        System.out.println(                "after method " + methodName + " occurs exception: " + (ex == null ? "NULL" : ex.getMessage()));    }

备注:只有在方法抛出异常时,异常通知方法才会执行,而且可以接受异常对象。

在测试类Client.java中追加测试代码:

int result = arithmeticCalculator.add(1, 3);        System.out.println(result);        result = arithmeticCalculator.div(4, 0);        System.out.println(result);

打印信息

@Around:环绕通知

环绕通知其他方式不同,它相当于代理对象使用时效果一样。可以在方法前、后、返回、异常打印信息。

我们先看代理方法打印信息时如何处理,之后再看环绕通知如何处理。

代理方法实现通知:

package com.dx.spring.beans.aop;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;public class ArithmeticCalculatorProxy implements InvocationHandler {    private Object obj;    public ArithmeticCalculatorProxy(Object obj) {        this.obj = obj;    }    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        Object object = null;        try {            // *** 前置通知,在方法执行之前执行            System.out.println("before " + method);            // 真实的调用方法操作            object = method.invoke(obj, args);            // *** 返回通知,在方法返回结果之后执行(因此该通知方法在方法抛出异常时,不能执行);            System.out.println("before returning " + method);        } catch (Exception ex) {            ex.printStackTrace();            // *** 异常通知,在方法抛出异常之后执行;            System.out.println("before throwing " + method);            throw ex;        }        // *** 后置通知,在方法执行之后执行;因为方法可以抛出异常,所以访问不到方法的返回值        System.out.println("after " + method);        return object;    }}

环绕通知:

修改切面类LoggingAspect,添加环绕通知:

package com.dx.spring.beans.aop;import java.util.List;import java.util.Arrays;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.After;import org.aspectj.lang.annotation.AfterReturning;import org.aspectj.lang.annotation.AfterThrowing;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.springframework.stereotype.Component;//把这个类声明为一个切面:需要把该类放入到IOC容器中、再声明为一个切面。@Aspect@Componentpublic class LoggingAspect {//    // 声明该方法为一个前置通知:在目标方法开始之前执行//    @Before("execution(public int com.dx.spring.beans.aop.IArithmeticCalculator.*(int, int))")//    public void beforeMethod(JoinPoint joinpoint) {//        String methodName = joinpoint.getSignature().getName();//        List args = Arrays.asList(joinpoint.getArgs());//        System.out.println("before method " + methodName + " with " + args);//    }////    // 声明该方法为一个后置通知:在目标方法结束之后执行(无论方法是否抛出异常)。//    // 但是因为当方法抛出异常时,不能返回值为null,因此这里无法获取到异常值。//    @After(value = "execution(public intcom.dx.spring.beans.aop.IArithmeticCalculator.*(int,int))")//    public void afterMethod(JoinPoint joinpoint) {//        String methodName = joinpoint.getSignature().getName();//        List args = Arrays.asList(joinpoint.getArgs());//        System.out.println("after method " + methodName);//    }////    /**//     * 声明该方法为一个返回通知:在目标方法返回结果时后执行(当方法抛出异常时,无法执行)。 但是因为当方法抛出异常时,类似代码 {@code//     * try 
// * {
// * // before 前置通知
// * // do action method
// * // after returning 返回通知,可以访问到方法的返回值
// * } catch(Exception e){ e.printStackTrace(); // after throwing// * 异常通知,可以访问到方法出现的异常 } // after 后置通知,因为方法可以抛出异常,所以访问不到方法的返回值 }// */// @AfterReturning(value = "execution(public intcom.dx.spring.beans.aop.IArithmeticCalculator.*(int,int))", returning = "result")// public void afterReturningMethod(JoinPoint joinpoint, Object result) {// String methodName = joinpoint.getSignature().getName();// List args = Arrays.asList(joinpoint.getArgs());// System.out.println(// "after method " + methodName + " with returning " + (result == null ? "NULL" : result.toString()));// }//// /**// * 定义一个异常通知函数: 只有在方法跑吹异常时,该方法才会执行,而且可以接受异常对象。// */// @AfterThrowing(value = "execution(public intcom.dx.spring.beans.aop.IArithmeticCalculator.*(int,int))", throwing = "ex")// public void afterThrowingMethod(JoinPoint joinpoint, Exception ex) {// String methodName = joinpoint.getSignature().getName();// List args = Arrays.asList(joinpoint.getArgs());// System.out.println(// "after method " + methodName + " occurs exception: " + (ex == null ? "NULL" : ex.getMessage()));// } @Around(value = "execution(public int com.dx.spring.beans.aop.IArithmeticCalculator.*(int, int))") public Object aroundMethod(ProceedingJoinPoint pJoinPoint) throws Exception { String methodName = pJoinPoint.getSignature().getName(); List args = Arrays.asList(pJoinPoint.getArgs()); Object result = null; try { // 前置通知 System.out.println("before method " + methodName + " with " + args); // 执行目标方法 result = pJoinPoint.proceed(); // 返回通知 System.out.println("after method " + methodName + " returning " + result); } catch (Throwable ex) { // 异常通知 System.out.println("after method " + methodName + " occurs exception: " + ex.getMessage()); throw new Exception(ex); } // 后置通知 System.out.println("after method " + methodName); return result; }}

修改Client.java测试类,添加代码:

int result = arithmeticCalculator.add(1, 3);        System.out.println(result);        result = arithmeticCalculator.div(4, 0);        System.out.println(result);

执行结果

 

转载地址:http://wqmax.baihongyu.com/

你可能感兴趣的文章
ajax上传图片的本质
查看>>
转]最长递增子序列问题的求解
查看>>
SilverLight:基础控件使用(6)-Slider控件
查看>>
Android写的一个设置图片查看器,可以调整透明度
查看>>
第 5 章 File Share
查看>>
判断字符串解析是JsonObject或者JsonArray
查看>>
[LeetCode] Implement strStr()
查看>>
多模块Struts应用程序的几个问题(及部分解决方法)
查看>>
1.2. MariaDB
查看>>
SpringSide示例之HelloWorld
查看>>
日志不说谎--Asp.net的生命周期
查看>>
C#~异步编程续~.net4.5主推的await&async应用
查看>>
C#进行MapX二次开发之图层操作
查看>>
ASP.NET 运行机制详解
查看>>
C++ little errors , Big problem
查看>>
在 ML2 中配置 OVS vlan network - 每天5分钟玩转 OpenStack(136)
查看>>
Selenium2+python自动化34-获取百度输入联想词
查看>>
【★★★★★】提高PHP代码质量的36个技巧
查看>>
如何解决/home/oracle: is a directory报警
查看>>
BaaS API 设计规范
查看>>