一文搞懂Spring AOP的五大通知类型

目录
  • 一、通知类型
  • 二、环境准备
    • 添加AOP依赖
    • 创建目标接口和实现类
    • 创建通知类
    • 创建Spring核心配置类
    • 编写运行程序
  • 三、添加通知
    • 普通通知
    • 环绕通知(重点)

一、通知类型

Advice 直译为通知,也有人翻译为 “增强处理”,共有 5 种类型,如下表所示。

通知类型 注解 说明
before(前置通知) @Before 通知方法在目标方法调用之前执行
after(后置通知) @After 通知方法在目标方法返回或异常后调用
after-returning(返回通知) @AfterReturning 通知方法会在目标方法返回后调用
after-throwing(异常通知) @AfterThrowing 通知方法会在目标方法抛出异常后调用
around(环绕通知) @Around 通知方法会将目标方法封装起来

二、环境准备

添加AOP依赖

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

<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>

创建目标接口和实现类

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

创建通知类

创建通知类,并指定切入点

/*通知类*/
@Component//将这个类定义成 Bean
@Aspect//将这个Bean定义为切面
public class MyAdvice {
    //指定UserDao类中的save方法为切入点
    @Pointcut("execution(void com.bighorn.dao.UserDao.save())")
    private void pt1(){}
    //指定UserDao类中的update方法为切入点
    @Pointcut("execution(int com.bighorn.dao.UserDao.update())")
    private void pt2(){}
}

创建Spring核心配置类

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

编写运行程序

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

三、添加通知

普通通知

MyAdvice这个通知类中添加前置通知后置通知返回后通知异常后通知及相关注解。

//前置通知
@Before("pt1()")
public void before() {
    System.out.println("before advice ...");
}
//后置通知
@After("pt1()")
public void after() {
    System.out.println("after advice ...");
}
//返回后通知
@AfterReturning("pt1()")
public void afterReturning() {
    System.out.println("afterReturning advice ...");
}
//异常后通知
@AfterThrowing("pt1()")
public void afterThrowing() {
    System.out.println("afterThrowing advice ...");
}

观察运行App程序后的截图,发现并没有显示异常后通知

手动在save()方法中添加一行代码:int i = 1/0,造成异常后再次运行App。

发现异常后通知有了,但是运行后通知却消失了。

综上所述: 前置通知和后置通知是一定会执行的,而返回后通知是需要在原始方法正常执行后才会被执行,异常后通知是需要原始方法抛出异常才会被执行

环绕通知(重点)

环绕通知是非常强大的通知,能够完成上述四种通知的所有功能。

/**
     * 环绕通知需要携带ProceedingJoinPoint类型的参数
     * 环绕通知类似于动态代理的全过程:ProceedingJoinPoint类型的参数可以决定是否执行目标方法
     * 环绕通知必须要有返回值,返回值即为目标方法的返回值
     * @param pjp
     * @return Object
     */
@Around("pt2()")
public Object around(ProceedingJoinPoint pjp) {
    Object result = null;
    try {
        System.out.println("这是环绕通知中的前置通知......");
        //执行目标方法
        result = pjp.proceed();
        System.out.println("这是环绕通知中的返回通知......");
    } catch (Throwable e) {
        System.out.println("这是环绕通知中的异常通知......");
    }
    System.out.println("这是环绕通知中的后置通知......");
    return result;
}

修改App类,调用UserDao的update()方法,运行程序,观察结果。

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

运行结果如下

注意点

使用环绕通知必须传入形参ProceedingJoinPoint,并使用pjp.proceed()方法实现对原始方法的调用,进而实现原始方法调用前后同时添加通知

通知中如果未使用使用pjp.proceed()方法实现对原始方法的调用,则将跳过原始方法的执行

原始方法的返回值类型决定环绕通知的返回值类型。原始方法若不接收返回值,通知方法的返回值类型可以设置成void,也可以设置成Object;如果接收返回值,最好设定为Object类型

由于无法预知原始方法运行后是否会抛出异常,因此环绕通知方法必须要处理Throwable异常

以上就是一文搞懂Spring AOP的五大通知类型的详细内容,更多关于Spring AOP通知类型的资料请关注我们其它相关文章!

(0)

