Java EE 突击 14 - Spring AOP

Spring AOP 详解

  • 一 . 什么是 Spring AOP ?
  • 二 . AOP 基础组成
    • 2.1 切面 (Aspect)
    • 2.2 连接点 (Join Point)
    • 2.3 切点 (Pointcut)
    • 2.4 通知 (Advice)
  • 三 . 实现 Spring AOP
    • 3.1 添加 Spring AOP 框架支持
    • 3.2 定义切面和切点
    • 3.3 定义通知
      • 前置通知
      • 后置通知
      • 返回通知
      • 异常通知
      • 环绕通知
    • 3.4 AOP 表达式
  • 四 . AOP 的实现原理
    • 4.1 AOP 原理
      • 动态代理 静态代理
        • JDK 动态代理实现(了解)
        • CGLIB 动态代理实现(了解)
        • JDK 和 CGLB 实现的区别(面试题)
      • 织入 (Weaving) : 代理的生成时机
  • 总结

这个专栏给大家介绍一下 Java 家族的核心产品 - SSM 框架
JavaEE 进阶专栏

Java 语言能走到现在 , 仍然屹立不衰的原因 , 有一部分就是因为 SSM 框架的存在

接下来 , 博主会带大家了解一下 Spring、Spring Boot、Spring MVC、MyBatis 相关知识点

并且带领大家进行环境的配置 , 让大家真正用好框架、学懂框架

来上一篇文章复习一下吧
点击即可跳转到前置文章
CSDN 平台观感有限 , 可以私聊作者获取源笔记链接
在这里插入图片描述

一 . 什么是 Spring AOP ?

Spring AOP = Spring + AOP
Spring 我们已经知道了 , Spring 是一个包含了众多工具和方法的 IoC 容器
那么这个 AOP 是什么呢 ?
我们之前学习过 OOP ( 面向对象编程 ) , 那这个 AOP 与 OOP 有什么关联呢 ?
AOP ( Aspect Oriented Programming ) : 面向切面编程 , 所谓的切面就是针对某一方面的功能进行处理 , 是一种通过分离功能性需求和横切关注点的方式 , 将特定功能从业务逻辑中独立出来的编程思想

比如说 : 用户的登录授权功能
这个功能是一类功能 , 在发布文章的时候要检验用户是否登录 , 在评论文章的时候要检验用户是否登录 , 在用户删除文章的时候要检验用户是否登录 …
在进行这些需要验证用户登录的操作的时候 , 我们都需要做某件事 , 那么我们就可以把这件事提取出来 , 做统一处理 , 这个功能我们就可以用 AOP 来完成
这里就不能用 OOP 处理了 , 这种高度抽象的事情我们就需要用 AOP 处理了 , 但是还离不开 OOP 思想
AOP 就相当于对 OOP 的补充
对于一样功能 , 我们可以分为好几个阶段 :

  1. 开发初期阶段 : 所有的方法都去实现一遍 , 比如添加文章、评论文章、删除文章 , 我们都需要各自实现一遍 , 同样的代码写了三遍 , 封装性不太好
  2. 开发中期阶段 : 封装成公共的方法 , 在需要的位置进行调用 . 但是我们仍然需要调用这个方法 , 这个方法与业务无关 , 这就造成了我们代码仍然是比较冗余 , 混杂进去了许多与业务无关的代码 , 并未实现代码的高度业务化
  3. 开发高级阶段 : 使用 AOP ( 拦截器 / 过滤器 ) 对某个功能做统一的处理 , 在业务代码中就不再需要去混杂非业务代码 ( 比如 : 登录状态校验 ) 了

举个栗子 :

开发中期阶段 : 火车站有 5 个站台 , 每个站台列车员都要检查违禁物品
开发高级阶段 : 火车站虽然有 5 个站台 , 但是违禁物品我们在进火车站的时候就统一检验了

使用 AOP , 更加实现了代码的解耦合

开发中期阶段 : 我们安全校验的函数需要传入两个参数 , 但是某一天业务变了 , 需要传入三个参数 , 因为我们还在业务代码中调用安全校验函数 , 所以业务代码中函数调用的部分也需要更改参数个数
开发高级阶段 : 我们把安全校验的函数统一处理 , 如果发生参数个数的改变 , 也与业务代码无关 , 实现了代码的解耦合

