Spring基于注解使用AOP

1.1 AOP简介和作用

  • AOP(Aspect Oriented Programming)面向切面编程,一种编程范式,指导开发者如何组织程序结构
  • OOP(Object Oriented Programming)面向对象编程
  • 作用:在不惊动原始设计的基础上为其进行功能增强。简单的说就是在不改变方法源代码的基础上对方法进行功能增强。
  • Spring理念:无入侵式/无侵入式

1.2 AOP中的核心概念

连接点(JoinPoint):正在执行的方法,例如:update()、delete()、select()等都是连接点。

  • 切入点(Pointcut):进行功能增强了的方法,例如:update()、delete()方法,select()方法没有被增强所以不是切入点,但是是连接点。
  • 在SpringAOP中,一个切入点可以只描述一个具体方法,也可以匹配多个方法
    • 一个具体方法:com.itheima.dao包下的BookDao接口中的无形参无返回值的save方法
    • 匹配多个方法:所有的save方法,所有的get开头的方法,所有以Dao结尾的接口中的任意方法,所有带有一个参数的方法
  • 通知(Advice):在切入点前后执行的操作,也就是增强的共性功能
  • 在SpringAOP中,功能最终以方法的形式呈现
  • 通知类:通知方法所在的类叫做通知类
  • 切面(Aspect):描述通知与切入点的对应关系,也就是哪些通知方法对应哪些切入点方法。

2.2 AOP入门案例实现

导入aop相关坐标
<dependencies>
    <!--spring核心依赖,会将spring-aop传递进来-->
    <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>
定义通知类 定义切入点表达式、配置切面(绑定切入点与通知关系)
//&#x901A;&#x77E5;&#x7C7B;&#x5FC5;&#x987B;&#x914D;&#x7F6E;&#x6210;Spring&#x7BA1;&#x7406;&#x7684;bean
@Component
//&#x8BBE;&#x7F6E;&#x5F53;&#x524D;&#x7C7B;&#x4E3A;&#x5207;&#x9762;&#x7C7B;&#x7C7B;
@Aspect
public class MyAdvice {
    //&#x8BBE;&#x7F6E;&#x5207;&#x5165;&#x70B9;&#xFF0C;@Pointcut&#x6CE8;&#x89E3;&#x8981;&#x6C42;&#x914D;&#x7F6E;&#x5728;&#x65B9;&#x6CD5;&#x4E0A;&#x65B9;
    @Pointcut("execution(void com.itheima.dao.BookDao.update())")
    private void pt(){}

    //&#x8BBE;&#x7F6E;&#x5728;&#x5207;&#x5165;&#x70B9;pt()&#x7684;&#x524D;&#x9762;&#x8FD0;&#x884C;&#x5F53;&#x524D;&#x64CD;&#x4F5C;(&#x524D;&#x7F6E;&#x901A;&#x77E5;)
    @Before("pt()")
    public void method(){
    //&#x901A;&#x77E5;&#x65B9;&#x6CD5;
        System.out.println(System.currentTimeMillis());
    }
}
在配置类中进行Spring注解包扫描和开启AOP功能
@Configuration
@ComponentScan("通知类所在的包")
//开启注解开发AOP功能
@EnableAspectJAutoProxy
public class SpringConfig {
}

AOP切入点表达式

语法格式

  • 切入点:要进行增强的方法
  • 切入点表达式:要进行增强的方法的描述方式
  • 描述方式一:执行com.dao包下的BookDao接口中的无参数update方法
execution(void com.dao.BookDao.update())
  • 描述方式二:执行com.dao.impl包下的BookDaoImpl类中的无参数update方法
execution(void com.dao.impl.BookDaoImpl.update())
  • 切入点表达式标准格式:动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参数) 异常名)
execution(public User com.service.UserService.findById(int))
  • 动作关键字:描述切入点的行为动作,例如execution表示执行到指定切入点
  • 访问修饰符:public,private等,可以省略
  • 返回值:写返回值类型
  • 包名:多级包使用点连接
  • 类/接口名:
  • 方法名:
  • 参数:直接写参数的类型,多个类型用逗号隔开
  • 异常名:方法定义中抛出指定异常,可以省略

通配符

目的:可以使用通配符描述切入点,快速描述。

  • :单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现

匹配com包下的任意包中的UserService类或接口中所有find开头的带有一个参数的方法

execution&#xFF08;public * com.*.UserService.find*(*))
  • .. :多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写

匹配com包下的任意包中的UserService类或接口中所有名称为findById的方法

