Spring学习之AOP(下)

105 阅读2分钟

「这是我参与2022首次更文挑战的第4天,活动详情查看:2022首次更文挑战

前言:书接上文,上篇说完了aop的技术点,今天我们来看一下他的代码实现。

介绍:在上文我们说到Spring2.0后是可以使用基于 AspectJ 注解或者XML来编写的。

基于 AspectJ 注解来完成AOP。

  1. 切入点表达式的用法多样,各种写法都对应这不通的情况,介绍如下:
  • execution(public void com.aop.HelloWorld()) :匹配aop中返回值为void且无参数的HelloWorld方法
  • execution(public void com.aop.*()) :匹配aop中返回值为void且无参数的所有public方法
  • execution(public void com.aop.*(..)) :匹配aop中返回值为void的所有public方法
  • execution(public * com.aop.*(..)) :匹配aop中所有public方法
  1. 代码实现
// 声明该方法为前置通知
@Before("execution(public void com.aop.demo.HelloWorld()) || execution(public void com.aop.demo.HelloWorlds(String))")
public void beforeMethod(JoinPoint point) {
    String methodName = point.getSignature().getName();
    System.out.println("如果访问com.aop.demo.HelloWorld方法或者访问com.aop.demo.HelloWorld(String)方法 beforeMethod: " + methodName);
}

// 后置通知,无论该方法是否发生异常
@After("execution(public void com.aop.demo.*())")
public void endMethod(JoinPoint point) {
    String methodName = point.getSignature().getName();
    System.out.println("如果访问com.aop.demo下的所有方法 endMethod: " + methodName);
}

// 返回通知,正常返回时的通知,不包括异常,可以访问到方法的方绘制
@AfterReturning(value = "execution(public void com.aop.demo.*())", returning = "result")
public void afterReturning(JoinPoint point, Object result) {
    String methodName = point.getSignature().getName();
    System.out.println("AfterReturning : " + methodName + ", result: " + result);
}

// 异常通知
@AfterThrowing(value = "execution(public void com.aop.demo.*())", throwing = "ex")
public void afterThrowing(JoinPoint point) {
    String methodName = point.getSignature().getName();
    System.out.println("AfterThrowing : " + methodName);
}

// 环绕通知
@Around("execution(public void com.aop.demo.*())")
public void aroundMethod(ProceedingJoinPoint point,MyAnnotation around) throws Throwable{
    // 环绕通知(前通知)
    System.out.println("开始...");
    System.out.println("调用方法:"+ around.methodName());
    System.out.println("调用类:" + point.getSignature().getDeclaringTypeName());
    System.out.println("调用类名" + point.getSignature().getDeclaringType().getSimpleName());
    try {
        // 前置通知
        point.proceed(); // 目标方法执行
    } catch (Throwable throwable) {
        // 异常通知
        throwable.printStackTrace();
    }
    // 环绕通知(后通知)
    System.out.println("结束...");
}

基于web.xml配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
        xmlns:javaee="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance  http://www.springmodules.org/schema/cache/springmodules-cache.xsd http://www.springmodules.org/schema/cache/springmodules-ehcache.xsd"
        xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee   http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
    <!-- 配置目标类id -->
    <bean id="helloWorldService" class="com.aop.demo"/>
    <!-- 配置切面id -->
    <bean id="aspect" class="com.aop.demo"/>
    <aop:config>
    <aop:pointcut id="pointcut" expression="execution(* com.aop..*.*(..))"/>
    <!-- aop:aspect的ref引用切面支持类的方法 -->
    <aop:aspect ref="aspect">
    <aop:before pointcut-ref="pointcut"
            method="beforeAdvice"/>
    <aop:after pointcut-ref="pointcut"
            method="afterAdvice"/>
    </aop:aspect>
    </aop:config>
    </beans>
</web-app>

切入点使用<aop:config>标签下的<aop:pointcut>配置,expression属性用于定义切入点模式,默认是AspectJ语法,"execution(* com.aop...(..))"表示匹配com.aop包下及子包下的任何方法执行。 切面使用<aop:config>标签下的<aop:aspect>标签配置,其中"ref"用来引用切面支持类的方法。

前置通知<aop:before>标签,pointcut-ref属性用于引用切入点Bean,而method用来引用切面通知实现类中的方法,该方法就是通知实现,即在目标类方法执行之前调用的方法。

后置通知<aop:after>标签,切入点除了使用pointcut-ref属性来引用已经存在的切入点,也可以使用pointcut属性来定义,如pointcut="execution(* com.aop...(..))",method属性同样是指定通知实现,即在目标类方法执行之后调用的方法。

小结:AOP的总结就到这里了,在这我祝大家,心想事成,身体健康,万事如意。