不使用 AOP , 我们的程序也能正常实现 , 但是一旦出现问题 , 我们的代码可维护性是非常差的 .
AOP 还可以实现 :

  • 统⼀日志记录
  • 统⼀方法执行时间统计
  • 统⼀的返回格式设置

code : 返回错误码
message : 返回错误信息
data : 返回数据

  • 统⼀的异常处理
  • 声明型事务的开启和提交等

也就是说使用 AOP 可以扩充多个对象的某个能力 , 所以 AOP 可以说是 OOP ( Object Oriented Programming , 面向对象编程 ) 的补充和完善。
AOP 是一种思想 , Spring AOP 就是 AOP 思想的一种具体实现 ( 类似于 IoC 与 DI )

二 . AOP 基础组成

2.1 切面 (Aspect)

切面指的是当前 AOP 的作用 , 我们可以笼统的认为当前的 AOP 是针对谁的
切面可以看做是一个与业务逻辑无关 , 但对多个对象产生影响的模块化单元
比如 : 用户登录的判断 , 这就是一个切面 . 我还可以设计其他切面 , 比如 : 记录日志
这是一个很大的概念
切面由切点 ( Pointcut ) 和通知 ( Advice , 也叫做增强 ) 组成 . 它既包含了横切逻辑的定义 , 也包括了连接点的定义

2.2 连接点 (Join Point)

应用程序执行过程中 , 能够插入切面的一个点 , 就叫做连接点
举个栗子 : 我现在提供了一个功能 , 程序中有哪些位置需要这个功能 , 在哪些位置需要调用 AOP , 就称为连接点

2.3 切点 (Pointcut)

切点的作用 : 提供一组规则 , 用来匹配通知的
切面就是定义了哪件事需要重复调用 , 比如登录检查 , 切点就是制定我们拦截的规则
比如 : 注册是不需要进行登录检查的 , 因为我都没登录 , 如果注册阶段就进行登录检查的话 , 那这个账号就永远不会被注册成功 .

2.4 通知 (Advice)

通知就是具体要执行的动作 , 比如 : 借助切点 , 我们把某项操作拦截下来 , 但是拦截下来我们要干嘛呢
通知分为 5 种 :

  • 前置通知使用 @Before : 通知方法会在目标方法调用之前执行

执行某个业务 , 执行之前先执行前置通知方法 (前置增强方法)

  • 后置通知使用 @After : 通知方法会在目标方法返回或者抛出异常后调用

执行完这个业务之后 , 我再执行后置通知方法

  • 返回之后通知使用 @AfterReturning : 通知方法会在目标方法返回后调用

在 return 之后 , 再通知一下

  • 抛异常后通知使用 @AfterThrowing : 通知方法会在目标方法抛出异常后调用

在抛出异常之后 , 进行通知

  • 环绕通知使用 @Around : 通知包裹了被通知的方法 , 在被通知的方法通知之前和调⽤之后执行自定义的行为

在被通知的方法执行之前或者执行之后执行的通知 , 叫做环绕通知


总结一下 : AOP 基础组成

  1. 切面 : 定义 AOP 业务类型的 (当前 AOP 是干嘛的)
  2. 连接点 : 有可能调用 AOP 的地方就叫做一个连接点
  3. 切点 : 定义 AOP 拦截规则
  4. 通知 / 增强方法 : 定义什么时候干什么事的
    1. 前置通知 : 拦截的目标方法之前执行的通知
    2. 后置通知 : 拦截的目标方法之后执行的通知
    3. 返回之前通知 : 拦截的目标方法返回数据之后通知
    4. 抛出异常之后的通知 : 拦截的目标方法抛出异常之后执行的通知
    5. 环绕通知 : 在拦截方法执行前后都执行的通知

可以再通过这个图片再理解一下
Java EE 突击 14 - Spring AOP_第1张图片

三 . 实现 Spring AOP

3.1 添加 Spring AOP 框架支持