execution&#xFF08;public User com..UserService.findById(..))
  • +:专用于匹配子类类型
execution(* *..*Service+.*(..))

AOP通知类型

前置通知
  • 名称:@Before
  • 类型: 方法注解
  • 位置:通知方法定义上方
  • 作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法前运行
  • 范例:
@Before("pt()")
public void before() {
    System.out.println("before advice ...");
}
后置通知
  • 名称:@After
  • 类型: 方法注解
  • 位置:通知方法定义上方
  • 作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法后运行
  • 范例:
@After("pt()")
public void after() {
    System.out.println("after advice ...");
}
返回后通知
  • 名称:@AfterReturning(了解)
  • 类型: 方法注解
  • 位置:通知方法定义上方
  • 作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法正常执行完毕后运行
  • 范例:
@AfterReturning("pt()")
public void afterReturning() {
    System.out.println("afterReturning advice ...");
}
抛出异常后通知
  • 名称:@AfterThrowing(了解)
  • 类型: 方法注解
  • 位置:通知方法定义上方
  • 作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法运行抛出异常后执行
  • 范例:
@AfterThrowing("pt()")
public void afterThrowing() {
    System.out.println("afterThrowing advice ...");
}
5.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类型,用于将原始对象方法的返回值进行返回,哪里使用代理对象就返回到哪里。

AOP切入点数据获取

2 获取参数

说明:在前置通知和环绕通知中都可以获取到连接点方法的参数们

  • JoinPoint对象描述了连接点方法的运行状态,可以获取到原始方法的调用参数
@Before("pt()")
public void before(JoinPoint jp) {
    Object[] args = jp.getArgs(); //&#x83B7;&#x53D6;&#x8FDE;&#x63A5;&#x70B9;&#x65B9;&#x6CD5;&#x7684;&#x53C2;&#x6570;&#x4EEC;
    System.out.println(Arrays.toString(args));
}
  • ProccedJointPoint是JoinPoint的子类
@Around("pt()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
    Object[] args = pjp.getArgs(); //&#x83B7;&#x53D6;&#x8FDE;&#x63A5;&#x70B9;&#x65B9;&#x6CD5;&#x7684;&#x53C2;&#x6570;&#x4EEC;
    System.out.println(Arrays.toString(args));
    Object ret = pjp.proceed();
    return ret;
}

2.2 获取返回值

说明:在返回后通知和环绕通知中都可以获取到连接点方法的返回值

  • 抛出异常后通知可以获取切入点方法中出现的异常信息,使用形参可以接收对应的异常对象
@AfterReturning(value = "pt()",returning = "ret")
public void afterReturning(String ret) { //&#x53D8;&#x91CF;&#x540D;&#x8981;&#x548C;returning="ret"&#x7684;&#x5C5E;&#x6027;&#x503C;&#x4E00;&#x81F4;
    System.out.println("afterReturning advice ..."+ret);
}
  • 环绕通知中可以手工书写对原始方法的调用,得到的结果即为原始方法的返回值
@Around("pt()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
    // &#x624B;&#x52A8;&#x8C03;&#x7528;&#x8FDE;&#x63A5;&#x70B9;&#x65B9;&#x6CD5;&#xFF0C;&#x8FD4;&#x56DE;&#x503C;&#x5C31;&#x662F;&#x8FDE;&#x63A5;&#x70B9;&#x65B9;&#x6CD5;&#x7684;&#x8FD4;&#x56DE;&#x503C;
    Object ret = pjp.proceed();
    return ret;
}

2.3 获取异常

说明:在抛出异常后通知和环绕通知中都可以获取到连接点方法中出现的异常

  • 抛出异常后通知可以获取切入点方法中出现的异常信息,使用形参可以接收对应的异常对象
@AfterThrowing(value = "pt()",throwing = "t")
public void afterThrowing(Throwable t) {//&#x53D8;&#x91CF;&#x540D;&#x8981;&#x548C;throwing = "t"&#x7684;&#x5C5E;&#x6027;&#x503C;&#x4E00;&#x81F4;
    System.out.println("afterThrowing advice ..."+ t);
}
@Around("pt()")
public Object around(ProceedingJoinPoint pjp)  {
    Object ret = null;
    //此处需要try...catch处理,catch中捕获到的异常就是连接点方法中抛出的异常
    try {
        ret = pjp.proceed();
    } catch (Throwable t) {
        t.printStackTrace();
    }
    return ret;
}

