Spring AOP 扫盲(应用篇)

282 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第18天,点击查看活动详情

相关链接:Spring AOP 扫盲(概念篇)

概述

Spring 提供了两种方式来开配置 AOP 切面,一种是注解的方式,一种是 XML 配置文件的方式。现在已经很少使用 XML 配置文件来开发 Spring 应用了,因此,通过注解来配置 AOP 切面是比较推荐和常用的方式。

下面分别介绍如何通过这两种方式来配置 AOP 切面。

假设要多一下的 UserService 类型的add方法进行增强。

public class UserService {
    public void add() {
        System.out.println("Add...");
    }
}

基于注解的 Spring AOP 切面配置

基于注解配置切面非常简单,示例代码如下:

@Component
@Aspect
public class UserAspect {

    /**
     * 前置通知
     */
    @Before("execution(* pseudocode.UserService.add(..))")
    public void before() {
        System.out.println("Before...");
    }

    /**
     * 环绕通知
     */
    @Around("execution(* pseudocode.UserService.add(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("Around Before ...");
        // 执行被增强的目标方法
        proceedingJoinPoint.proceed();
        System.out.println("Around After ...");
    }
}

首先,在一个类上添加@Aspect注解,这个类就是一个切面的定义。

然后,可以在这个类中定义一些方法作为增强的逻辑,比如以上的代码中,分别定义了前置增强和环绕增强,其它几个增强类型的定义与前置增强类似,只不过用到的注解不同,分别是@After@AfterThrowing@AfterReturning,为了避免代码过长,就不一一罗列了。在这几个类型增强注解当中,配置的属性值就是切入点表达式。

如果几个增强方法用到了同样的切入点表达式,还可以单独配置切入点表达式,并在增强方法的注解中引用,像下面这样。

@Pointcut("execution(* pseudocode.UserService.add(..))")
public void point() {

}

/**
 * 前置通知
 */
@Before("point()")
public void before() {
    System.out.println("Before...");
}

注意,配置切入点表达式的方法,不会被执行。

配置完这些,我们就定义好了一个切面,要让它起作用,还需要告诉 Spring 开启 AOP 支持。

开启的方式有两种,第一种是针对基于 XML 配置的 Spring 上下文,可以在 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.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <aop:aspectj-autoproxy/>

</beans>

然后再把切面配置类注册为一个 Bean 就可以了。

另外一种就是针对基于注解配置的 Spring 上下文,可以通过在@Configuration配置类上添加@EnableAspectJAutoProxy注解来开启 Spring 对 AOP 特性的支持,此时,还需要给切面配置类添加@Component注解,使其作为一个 Bean 被注册到 Spring 容器中,配置类的实例如下:

@Configuration
@EnableAspectJAutoProxy
public class SpringConfig {
}

基于 XML 文件的 Spring AOP 切面配置

接下来介绍在 XML 配置文件中配置切面,这种方式只能用于基于 XML 配置的 Spring 上下文。

首先,还是要通过一个类和其中的方法定义切面的增强逻辑。

public class UserAspect {

    /**
     * 前置通知
     */
    public void before() {
        System.out.println("Before...");
    }

    /**
     * 环绕通知
     */
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("Around Before ...");
        // 执行被增强的目标方法
        proceedingJoinPoint.proceed();
        System.out.println("Around After ...");
    }
}

与通过注解配置切面不同,这个类只是一个普通的类,包含定义了增强逻辑的方法就可以了,然后是 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.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="userService" class="pseudocode.UserService"/>
    <bean id="userAspect" class="pseudocode.UserAspect"/>

    <aop:config>
        <aop:pointcut id="pointcut"
                      expression="execution(* pseudocode.UserService.add(..))"/>
        <aop:aspect ref="userAspect">
            <aop:before method="before" pointcut-ref="pointcut"/>
            <aop:around method="around" pointcut-ref="pointcut"/>
        </aop:aspect>
    </aop:config>

</beans>

XML 配置文件中的切面配置很容易理解,跟通过注解的方式很相似,只是换了一种配置格式而已。

另外,如果切面是通过 XML 配置文件配置的,那么 Spring 会自动开启对 AOP 特性的支持。

AspectJ 切面配置方式

在基于注解的切面配置中,使用到的@``Aspect等所有的注解,并非来自 Spring,而是来自另外一个 AOP 框架,即 AspectJ。Spring 在切面配置方面,使用了 AspectJ 的切入点表达式和配置方式,即使是通过 XML 来配置切面,也保持了同样的配置模型。

除此之外,在 Spring 应用运行时,对切入点的解析和匹配,也会使用 AspectJ 的库来完成。

但是,在运行方式上,Spring AOP 和 AspectJ 是完全不同的两个 AOP 框架。

总结

本文介绍了 Spring 中最基本的切面配置方法,以及开启 Spring 对 AOP 特性支持的方法,适合初学者阅读。最近在更新【Spring Framework 源码解读】专栏,这篇也是为了之后分析 AOP 相关的源码做准备。