我们不新创建项目了 , 用之前 MyBatis 的项目即可
把这段内容复制到 pom.xml 中


<dependency>
	<groupId>org.springframework.bootgroupId>
	<artifactId>spring-boot-starter-aopartifactId>
dependency>

Java EE 突击 14 - Spring AOP_第2张图片

3.2 定义切面和切点

之前我们编写的登录功能 , 我们就不在每个业务里面去写了 , 我直接就定义一个切面 , 我们执行业务代码的时候 , 他就会先去执行 AOP , 我们就不需要每个地方都去写登录功能了
我们在 demo 包底下新建一个 aop 包 , 再新建一个类 , 叫做 LoginAOP
Java EE 突击 14 - Spring AOP_第3张图片
Java EE 突击 14 - Spring AOP_第4张图片
声明切面的注解是 @Aspect , 声明切点的注解是 @Pointcut
然后编写以下代码

package com.example.demo.aop;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * 登录的 AOP 实现代码
 */
@Component // Spring Boot 项目要添加该注解
@Aspect // 标识当前类为一个切面
public class LoginAOP {
    // 定义切点(拦截的规则)
    // 括号里面内容先不用管
    @Pointcut("execution(* com.example.demo.controller.UserController.*(..))")
    // 写一个返回值为void的空方法,方法名无所谓
    public void pointcut() {
    }
}

Java EE 突击 14 - Spring AOP_第5张图片

3.3 定义通知

前置通知

前置通知使用 @Before 注解

package com.example.demo.aop;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * 登录的 AOP 实现代码
 */
@Component // Spring Boot 项目要添加该注解
@Aspect // 标识当前类为一个切面
public class LoginAOP {
    // 定义切点(拦截的规则)
    // 括号里面内容先不用管
    @Pointcut("execution(* com.example.demo.controller.UserController.*(..))")
    // 写一个返回值为void的空方法,方法名无所谓
    public void pointcut() {
    }

    // 前置通知
    // 在调用 UserController 里面的方法之前,先执行的方法
    @Before("pointcut()")
    public void before() {
        System.out.println("执行了前置通知");
    }
}

Java EE 突击 14 - Spring AOP_第6张图片
我们启动程序 , 通过浏览器查看效果
Java EE 突击 14 - Spring AOP_第7张图片
Java EE 突击 14 - Spring AOP_第8张图片
运行之后
image.png
Java EE 突击 14 - Spring AOP_第9张图片


那么我们把这里改成 TestController
Java EE 突击 14 - Spring AOP_第10张图片
但是我们访问的是 UserController 里面的方法 , 前置通知还能被打印吗
Java EE 突击 14 - Spring AOP_第11张图片
编写以下代码

package com.example.demo.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {
    @RequestMapping("hi")
    public String sayHi() {
        return "hi";
    }
}

接下来 , 我们去浏览器里面访问 127.0.0.1:8080/user/getall, 看一看效果
image.png
Java EE 突击 14 - Spring AOP_第12张图片
那我们访问 127.0.0.1:8080/hi呢 ?
image.png
Java EE 突击 14 - Spring AOP_第13张图片
把 TestController 改回来

后置通知

后置通知实现 @After 注解

package com.example.demo.aop;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * 登录的 AOP 实现代码
 */
@Component // Spring Boot 项目要添加该注解
@Aspect // 标识当前类为一个切面
public class LoginAOP {
    // 定义切点(拦截的规则)
    // 括号里面内容先不用管
    @Pointcut("execution(* com.example.demo.controller.UserController.*(..))")
    // 写一个返回值为void的空方法,方法名无所谓
    public void pointcut() {
    }

    // 前置通知
    // 在调用 UserController 里面的方法之前,先执行的方法
    @Before("pointcut()")
    public void before() {
        System.out.println("执行了前置通知");
    }

    // 后置通知
    @After("pointcut()")
    public void after() {
        // 后置通知实现的具体业务代码
        System.out.println("执行了后置通知");
    }
}

在浏览器查看一下效果
image.png
Java EE 突击 14 - Spring AOP_第14张图片

返回通知

使用注解 @AfterReturning

package com.example.demo.aop;

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