Original: https://www.cnblogs.com/icemomo/p/16209985.html
Author: 冰莫莫
Title: Spring基于注解使用AOP

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/580395/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • JavaWeb-Tomcat简介

    为了能让 web服务器与 web应用进行协作,首先应该由一个中介方制定web应用于web服务器进行协作的标准接口, Servlet就是其中最主要的一个接口。中介方规定: web服务…

    Java 2023年5月29日
    0146
  • 【转载】JAVA基础:注解

    一、认识注解 注解(Annotation)很重要,未来的开发模式都是基于注解的,JPA是基于注解的,Spring2.5以上都是基于注解的,Hibernate3.x以后也是基于注解的…

    Java 2023年5月29日
    0113
  • 我是如何将一个老系统的kafka消费者服务的性能提升近百倍的

    大家好,又见面了~ kafka作为一种高吞吐量的分布式发布订阅消息系统,在业务系统中被广泛的使用。 如果问你,如何提高kafka队列中的消息消费速度呢?答案很简单,topic多分几…

    Java 2023年6月7日
    089
  • node热加载

    node可以通过require热加载文件,这里先提一下require的加载方式:当我们第一次使用require加载模块时require会把被加载文件的绝对路径作为key存放在req…

    Java 2023年6月5日
    0101
  • CSS相关知识及入门

    修饰HTML页面,美化 CSS代码规范 CSS选择器 就是以HTML中的标签名作为选择器名称 选择CSS代码作用于对应标签名的标签上 适用于将相同样式作用于多个同名标签上 给相应的…

    Java 2023年6月6日
    068
  • 记批量生成二维码并返回压缩包

    记批量生成二维码并返回压缩包 添加依赖包—如果3.5.0用不了可以切换为3.4.1 com.google.zxing core 3.5.0 com.google.zxi…

    Java 2023年6月14日
    0133
  • 自定义视图(自定义属性)

    我们先把所需要到属性定义好,在res/values/目录下新建xml文件attrs.xml,此文件定义了所有需要到属性,为了说明这个过程就定义了一个attr_title属性。如下所…

    Java 2023年6月7日
    078
  • win10添加或取消管理员账号

    win10添加或取消administrator账号,输入命令开启:net user administrator /active:yes,输入命令关闭:net user admini…

    Java 2023年6月5日
    091
  • Nginx报504 gateway timeout错误的解决方法

    一、今天登录我的网站,突然发现报了下面的一个错误: 我的第一反应是:超时了应该是Nginx代理没有设置超时时间,默认的超时时间估计太小了,然后就按照正常的方式用Xshell连接服务…

    Java 2023年6月13日
    068
  • Mysql异常——com.alibaba.druid.sql.parser.ParserException

    今天写业务逻辑时候,写完发现控制台出现报错,但是程序可以正常运行。在控制台报错中发现是因为SQL模糊查询格式问题 修改模糊查询 写法后 成功解决该报错 Original: http…

    Java 2023年6月9日
    096
  • SpringWebflux中WebClient怎么打印日志

    一、背景 去年高峰压测的时候,有个服务是专门调用其它系统的,在测试接口http请求的时候,那TPS唰唰的往下掉,还专门用Arthas看了一下方法执行时间,那家伙,我sleep 2s…

    Java 2023年6月15日
    095
  • RabbitMQ镜像队列集群搭建、与SpringBoot整合

    服务器IP hostname 节点说明 端口 管控台地址 192.168.2.121 zhouhong121 rabbitmq master 5672 http://192.168…

    Java 2023年6月13日
    0107
  • WebSocket 服务端未启动时,客户端重连报错

    当WebSocket服务端未启动时,我们在客户端申请连接,会报 System.Net.Sockets.SocketException 异常。 当然,我们调试时异常设置默认是不勾选这…

    Java 2023年5月30日
    069
  • logstash在windows系统下的安装与使用

    前言:Logstash 是开源的服务器端数据处理管道,能够同时从多个来源采集数据,转换数据,然后将数据发送到 Elasticsearch。 ES官网:https://www.ela…

    Java 2023年6月16日
    090
  • git 学习笔记

    git status (-s) 查看文件状态git init 创建本地的git仓库git clone 克隆仓库git add [文件名] 添加到缓存区git rest HEAd […

    Java 2023年6月9日
    078
  • HashMap中红黑树插入节点的调整过程

    一、引言 二、HashMap源码中红黑树插入节点的调整过程 三、阅读HashMap源码的一些Tips 1. 代码风格 2. 变量名 balanceInsertion方法中的变量名 …

    Java 2023年6月6日
    080
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球