Skip to content

基于 XML 的 AspectJ AOP 开发

我们可以在 Spring 项目中通过 XML 配置,对切面(Aspect 或 Advisor)、切点(PointCut)以及通知(Advice)进行定义和管理,以实现基于 AspectJ 的 AOP 开发。

Spring 提供了基于 XML 的 AOP 支持,并提供了一个名为“aop”的命名空间,该命名空间提供了一个 <aop:config> 元素。

  • 在 Spring 配置中,所有的切面信息(切面、切点、通知)都必须定义在 <aop:config> 元素中;
  • 在 Spring 配置中,可以使用多个 <aop:config>
  • 每一个 <aop:config> 元素内可以包含 3 个子元素: pointcut、advisor 和 aspect ,这些子元素必须按照这个顺序进行声明。

引入 aop 命名空间

首先,我们需要在 XML 配置文件中导入 Spring aop 命名空间的约束,如下所示。

xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
    ...
</beans>

定义切面 <aop:aspect>

在 Spring 配置文件中,使用 <aop:aspect> 元素定义切面。该元素可以将定义好的 Bean 转换为切面 Bean,所以使用 <aop:aspect> 之前需要先定义一个普通的 Spring Bean。

xml
<aop:config>
    <aop:aspect id="myAspect" ref="aBean">
        ...
    </aop:aspect>
</aop:config>

其中,id 用来定义该切面的唯一标识名称,ref 用于引用普通的 Spring Bean。

定义切入点 <aop:pointcut>

<aop:pointcut> 用来定义一个切入点,用来表示对哪个类中的那个方法进行增强。它既可以在 <aop:pointcut> 元素中使用,也可以在 <aop:aspect> 元素下使用。

  • <aop:pointcut> 元素作为 <aop:config> 元素的子元素定义时,表示该切入点是全局切入点,它可被多个切面所共享;

  • <aop:pointcut> 元素作为 <aop:aspect> 元素的子元素时,表示该切入点只对当前切面有效。

xml
<aop:config>
    <aop:pointcut id="myPointCut"
        expression="execution(* net.biancheng.service.*.*(..))"/>
</aop:config>

其中,id 用于指定切入点的唯一标识名称,execution 用于指定切入点关联的切入点表达式。

execution 的语法格式格式为:

 execution([权限修饰符] [返回值类型] [类的完全限定名] [方法名称]([参数列表])

其中:

  • 返回值类型、方法名、参数列表是必须配置的选项,而其它参数则为可选配置项。
  • 返回值类型:*表示可以为任何返回值。如果返回值为对象,则需指定全路径的类名。
  • 类的完全限定名:指定包名 + 类名。
  • 方法名:*代表所有方法,set* 代表以 set 开头的所有方法。
  • 参数列表:(..)代表所有参数;(*)代表只有一个参数,参数类型为任意类型;(*,String)代表有两个参数,第一个参数可以为任何值,第二个为 String 类型的值。

举例 1:对 net.biancheng.c 包下 UserDao 类中的 add() 方法进行增强,配置如下。

java
execution(* net.biancheng.c.UserDao.add(..))

举例 2:对 net.biancheng.c 包下 UserDao 类中的所有方法进行增强,配置如下。

java
execution(* net.biancheng.c.UserDao.*(..))

举例 3:对 net.biancheng.c 包下所有类中的所有方法进行增强,配置如下。

java
execution(* net.biancheng.c.*.*(..))

定义通知

AspectJ 支持 5 种类型的 advice,如下。

xml
<aop:aspect id="myAspect" ref="aBean">
    <!-- 前置通知 -->
    <aop:before pointcut-ref="myPointCut" method="..."/>
   
    <!-- 后置通知 -->
    <aop:after-returning pointcut-ref="myPointCut" method="..."/>
    <!-- 环绕通知 -->
    <aop:around pointcut-ref="myPointCut" method="..."/>
    <!-- 异常通知 -->
    <aop:after-throwing pointcut-ref="myPointCut" method="..."/>
    <!-- 最终通知 -->
    <aop:after pointcut-ref="myPointCut" method="..."/>
    .... 
</aop:aspect>

示例

下面我们通过一个示例来演示下 Spring 集成 AspectJ 基于 XML 实现 AOP 开发。

  1. 新建一个名为 my-spring-asepctj-demo 的 Java 项目,并将以下依赖 Jar 包导入到该项目中。
  • commons-logging-1.2.jar

  • spring-aop-5.3.13.jar

  • spring-aspects-5.3.13.jar

  • spring-beans-5.3.13.jar

  • spring-context-5.3.13.jar

  • spring-core-5.3.13.jar

  • spring-expression-5.3.13.jar

  • aspectjweaver-1.9.7.jar

  1. 在 net.biancheng.c.dao 包下,创建一个名为 OrderDao 的接口,代码如下。
java
package net.biancheng.c.dao;
public interface OrderDao {
    public void add();
    public void delete();
    public Integer modify();
    public void get();
}
  1. 在 net.biancheng.c.dao.impl 包下,创建 OrderDao 的实现类 OrderDaoImpl,代码如下。
java
package net.biancheng.c.dao.impl;
import net.biancheng.c.dao.OrderDao;
public class OrderDaoImpl implements OrderDao {
    @Override
    public void add() {
        System.out.println("正在执行 OrderDao 中的 add() 方法");
    }
    @Override
    public void delete() {
        System.out.println("正在执行 OrderDao 中的 delete() 方法");
    }
    @Override
    public int modify() {
        System.out.println("正在执行 OrderDao 中的 modify() 方法");
        return 1;
    }
    @Override
    public void get() {
        //异常
        int a = 10 / 0;
        System.out.println("正在执行 OrderDao 中的 get() 方法");
    }
}
  1. 在 net.biancheng.c 包下,创建一个名为 MyOrderAspect 的类,代码如下。
java
package net.biancheng.c;
import org.aspectj.lang.ProceedingJoinPoint;
public class MyOrderAspect {
   
    public void before() {
        System.out.println("前置增强……");
    }
    public void after() {
        System.out.println("最终增强……");
    }
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕增强---前……");
        proceedingJoinPoint.proceed();
        System.out.println("环绕增强---后……");
    }
    public void afterThrow(Throwable exception) {
        System.out.println("异常增强…… 异常信息为:" + exception.getMessage());
    }
    public void afterReturning(Object returnValue) {
        System.out.println("后置返回增强…… 方法返回值为:" + returnValue);
    }
}
  1. 在 src 目录下创建一个 Spring 配置文件 Beans2.xml,配置内容如下。
xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--定义 Bean-->
    <bean id="orderDao" class="net.biancheng.c.dao.impl.OrderDaoImpl"></bean>
    <!--定义切面-->
    <bean id="myOrderAspect" class="net.biancheng.c.MyOrderAspect"></bean>
    <aop:config>
        <aop:pointcut id="beforePointCut" expression="execution(* net.biancheng.c.dao.OrderDao.add(..))"/>
        <aop:pointcut id="throwPointCut" expression="execution(* net.biancheng.c.dao.OrderDao.get(..))"/>
        <aop:pointcut id="afterReturnPointCut" expression="execution(* net.biancheng.c.dao.OrderDao.modify(..))"/>
        <aop:pointcut id="afterPointCut" expression="execution(* net.biancheng.c.dao.OrderDao.*(..))"/>
        <aop:aspect ref="myOrderAspect">
            <!--前置增强-->
            <aop:before method="before" pointcut-ref="beforePointCut"></aop:before>
            <!--后置返回增强-->
            <aop:after-returning method="afterReturning" pointcut-ref="afterReturnPointCut"
                                 returning="returnValue"></aop:after-returning>
            <!--异常通知-->
            <aop:after-throwing method="afterThrow" pointcut-ref="throwPointCut"
                                throwing="exception"></aop:after-throwing>
            <!--最终通知-->
            <aop:after method="after" pointcut-ref="afterPointCut"></aop:after>
            <!--环绕通知-->
            <aop:around method="around" pointcut-ref="beforePointCut"></aop:around>
        </aop:aspect>
    </aop:config>
</beans>
  1. 在 net.biancheng.c 包下,创建一个名 MainApp 的类,代码如下。
java
package net.biancheng.c;
import net.biancheng.c.dao.OrderDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
    public static void main(String[] args) {
        ApplicationContext context2 = new ClassPathXmlApplicationContext("Beans2.xml");
        OrderDao orderDao = context2.getBean("orderDao", OrderDao.class);
        orderDao.add();
        orderDao.delete();
        orderDao.modify();
        orderDao.get();
    }
}
  1. 执行 MainApp 中的 main 方法,控制台输出如下。
前置增强……
环绕增强---前……
正在执行 OrderDao 中的 add() 方法
环绕增强---后……
最终增强……
正在执行 OrderDao 中的 delete() 方法
最终增强……
正在执行 OrderDao 中的 modify() 方法
最终增强……
后置返回增强…… 方法返回值为:1
最终增强……
异常增强…… 异常信息为:/ by zero

其中,id 用于指定切入点的唯一标识名称,execution 用于指定切入点关联的切入点表达式。