【Spring 学习笔记(十一)】基于注解的Spring AOP

227 阅读6分钟

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

写在前面😘

大一电子信息工程新生,请多多关照,希望能在掘金记录自己的学习历程!
【Spring 学习笔记】 系列教程基于 Spring 5.2.10.RELEASE讲解。

一、AOP简介

前面说过,Spring的核心思想就是IoC和AOP,有关IoC的内容已经介绍过一部分了,接下来就来讲下Spring 另一大重点:AOP

1️⃣什么是AOP

AOP ,“Aspect Oriented Programming”,译为“面向切面编程”,和 OOP(面向对象编程)类似,它也是一种编程思想

2️⃣AOP的作用(特点)

代理模式

Spring AOP的实现原理是代理模式,AOP的作用是通过代理类为原始类增加一些额外功能:如日志管理、权限管理、事务管理、异常管理等一些非业务性功能

无入侵式

与传统的公共方法不同,Spring AOP并不是直接调用的,AOP 是通过横向的抽取机制实现的。它将一些非业务的通用功能抽取出来单独维护,并通过配置文件或注解的形式定义这些功能要以哪种方式作用在哪个模块中,可以在无须修改任何业务代码的基础上完成对这些通用功能的调用和修改,即无入侵式的。

解耦合

事务与非事务功能分离,Spring AOP还减少代码的重复,让我们更专注于专注业务逻辑代码。

3️⃣AOP相关的专业术语

名称说明
Joinpoint(连接点)AOP 的核心概念,指的是程序执行期间明确定义的一个点,例如方法的调用、类初始化、对象实例化等。 在 Spring 中,连接点则指可以被动态代理拦截目标类的方法。
Pointcut(切入点)又称切点,指要对哪些 Joinpoint 进行拦截,即被拦截的连接点。
Advice(通知)指拦截到 Joinpoint 之后要执行的代码,即对切入点增强的内容。
Target(目标)指代理的目标对象,通常也被称为被通知(advised)对象。
Weaving(织入)指把增强代码应用到目标对象上,生成代理对象的过程。
Proxy(代理)指生成的代理对象。
Aspect(切面)切面是切入点(Pointcut)和通知(Advice)的结合。

4️⃣AOP框架

目前最流行的 AOP 实现(框架)主要有两个,分别为 Spring AOPAspectJ

  • 注:一般这两个框架一起整合使用。
AOP 框架说明
Spring AOP是一款基于 AOP 编程的框架,它能够有效的减少系统间的重复代码,达到松耦合的目的。 Spring AOP 支持 2 种代理方式,分别是基于接口的 JDK 动态代理和基于继承的 CGLIB 动态代理。
AspectJ是一个基于 Java 语言的 AOP 框架,从 Spring 2.0 开始,Spring AOP 引入了对 AspectJ 的支持。 AspectJ 扩展了 Java 语言,提供了一个专门的编译器,在编译时提供横向代码的植入。

二、AOP入门案例

SpringAOP的开发有两种方式,XML 和 注解, 本篇文章及之后的教程都使用注解开发演示.

1️⃣添加AOP依赖

pom.xml文件里添加Spring AOPAspectJ的jar包依赖

  • Spring AOP 使用纯 Java 实现,不需要专门的编译过程和类加载器,在运行期通过代理方式向目标类织入增强代码。
  • AspectJ 是一个基于 Java 语言的 AOP 框架。从 Spring2.0 开始,Spring AOP 引入对 Aspect 的支持,AspectJ 扩展了 Java 语言,提供了一个专门的编译器,在编译时提供横向代码的织入。
<dependencies>
    <!--包含Spring AOP:有基本的AOP功能-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.10.RELEASE</version>
    </dependency>
    <!--AspectJ框架有更强大的AOP功能-->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.5</version>
    </dependency>
</dependencies>

可以看到spring-context中已经导入了spring-aop,所以不需要再单独导入spring-aop

image-20220614154706480

2️⃣创建目标接口和实现类

