Spring AOP使用

624 阅读5分钟

1.AOP概述

AOP: 全称是 Aspect Oriented Programming 即: 面向切面编程。
简单的说:AOP帮助我们抽离重复代码,在原来代码基础上进行一定的包装增强,如在方法执行前、方法返回后、方法抛出异常后等地方进行一定的拦截处理或者叫增强处理。

Spring AOP

  • AOP是一种思想,并不仅仅是Spring才有
  • 它基于动态代理来实现。默认地,如果使用接口的,用JDK 提供的动态代理实现,如果没有接口,使用 CGLIB 实现。
  • Spring AOP 需要依赖于 IOC 容器来管理,Spring AOP 只能作用于 Spring 容器中的 Bean,它是使用纯粹的 Java 代码实现的,只能作用于 bean 的方法。
  • Spring AOP 是基于代理实现的,在容器启动的时候需要生成代理实例,在方法调用上也会增加栈的深度,使得 Spring AOP 的性能不如 AspectJ 那么好。
  • Spring AOP 致力于解决的是企业级开发中最普遍的 AOP 需求(方法织入),而不是力求成为一个像 AspectJ 一样的 AOP 编程完全解决方案。

AOP开发相关术语

  • JoinPoint:连接点,可以被拦截(增强)到的点
  • pointCut:切入点,真正要被拦截(增强)到的点
  • Advice:通知、增强。方法层面的增强。比如权限校验的方法被称为是通知。
  • Introduction:引介,也是增强,不过是类层面的增强。一般研究的是方法层面。
  • Target:目标,被增强的对象(例如userDao)
  • Weaving:织入,将通知应用到目标的过程。比如将权限校验的代码应用到UserDao的save方法上的过程。
  • Proxy:一个类被AOP织入增强后,产生一个结果代理类
  • Aspect:切面,多个通知和多个切入点的组合

2.利用Spring完成AOP的开发

2.2 基于注解方式开发

2.2.1通知类型及API介绍

  • @Aspect:指定一个类为切面类
  • @Pointcut("execution(* com.zcc.service.Userservice.save(..))") 指定切入点表达式
  • @Before("表达式或切入点") 前置通知: 目标方法之前执行
  • @After("pointCut_()") 后置通知:目标方法之后执行(始终执行)
  • @AfterReturning("pointCut_()") 返回后通知: 执行方法结束前执行(异常不执行)
  • @AfterThrowing("pointCut_()") 异常通知: 出现异常时候执行
  • @Around("pointCut_()") 环绕通知: 环绕目标方法执行

2.2.2切入点表达式

主要利用切入点表达式针对某些类中的方法进行增强。
官网表达式语法说明
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)

符号讲解: ?号代表0或1,可以不写“*”号代表任意类型,0或多,方法参数为..表示为可变参数

参数讲解:

  • modifiers-pattern?【修饰的类型,可以不写】
  • ret-type-pattern【方法返回值类型,必写】
  • declaring-type-pattern?【方法声明的类型,可以不写】
  • name-pattern(param-pattern)【要匹配的名称,括号里面是方法的参数】
  • throws-pattern?【方法抛出的异常类型,可以不写】

官方也有给出一些例子给我们理解:

2.2.3代码实例

新建项目,在pom.xml中引入相关jar包地址
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.7.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>5.0.7.RELEASE</version>
</dependency>
<dependency>
    <groupId>aopalliance</groupId>
    <artifactId>aopalliance</artifactId>
    <version>1.0</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.1</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.0.7.RELEASE</version>
</dependency>

配置Spring的配置文件,编写Spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- =======================引入spring aop开发的约束 ========================= -->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!--打开组件扫描,启用注解配置-->
    <context:component-scan base-package="com.zcc"/>
    <!-- 开启aop注解开发 -->
    <aop:aspectj-autoproxy/>
</beans>

编写UserService.java

package com.zcc.Service;

import org.springframework.stereotype.Service;

@Service
public class UserService {

    public void save() {
        System.out.println("保存用户...");
    }

    public void delete() {
        System.out.println("删除用户...");
    }

    public void update() {
        int i = 1/0;
        System.out.println("更新用户...");
    }

    public void find() {
        System.out.println("查询用户...");
    }
}

编写切面类MyAspect.java

package com.zcc.AOP;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class MyAspect {

    @Before("execution(* com.zcc.Service.UserService.save(..))")
    public void before(){
        System.out.println("【前置通知】权限校验....");
    }

    @After("execution(* com.zcc.Service.UserService.delete(..))")
    public void after(){
        System.out.println("【后置通知】日志记录....");
    }

    @Around("execution(* com.zcc.Service.UserService.find(..))")
    public void around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("环绕前....");
        pjp.proceed();//执行目标方法
        System.out.println("环绕后....");
    }

    @AfterThrowing("pt()")
    public void afterThrowing(){
        System.out.println("afterThrowing()....");
    }

    @Pointcut("execution(* com.zcc.Service.UserService.update(..))")
    public void pt(){

    }

}

编写测试类

    @org.junit.Test
    public void demo4() {
        //使用工厂类去读取配置文件去创建并存储对象
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("Spring.xml");
        //获取代理对象
        UserService userService = applicationContext.getBean("userService", UserService.class);
        System.out.println(userService.getClass());
        userService.save();
        userService.delete();
        userService.update();
        userService.find();
    }

运行结果如下:

2.3基于XML方式开发

去掉上述所有的注解,重新编写Spring的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!-- =======================引入spring aop开发的约束 ========================= -->
<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
        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="userService2" class="com.zcc.Service.UserService2"></bean>
    <bean id="myAspect2" class="com.zcc.AOP.MyAspect2"></bean>

    <!-- 通过aop的配置来对目标类产生代理 -->
    <aop:config>
        <!--配置切入点,定义对谁进行拦截增强-->
        <aop:pointcut id="point1" expression="execution(* com.zcc.Service.UserService.save(..))"/>
        <aop:pointcut id="point2" expression="execution(* com.zcc.Service.UserService.delete(..))"/>

        <!--配置切面类-->
        <aop:aspect ref="myAspect2">
            <!--配置用切面类的哪个方法进行增强-->
            <aop:before method="before" pointcut-ref="point1"/>
            <aop:after method="after" pointcut-ref="point2"/>
        </aop:aspect>
    </aop:config>
</beans>

2.4参考链接

juejin.cn/post/684490…
juejin.cn/post/684490…