/**
 * 登录的 AOP 实现代码
 */
@Component // Spring Boot 项目要添加该注解
@Aspect // 标识当前类为一个切面
public class LoginAOP {
    // 定义切点(拦截的规则)
    // 括号里面内容先不用管
    @Pointcut("execution(* com.example.demo.controller.UserController.*(..))")
    // 写一个返回值为void的空方法,方法名无所谓
    public void pointcut() {
    }

    // 前置通知
    // 在调用 UserController 里面的方法之前,先执行的方法
    @Before("pointcut()")
    public void before() {
        System.out.println("执行了前置通知");
    }

    // 后置通知
    @After("pointcut()")
    public void after() {
        // 后置通知实现的具体业务代码
        System.out.println("执行了后置通知");
    }

    // 返回通知
    @AfterReturning("pointcut()")
    public void afterReturning() {
        // 返回通知实现的具体业务代码
        System.out.println("执行了返回通知");
    }
}

Java EE 突击 14 - Spring AOP_第15张图片

异常通知

使用注解 @AfterThrowing

package com.example.demo.aop;

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

/**
 * 登录的 AOP 实现代码
 */
@Component // Spring Boot 项目要添加该注解
@Aspect // 标识当前类为一个切面
public class LoginAOP {
    // 定义切点(拦截的规则)
    // 括号里面内容先不用管
    @Pointcut("execution(* com.example.demo.controller.UserController.*(..))")
    // 写一个返回值为void的空方法,方法名无所谓
    public void pointcut() {
    }

    // 前置通知
    // 在调用 UserController 里面的方法之前,先执行的方法
    @Before("pointcut()")
    public void before() {
        System.out.println("执行了前置通知");
    }

    // 后置通知
    @After("pointcut()")
    public void after() {
        // 后置通知实现的具体业务代码
        System.out.println("执行了后置通知");
    }

    // 返回通知
    @AfterReturning("pointcut()")
    public void afterReturning() {
        // 返回通知实现的具体业务代码
        System.out.println("执行了返回通知");
    }

    // 异常通知
    @AfterThrowing("pointcut()")
    public void afterThrowing() {
        // 异常通知实现的具体业务代码
        System.out.println("执行了异常通知");
    }
}

Java EE 突击 14 - Spring AOP_第16张图片

环绕通知

使用注解 @Around
注解部分 : @Around(“pointcut()”) 同上
返回值 : Object , 代表目标方法在执行之后 , 把生成的对象再返回给 Spring 框架 , 因为 Spring 框架执行完一个方法之后 , 可能还会去执行后续操作 , 比如 : 释放资源… , 所以他需要拿到这个对象
固定参数 : ProceedingJoinPoint joinPoint , 代表正在执行的目标方法

Proceeding : 正在加工的
JoinPoint : 连接点
正在加工的连接点 -> 要去拦截的目标方法 -> 把目标方法拦截之后 , 变成了 ProceedingJoinPoint 的对象

接下来 , 我们自己去实现一个环绕通知

package com.example.demo.aop;

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

/**
 * 登录的 AOP 实现代码
 */
@Component // Spring Boot 项目要添加该注解
@Aspect // 标识当前类为一个切面
public class LoginAOP {
    // 定义切点(拦截的规则)
    // 括号里面内容先不用管
    @Pointcut("execution(* com.example.demo.controller.UserController.*(..))")
    // 写一个返回值为void的空方法,方法名无所谓
    public void pointcut() {
    }

    // 前置通知
    // 在调用 UserController 里面的方法之前,先执行的方法
    @Before("pointcut()")
    public void before() {
        System.out.println("执行了前置通知");
    }

    // 后置通知
    @After("pointcut()")
    public void after() {
        // 后置通知实现的具体业务代码
        System.out.println("执行了后置通知");
    }

    // 返回通知
    @AfterReturning("pointcut()")
    public void afterReturning() {
        // 返回通知实现的具体业务代码
        System.out.println("执行了返回通知");
    }

    // 异常通知
    @AfterThrowing("pointcut()")
    public void afterThrowing() {
        // 异常通知实现的具体业务代码
        System.out.println("执行了异常通知");
    }