目标接口和实现类就是所谓的Target(目标),即要被代理的对象。

/*UserDao接口*/
public interface UserDao {
    public void add();
    public void delete();
    public void update();
    public void select();
}
/*UserDaoImpl实现类*/
@Repository
public class UserDaoImpl implements UserDao {
    @Override
    public void add() {
        System.out.println("正在执行 UserDao 的 add 方法");
    }
    @Override
    public void delete() {
        System.out.println("正在执行 UserDao 的 delete 方法");
    }
    @Override
    public void update() {
        System.out.println("正在执行 UserDao 的 update 方法");
    }
    @Override
    public void select() {
        System.out.println("正在执行 UserDao 的 select 方法");
    }
}

3️⃣创建切面类(通知类)

3.1定义切面 @Aspect

通过 @Aspect 注解将一个 Bean 定义为切面。

@Component//将这个类定义成 Bean
@Aspect//将这个Bean定义为切面
public class MyAdvice {
}

3.2定义切点 @Pointcut

在 AspectJ 中,我们可以使用 @Pointcut 注解用来定义一个切点。

//切点方法必须是private,无返回值,无参数
@Pointcut(value ="execution(* com.bighorn.*.*Dao.*(..))")
private void pointCut() {
}

注意:

  • 定义为切点的方法是一个无意义的private方法,即无参数、无返回值(void)、无方法体
  • @Pointcut注解中有一个 value 属性,这个属性的值就是切入点表达式(插个眼在这,下篇文章详细嗦嗦)

3.3定义通知

**通知(Advice)**就是将共性功能抽取出来后形成的方法,即对切入点增强的内容

通知有很多类型:前置通知、后置通知、环绕通知、异常通知、返回通知。(插个眼在这,下篇文章详细嗦嗦)

通知注解中有一个 value 属性,value 属性的取值就是这些通知(Advice)所要织入(Weaving)切点(PointCut),它既可以是切入点表达式,也可以是切入点引用(切入点对应的方法名称)

//使用切入点引用
@Before("MyAdvice.pointCut()")
public void beforeAdvice() {
    System.out.println("这是前置通知……");
}
//使用切入点表达式
@After(value ="execution(* com.bighorn.*.*Dao.*(..))")
public void afterAdvice(){
    System.out.println("这是后置通知……");
}

3.4切面类完整代码

/*切面类(通知类)*/
@Component//将这个类定义成 Bean
@Aspect//将这个Bean定义为切面
public class MyAdvice {
    //切点方法必须是private,无返回值,无参数
    @Pointcut(value = "execution(* com.bighorn.*.*Dao.*(..))")
    private void pointCut() {
    }

    //使用切入点引用
    @Before("MyAdvice.pointCut()")
    public void beforeAdvice() {
        System.out.println("这是前置通知……");
    }
    //使用切入点表达式
    @After(value ="execution(* com.bighorn.*.*Dao.*(..))")
    public void afterAdvice(){
        System.out.println("这是后置通知……");
    }
}

4️⃣启用 @AspectJ 注解支持

在Spring的配置类中使用@AspectJ 注解开启AspectJ 的自动代理,使用@ComponentScan 注解开启注解扫描。

/*Spring核心配置类*/
@Configuration
@ComponentScan("com.bighorn") //开启注解扫描
@EnableAspectJAutoProxy //开启 AspectJ 的自动代理
public class SpringConfig {
}

5️⃣编写运行程序

public static void main(String[] args) throws SQLException {
    //获取配置类初始化容器
    ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
    //从容器中获取UserDao对象
    UserDao userDao = context.getBean(UserDao.class);
    //调用userDao的方法
    userDao.add();
    userDao.delete();
    userDao.update();
    userDao.select();
}

运行结果如下,可以发现对每个方法都进行的加强:前置通知和后置通知

image-20220614235030838

写在后面🍻

感谢观看啦✨
有什么不足,欢迎指出哦💖
掘金的运营同学审核辛苦了💗