Spring AOP的5种通知类型

Posted 流楚丶格念

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring AOP的5种通知类型相关的知识,希望对你有一定的参考价值。

AOP通知类型

1. AOP通知分类

AOP通知描述了抽取的共性功能,根据共性功能抽取的位置不同,最终运行代码时要将其加入到合理的位置

  • AOP通知共分为5种类型
    • 前置通知:在切入点方法执行之前执行
    • 后置通知:在切入点方法执行之后执行,无论切入点方法内部是否出现异常,后置通知都会执行。
    • ==环绕通知(重点):==手动调用切入点方法并对其进行增强的通知方式。
    • 返回后通知:在切入点方法执行之后执行,如果切入点方法内部出现异常将不会执行。
    • 抛出异常后通知:在切入点方法执行之后执行,只有当切入点方法内部出现异常之后才执行。

2. AOP通知详解

2.1 前置通知

  • 名称:@Before
  • 类型:方法注解
  • 位置:通知方法定义上方
  • 作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法前运行
  • 范例:
@Before("pt()")
public void before() 
    System.out.println("before advice ...");

2.2 后置通知

  • 名称:@After
  • 类型:方法注解
  • 位置:通知方法定义上方
  • 作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法后运行
  • 范例:
@After("pt()")
public void after() 
    System.out.println("after advice ...");

2.3 返回后通知

  • 名称:@AfterReturning(了解)
  • 类型:方法注解
  • 位置:通知方法定义上方
  • 作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法正常执行完毕后运行
  • 范例:
@AfterReturning("pt()")
public void afterReturning() 
    System.out.println("afterReturning advice ...");

2.4 抛出异常后通知

  • 名称:@AfterThrowing(了解)
  • 类型:方法注解
  • 位置:通知方法定义上方
  • 作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法运行抛出异常后执行
  • 范例:
@AfterThrowing("pt()")
public void afterThrowing() 
    System.out.println("afterThrowing advice ...");

2.5 环绕通知

  • 名称:@Around(重点,常用)
  • 类型:方法注解
  • 位置:通知方法定义上方
  • 作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法前后运行
  • 范例::
@Around("pt()")
public Object around(ProceedingJoinPoint pjp) throws Throwable 
    System.out.println("around before advice ...");
    Object ret = pjp.proceed();
    System.out.println("around after advice ...");
    return ret;

环绕通知注意事项

  1. 环绕通知方法形参必须是ProceedingJoinPoint,表示正在执行的连接点,使用该对象的proceed()方法表示对原始对象方法进行调用,返回值为原始对象方法的返回值。
  2. 环绕通知方法的返回值建议写成Object类型,用于将原始对象方法的返回值进行返回,哪里使用代理对象就返回到哪里。
  3. 环绕通知中可以对原始方法调用过程中出现的异常进行处理
  4. 环绕通知可以隔离原始方法的调用执行

3. 代码案例

项目结构如下:

导入依赖:

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.yyl</groupId>
  <artifactId>spring_20_aop_advice_type</artifactId>
  <version>1.0-SNAPSHOT</version>

  <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.10.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.4</version>
    </dependency>
  </dependencies>
</project>

编写配置类,开启切面编程配置

package com.yyl.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan("com.yyl")
@EnableAspectJAutoProxy
public class SpringConfig 


编写接口与实现类

package com.yyl.dao;

public interface BookDao 
    public void update();

    public int select();


package com.yyl.dao.impl;

import com.yyl.dao.BookDao;
import org.springframework.stereotype.Repository;

@Repository
public class BookDaoImpl implements BookDao 

    public void update()
        System.out.println("book dao update is running ...");
    

    public int select() 
        System.out.println("book dao select is running ...");
//        int i = 1/0;
        return 100;
    


编写通知类,我们使用5种通知类型 分别配置 update与save方法上

package com.yyl.aop;

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

@Component
@Aspect
public class MyAdvice 
    @Pointcut("execution(void com.yyl.dao.BookDao.update())")
    private void pt()
    @Pointcut("execution(int com.yyl.dao.BookDao.select())")
    private void pt2()

    //@Before:前置通知,在原始方法运行之前执行
    @Before("pt()")
    public void before() 
        System.out.println("before advice ...");
    

    //@After:后置通知,在原始方法运行之后执行
    @After("pt2()")
    public void afterpt2() 
        System.out.println("after advice ...");
    

    //@After:后置通知,在原始方法运行之后执行
    @After("pt()")
    public void afterpt() 
        System.out.println("after advice ...");
    


    //@Around:环绕通知,在原始方法运行的前后执行
    @Around("pt()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable 
        System.out.println("around before advice ...");
        //表示对原始操作的调用
        Object ret = pjp.proceed();
        System.out.println("around after advice ...");
        return ret;
    

    @Around("pt2()")
    public Object aroundSelect(ProceedingJoinPoint pjp) throws Throwable 
        System.out.println("around before advice ...");
        //表示对原始操作的调用
        Integer ret = (Integer) pjp.proceed();
        System.out.println("around after advice ...");
        return ret;
    

    //@AfterReturning:返回后通知,在原始方法执行完毕后运行,且原始方法执行过程中未出现异常现象
    @AfterReturning("pt2()")
    public void afterReturning() 
        System.out.println("afterReturning advice ...");
    

    //@AfterThrowing:抛出异常后通知,在原始方法执行过程中出现异常后运行
    @AfterThrowing("pt2()")
    public void afterThrowing() 
        System.out.println("afterThrowing advice ...");
    


运行结果如下图所示:

接下来我们将save中的除0操作取消注释

int i = 1/0;

再运行,发现运行异常通知

以上是关于Spring AOP的5种通知类型的主要内容,如果未能解决你的问题,请参考以下文章

spring面试题

Spring-AOP的5种通知

SpringAOP学习之5种通知

spring aop的五种通知类型

Spring中Aop是如何使用的呢?

阅读Spring官网总结Aop用法