    // 环绕通知
    // 注解部分:@Around("pointcut()") 同上
    // 返回值:Object,代表目标方法在执行之后,把生成的对象再返回给 Spring 框架,因为 Spring 框架执行完一个方法之后,还会去执行后续操作,比如:释放资源...,所以他需要拿到这个对象
    // 固定参数:ProceedingJoinPoint joinPoint
    @Around("pointcut()")
    public Object doAround(ProceedingJoinPoint joinPoint) {
        Object result = null;
        // 前置业务代码
        System.out.println("环绕通知的前置执行方法");
        // 执行目标方法
        try { 
            // 实际调用的是 UserController 里面的 getUsers 方法
            result = joinPoint.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        // 后置业务代码
        System.out.println("环绕通知的后置执行方法");

        return result;
    }
}

Java EE 突击 14 - Spring AOP_第17张图片
我们可以猜一下 : 环绕通知出现在前置通知的前还是后 , 环绕通知出现在后置通知的前还是后呢 ?
那么我们运行一下
image.png
Java EE 突击 14 - Spring AOP_第18张图片
可以发现 , 环绕通知执行顺序比前置通知还要早 , 执行顺序比后置通知还要晚 , 相当于龙头蛇尾 , 全给霸占上了 .
环绕通知最经典的用途就是记录方法执行时间
Java EE 突击 14 - Spring AOP_第19张图片

3.4 AOP 表达式

固定写法 :
execution(<修饰符><返回类型><包.类.⽅法(参数)><异常>)
Java EE 突击 14 - Spring AOP_第20张图片

四 . AOP 的实现原理

使用了 AOP 之后 , 我们就实现了一种特定的功能
比如 : 我们的登录验证 , 需要调用所有登录验证的位置 , 都不需要再关注登录验证这个事了 , 因为我们的 AOP 类里面已经做了相关处理了 , 并且我们也指定了拦截路径
image.png
这就代表我们拦截的话 , 就拦截 UserController 文件夹下面所有的方法 , 只要是需要登录验证的功能 , 全部写在 UserController 文件夹下 , 这样就可以实现登录拦截了 , 并且我们写业务的时候也不用关注登录验证功能了 .
那么 AOP 确实好用 , 但是 AOP 是咋回事呢 ?

4.1 AOP 原理

Spring AOP 是构建在动态代理基础上的 , 因此 Spring 对 AOP 的支持局限于方法级别的拦截
那么什么叫动态代理呢 ?

动态代理 静态代理

我们之前在讲 Fiddler 的时候 , 提到过代理

代理就可以理解为代购 , 我们想要去买海外商品就需要通过代购来帮我们购买

在程序执行期间生成的代理 , 就叫做动态代理

代理分为 : 静态代理、动态代理
以新年放烟花举例
静态代理指的是 : 一年四季一直卖烟花的人 , 实际上就是在程序还没执行之前就产生的代理
动态代理指的是 : 赶上正月十五放烟花的人多 , 有的人就进点炮去广场卖 , 卖几天就完事 , 这就指的是在程序运行期间生成的代理

Java EE 突击 14 - Spring AOP_第21张图片
动态代理也是一种思想 , 他在 Spring AOC 中的具体实现就是 JDK Proxy 和 CGLIB 方式

JDK Proxy 是 Java 官方提供给我们的动态代理
CGLIB 是第三方的动态代理
具体什么时候使用 JDK Proxy , 什么时候使用 CGLIB , 这要看我们的业务场景
假如说你的目标对象实现了接口或者实现了接口的子类 , 那么他就会使用 JDK Proxy
目标对象没有实现接口 , 只是一个普通的类 , 他就会使用 CGLIB

JDK 动态代理实现(了解)

不用具体了解 , 看看即可

package com.example.demo;

import org.example.demo.service.AliPayService;
import org.example.demo.service.PayService;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//动态代理:使⽤JDK提供的api(InvocationHandler、Proxy实现),此种⽅式实现,要求被代理类必须实现接⼝

public class PayServiceJDKInvocationHandler implements InvocationHandler {

    //⽬标对象即就是被代理对象
    private Object target;