相关推荐

  • SpringBoot通过AOP与注解实现入参校验详情

    目录 前言: 注解标记 通过AOP对方法进行增强 测试Get请求 测试POST请求 解决方法代码 再次测试POST请求 前言: 问题源头: 在日常的开发中,在Service层经常会用到对某一些必填参数进行是否存在的校验.比如我在写一个项目管理系统: 这种必填参数少一些还好,如果多一些的话光是if语句就要写一堆.像我这种有代码洁癖的人看着这一堆无用代码更是难受. 如何解决: 在Spring里面有一个非常好用的东西可以对方法进行增强,那就是AOP.AOP可以对方法进行增强,比如:我要校验参数是否存在

  • Spring 循环依赖之AOP实现详情

    前言: 我们接着上一篇文章继续往下看,首先看一下下面的例子,前面的两个serviceA和serviceB不变,我们添加一个BeanPostProcessor: @Component public class MyPostProcessor implements BeanPostProcessor { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansEx

  • Spring源码解析之循环依赖的实现流程

    目录 前言 循环依赖实现流程 前言 上篇文章中我们分析完了Spring中Bean的实例化过程,但是没有对循环依赖的问题进行分析,这篇文章中我们来看一下spring是如何解决循环依赖的实现. 之前在讲spring的过程中,我们提到了一个spring的单例池singletonObjects,用于存放创建好的bean,也提到过这个Map也可以说是狭义上的spring容器. private final Map<String, Object> singletonObjects = new Concurr

  • SpringBoot2.6.x默认禁用循环依赖后的问题解决

    目录 一.序言 二.问题复原 1.代码说明 2.错误示例 三.问题解决 1.粗暴解决 2.优雅解决 四.小结 一.序言 SpringBoot 2.6.x不推荐使用循环依赖,这是一个好消息,SpringBoot从底层逐渐引导开发者书写规范的代码,同时也是个忧伤的消息,循环依赖的应用场景实在是太广泛了. 如果从低版本升级到2.6.x,那么很大概率遇到的第一个问题便是循环依赖问题. 二.问题复原 1.代码说明 下面风格的代码比较普遍:两个类都有调用对方方法的需求,因此很容易写成循环引用. @Servi

  • SpringBoot使用AOP实现统计全局接口访问次数详解

    目录 AOP是什么 AOP的作用和优势 常见的动态代理技术 AOP相关概念 实现 AOP是什么 AOP(Aspect Oriented Programming),也就是面向切面编程,是通过预编译方式和运行期间动态代理实现程序功能的传统已维护的一种技术. AOP的作用和优势 作用:在程序运行期间,在不修改源代码的情况下对某些方法进行功能增强 优势:减少重复代码,提高开发效率,并且便于维护 常见的动态代理技术 jdk代理:基于接口的动态代理技术 cglib代理:基于父类的动态代理技术 AOP相关概念

  • 使用Spring AOP实现用户操作日志功能

    目录 我使用Spring AOP实现了用户操作日志功能 需求分析 功能实现 1. 需要一张记录日志的 Log 表 导出的 sql 如下: 2.我使用的是 Spring Boot 所以需要引入 spring aop 的 starter 3.Log 实体类 4.ILog 注解 5.切面类 LogAspect 总结 我使用Spring AOP实现了用户操作日志功能 今天答辩完了,复盘了一下系统,发现还是有一些东西值得拿出来和大家分享一下. 需求分析 系统需要对用户的操作进行记录,方便未来溯源 首先想到

  • 关于SpringBoot禁止循环依赖解说

    前言: Spring的Bean管理,一直是整个体系中津津乐道的东西.尤其是Bean的循环依赖,更是很多面试官最喜欢考察的2B知识点之一. 但事实上,项目中存在Bean的循环依赖,是代码质量低下的表现.多数人寄希望于框架层来给擦屁股,造成了整个代码的设计越来越糟,最后用一些奇技淫巧来填补犯下的错误. 还好,SpringBoot终于受不了这种滥用,默认把循环依赖给禁用了! 从2.6版本开始,如果你的项目里还存在循环依赖,SpringBoot将拒绝启动! 验证代码小片段: 为了验证这个功能,我们只需要

  • 教你Spring如何使用三级缓存解决循环依赖

    一级缓存存放实例化对象 .二级缓存存放已经在内存空间创建好但是还没有给属性赋值的对象.三级缓存存放对象工厂,用来创建提前暴露到bean的对象. @Service public class TestService1 { @Autowired private TestService2 testService2; public void test1() { } } @Service public class TestService2 { @Autowired private TestService1

  • 一文搞懂Spring AOP的五大通知类型

    目录 一.通知类型 二.环境准备 添加AOP依赖 创建目标接口和实现类 创建通知类 创建Spring核心配置类 编写运行程序 三.添加通知 普通通知 环绕通知(重点) 一.通知类型 Advice 直译为通知,也有人翻译为 “增强处理”,共有 5 种类型,如下表所示. 通知类型 注解 说明 before(前置通知) @Before 通知方法在目标方法调用之前执行 after(后置通知) @After 通知方法在目标方法返回或异常后调用 after-returning(返回通知) @AfterRet

  • 一文搞懂Spring中的Bean作用域

    目录 概述 Singleton prototype request session application 概述 scope用来声明容器中的对象所应该处的限定场景或者说该对象的存活时间,即容器在对象进入其 相应的scope之前,生成并装配这些对象,在该对象不再处于这些scope的限定之后,容器通常会销毁这些对象. Spring容器bean的作用域类型: singleton:Spring IoC 容器的单个对象实例作用域都默认为singleton prototype:针对声明为拥有prototyp

  • 一文搞懂Spring中的注解与反射

    目录 前言 一.内置(常用)注解 1.1@Overrode 1.2@RequestMapping 1.3@RequestBody 1.4@GetMapping 1.5@PathVariable 1.6@RequestParam 1.7@ComponentScan 1.8@Component 1.9@Service 1.10@Repository 二.元注解 @Target @Retention @Documented @Inherited 三.自定义注解 四.反射机制概述 4.1动态语言与静态语

  • 一文搞懂Spring Bean中的作用域和生命周期

    目录 一.Spring Bean 作用域 singleton(单例) prototype(原型) 小结 二.Spring Bean生命周期 如何关闭容器 生命周期回调 通过接口设置生命周期 通过xml设置生命周期 一.Spring Bean 作用域 常规的 Spring IoC 容器中Bean的作用域有两种:singleton(单例)和prototype(非单例) 注:基于Web的容器还有其他种作用域,在这就不赘述了. singleton(单例) singleton是Spring默认的作用域.当

  • 一文搞懂Spring中Bean的生命周期

    目录 一.使用配置生命周期的方法 二.生命周期控制——接口控制(了解) 小结 生命周期:从创建到消亡的完整过程 bean声明周期:bean从创建到销毁的整体过程 bean声明周期控制:在bean创建后到销毁前做一些事情 一.使用配置生命周期的方法 在BookDaoImpl中实现类中创建相应的方法: //表示bean初始化对应的操作 public void init(){ System.out.println("init..."); } //表示bean销毁前对应的操作 public v

  • 一文搞懂Spring循环依赖的原理

    目录 简介 循环依赖实例 测试 简介 说明 本文用实例来介绍@Autowired解决循环依赖的原理.@Autowired是通过三级缓存来解决循环依赖的. 除了@Autoired,还有其他方案来解决循环依赖的,见:Spring循环依赖的解决方案详解 概述 @Autowired进行属性注入可以解决循环依赖.原理是:Spring控制了bean的生命周期,先实例化bean,后注入bean的属性.Spring中记录了正在创建中的bean(已经实例化但还没初始化完毕的bean),所以可以在注入属性时,从记录

  • 一文搞懂Spring中@Autowired和@Resource的区别

    目录 1.来源不同 2.依赖查找顺序不同 2.1 @Autowired 查找顺序 2.2 @Resource 查找顺序 2.3 查找顺序小结 3.支持的参数不同 4.依赖注入的支持不同 5.编译器提示不同 总结 @Autowired 和 @Resource 都是 Spring/Spring Boot 项目中,用来进行依赖注入的注解.它们都提供了将依赖对象注入到当前对象的功能,但二者却有众多不同,并且这也是常见的面试题之一,所以我们今天就来盘它. @Autowired 和 @Resource 的区

  • 一文搞懂spring boot本地事务@Transactional参数

    目录 1. 本地事务 1.1. 基本概念 1.2. 隔离级别 1.3. 相关命令 1.4. 传播行为 1.4.1. 伪代码练习 1.4.2. 改造商品新增代码 1.4.3. 测试1:同一service + requires_new 1.4.4. 测试2:不同service + requires_new 1.4.5. 在同一个service中使用传播行为 1.5. 回滚策略 1.5.1. 测试编译时异常不回滚 1.5.2. 定制回滚策略 1.6. 超时事务 1.7. 只读事务 1. 本地事务 商品

  • 一文搞懂Spring中的JavaConfig

    目录 配置类 注册组件 扫描包配置 事务注解驱动 单元测试加载配置类 properties配置文件加载(了解) aspectj注解开关 传统spring一般都是基于xml配置的,不过后来新增了许多JavaConfig的注解.特别是springboot,基本都是清一色的java config,不了解一下,还真是不适应.这里给大家普及下Spring中的JavaConfig知识. 什么是JavaConfig.通过注解和配置类完成Spring的相关配置 Spring配置都做了什么? 注册组件.其他配置(

  • 一文搞懂如何实现Java,Spring动态启停定时任务

    目录 为什么需要定时任务 Java定时任务的原理 Timer+TimerTask ScheduledThreadPoolExecutor Timer VS ScheduledThreadPoolExecutor Spring定时任务 @Scheduled定时任务原理(源码) 为什么需要定时任务 定时任务的应用场景十分广泛,如定时清理文件.定时生成报表.定时数据同步备份等. Java定时任务的原理 jdk自带的库中,有两种技术可以实现定时任务,一种是Timer,另一种是ScheduledThrea

随机推荐