    public PayServiceJDKInvocationHandler(Object target) {
        this.target = target;
    }

    //proxy代理对象
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        //1.安全检查
        System.out.println("安全检查");
        //2.记录⽇志
        System.out.println("记录⽇志");
        //3.时间统计开始
        System.out.println("记录开始时间");
        //通过反射调⽤被代理类的⽅法
        Object retVal = method.invoke(target, args);
        //4.时间统计结束
        System.out.println("记录结束时间");
        return retVal;
    }

    public static void main(String[] args) {
        PayService target = new AliPayService();
        //⽅法调⽤处理器
        InvocationHandler handler =
                new PayServiceJDKInvocationHandler(target);
        //创建⼀个代理类:通过被代理类、被代理实现的接⼝、⽅法调⽤处理器来创建
        PayService proxy = (PayService) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                new Class[]{PayService.class},
                handler
        );
        proxy.pay();
    }
}

Java EE 突击 14 - Spring AOP_第22张图片

CGLIB 动态代理实现(了解)

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.example.demo.service.AliPayService;
import org.example.demo.service.PayService;

import java.lang.reflect.Method;

public class PayServiceCGLIBInterceptor implements MethodInterceptor {
    //被代理对象
    private Object target;

    public PayServiceCGLIBInterceptor(Object target) {
        this.target = target;
    }

    @Override
    public Object intercept(Object o, Method method, Object[] args,
                            MethodProxy methodProxy) throws Throwable {
        //1.安全检查
        System.out.println("安全检查");
        //2.记录⽇志
        System.out.println("记录⽇志");
        //3.时间统计开始
        System.out.println("记录开始时间");
        //通过cglib的代理⽅法调⽤
        Object retVal = methodProxy.invoke(target, args);
        //4.时间统计结束
        System.out.println("记录结束时间");
        return retVal;
    }

    public static void main(String[] args) {
        PayService target = new AliPayService();
        PayService proxy = (PayService)
                Enhancer.create(target.getClass(), new
                        PayServiceCGLIBInterceptor(target));
        proxy.pay();
    }
}

也是要实现接口 , 重写里面的 invoke 方法
创建对象也不能去 new , 需要通过其他方式

JDK 和 CGLB 实现的区别(面试题)

  1. JDK 实现 , 要求被代理类必须实现接口 , 之后是通过 InvocationHandler 及 Proxy , 在运行时动态的在内存中生成了代理类对象 , 该代理对象是通过实现同样的接口实现 ( 类似静态代理接口实现的方式 ) , 只是该代理类是在运行期时 , 动态的织入统⼀的业务逻辑字节码来完成
  2. CGLIB 实现 , 被代理类可以不实现接口 , 是通过继承被代理类,在运行时动态的生成代理类对象
  3. CGLIB 实现要比 JDK 实现 性能要高

织入 (Weaving) : 代理的生成时机

织入就是 代理生成的时机
代理生成的时机分为三类 :

  1. 编译期 : 编译时期生成的代理 (运行之前)
  2. 类加载期 : 类加载的时候 , 生成代理 (预运行阶段)
  3. 运行期 : 运行代码的时候生成的代理对象

动态代理生成在代码运行期 , 动态织入到字节码

总结

  1. AOP 是什么 : AOP 是面向切面编程 , 他是对某一方面的功能去进行处理
  2. Spring AOP 是对 AOP 思想的具体实现

分为 : JDK 和 CGLB 两种功能去实现的

  1. 我们自己写的 Spring AOP , 是通过注解实现的 , 而这些注解 , 他来自于 ASpectJ , 而 AspectJ 又是来自于 Spring AOP 的

Java EE 突击 14 - Spring AOP_第23张图片
如果我们把 import 删除掉
Java EE 突击 14 - Spring AOP_第24张图片

  1. Spring AOP 实现步骤 :
    1. 添加 AOP 框架支持
    2. 定义切面和切点
    3. 定义通知 : 分为五种
  2. Spring AOP 是通过动态代理的方式 , 在运行期将 AOP 代码织入到程序中的

你可能感兴趣的:(JavaEE,进阶,java-ee,spring,java)