原文:190301-SpringBoot基础篇AOP之基本使用姿势小结

一般来讲,谈到Spring的特性,绕不过去的就是DI(依赖注入)和AOP(切面),在将bean的系列中,说了DI的多种使用姿势;接下来看一下AOP的玩法

I. 背景知识

在实际使用之前有必要了解一下什么是AOP,以及AOP的几个基本概念

1. advice

  • before: 在方法执行之前被调用
  • after: 在方法执行之后调用
  • after returning: 方法执行成功之后
  • after throwing: 方法抛出异常之后
  • around: 环绕,自己在内部决定方法的执行时机,因此可以在之前之后做一些业务逻辑

2. join point

连接点,比如方法调用,方法执行,字段设置/获取、异常处理执行、类初始化、甚至是 for 循环中的某个点

但 Spring AOP 目前仅支持方法执行 (method execution)

简单来说,Spring AOP中,PointCut就是那个被拦截的方法

3. pointcut

切点,用来描述满足什么规则的方法会被拦截

  • 正则表达式 : @Before("execution(public * com.git.hui.demo.base.bean.*.*(..))")
  • 注解拦截方式 :@Around("@annotation(parameterCheck)")

4. aspect

切面是切点和通知的结合。通知和切点共同定义了关于切面的全部内容,它是什么时候,在何时和何处完成功能

5. introduction

引入允许我们向现有的类添加新的方法或者属性

6. weaving

组装方面来创建一个被通知对象。这可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。

简单来讲就是生成一个代理类,在调用被拦截的方法时,实际上执行的是代理类,这个代理类内部执行切面逻辑

II. 使用说明

1. 基本配置

首先是基本环境的搭建, 先贴上必要的xml配置, 使用aop需要引入包: spring-boot-starter-aop

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.0.4.RELEASE</version><relativePath/> <!-- lookup parent from repository -->
</parent><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><spring-cloud.version>Finchley.RELEASE</spring-cloud.version><java.version>1.8</java.version>
</properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency>
</dependencies><build><pluginManagement><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></pluginManagement>
</build><repositories><repository><id>spring-milestones</id><name>Spring Milestones</name><url>https://repo.spring.io/milestone</url><snapshots><enabled>false</enabled></snapshots></repository>
</repositories>
复制代码

2. 代码准备

首先创建一个被拦截的bean: com.git.hui.boot.aop.demo.DemoBean,如下

@Component
public class DemoBean {/*** 返回随机的字符串** @param time* @return*/public String randUUID(long time) {try {System.out.println("in randUUID before process!");return UUID.randomUUID() + "|" + time;} finally {System.out.println("in randUUID finally!");}}
}
复制代码

接着在启动类中,执行

@SpringBootApplication
public class Application {public Application(DemoBean demoBean) {String ans = demoBean.randUUID(System.currentTimeMillis());System.out.println("----- ans: " + ans + "---------");}public static void main(String[] args) {SpringApplication.run(Application.class);}
}
复制代码

3. AOP使用

在实际使用之前,需要创建一个切面,用@Aspect声明,其次切面也需要作为bean托付给Spring容器管理

@Aspect
@Component
public class AnoAspcet {
}
复制代码

a. before

在方法调用之前,需要执行一些操作,这个时候可以使用 @Before 注解来声明before advice

一种可使用姿势如下,我们的切点直接在注解中进行定义,使用正则表达式的方式

@Before("execution(public * com.git.hui.boot.aop.demo.*.*(*))")
public void doBefore(JoinPoint joinPoint) {System.out.println("do in Aspect before method called! args: " + JSON.toJSONString(joinPoint.getArgs()));
}
复制代码

b. after

在方法调用完毕之后,再执行一些操作,这个时候after就可以派上用场,为了考虑切点的通用性,我们可以考虑声明一个切点,使用@Pointcut注解

@Pointcut("execution(public * com.git.hui.boot.aop.demo.*.*(*))")
public void point() {
}
复制代码

使用pointcut的方式也比较简单,如下

@After("point()")
public void doAfter(JoinPoint joinPoint) {System.out.println("do in Aspect after method called! args: " + JSON.toJSONString(joinPoint.getArgs()));
}
复制代码

c. after returning

在正常返回结果之后,再次执行,这个也挺有意思的,通常使用这个advice时,一般希望获取返回结果,那么应该怎么处理呢?

  • org.aspectj.lang.annotation.AfterReturning#returning 指定返回结果对应参数name
  • 返回结果作为参数传入,要求类型一致,否则不生效
/*** 执行完毕之后,通过 args指定参数;通过 returning 指定返回的结果,要求返回值类型匹配** @param time* @param result*/
@AfterReturning(value = "point() && args(time)", returning = "result")
public void doAfterReturning(long time, String result) {System.out.println("do in Aspect after method return! args: " + time + " ans: " + result);
}
复制代码

d. around

这个也比较常见,在方法执行前后干一些事情,比如常见的耗时统计,日志打印,安全控制等,很多都是基于around advice实现的

使用这个advice需要注意的是传入参数类型为 ProceedingJoinPoint,需要在方法内部显示执行org.aspectj.lang.ProceedingJoinPoint#proceed()来表示调用方法

@Around("point()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {System.out.println("do in Aspect around ------ before");Object ans = joinPoint.proceed();System.out.println("do in Aspect around ------- over! ans: " + ans);return ans;
}
复制代码

e. 输出

执行之后输出如下

do in Aspect around ------ before
do in Aspect before method called! args: [1551433188205]
in randUUID before process!
in randUUID finally!
do in Aspect around ------- over! ans: 6849544b-160e-464c-80bd-641f2651c6c1|1551433188205
do in Aspect after method called! args: [1551433188205]
do in Aspect after method return! args: 1551433188205 ans: 6849544b-160e-464c-80bd-641f2651c6c1|1551433188205
----- ans: 6849544b-160e-464c-80bd-641f2651c6c1|1551433188205---------
复制代码

从输出结果上,可以看到每个advice的使用范围,当然也带来了一些疑问

  • 可以存在多个同类型的advice,拦截同一个目标吗?(如两个around都拦截methodA方法,那么methodA方法被调用时,两个around advice是否都会执行)
  • 多个advice之间的优先级怎么定义?
  • aop拦截的目标方法有没有限制(对非public的方法可以拦截么?)
  • 被拦截的方法中存在相互调用的时候,会怎样?(如methodA,methodB都可以被拦截,且methodA中调用了methodB,那么在执行methodA时,methodB的各种advice是否会被触发?)
  • 基于注解的aop方式可以怎样用

以上这些问题留在下一篇进行介绍

III. 其他

0. 项目

  • 工程:github.com/liuyueyi/sp…
  • 项目: github.com/liuyueyi/sp…

1. 一灰灰Blog

  • 一灰灰Blog个人博客 blog.hhui.top
  • 一灰灰Blog-Spring专题博客 spring.hhui.top

一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

2. 声明

尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激

  • 微博地址: 小灰灰Blog
  • QQ: 一灰灰/3302797840

3. 扫描关注

一灰灰blog

知识星球

SpringBoot基础篇AOP之基本使用姿势小结相关推荐

  1. 动力节点王鹤SpringBoot3学习笔记——第二章 掌握SpringBoot基础篇

    目录 二.掌控SpringBoot基础篇 2.1 Spring Boot ? 2.1.1 与Spring关系 2.1.2 与SpringCloud关系 2.1.3  最新的Spring Boot3 新 ...

  2. SpringBoot SpringBoot 基础篇(第一篇) 第2章 SpringBoot 全局配置 2.2 yaml 文件

    SpringBoot [千锋教育java教程SpringBoot2全套,springboot快速入门到项目实战视频教程] SpringBoot 基础篇(第一篇) 第2章 SpringBoot 全局配置 ...

  3. SpringBoot基础篇Bean之基本定义与使用

    更多Spring文章,欢迎点击 一灰灰Blog-Spring专题 我们知道在Spring中,有两个非常有名的特性,依赖注入(DI)与切面(AOP),其中依赖注入其主要的作用,可以说就是维护Spring ...

  4. SpringBoot基础篇Bean之条件注入之注解使用

    bean的条件注入,除了前面一篇博文中介绍的通过@Conditional注解配合Condition接口的实现之外,还提供了更多简化的注解使用方式,省略了自己实现Condtion接口,本篇博文主要介绍下 ...

  5. 黑马SpringBoot --基础篇

    目录 导读 1.快速上手 1.1SpringBoot入门程序开发 1.1.1基础创建方式--常规 1.1.2手动创建方式--补充 1.1.3基础创建方式--下载 1.1.4基础创建方式--阿里云 1. ...

  6. SpringBoot基础篇日志管理之logback配置文件

    1.美图 2.概述 SpringBoot默认选用logback进行日志管理,前一篇讲述了默认配置日志的参数,然而这些内容比较初级,并不一定能满足我们更加特殊的需求(比如输出到多个不同的配置文件,不同的 ...

  7. springboot基础篇(快速入门 + 完整项目案例)

    目录 1.springboot简介 2.REST风格 1.简介 2.RESTful 3.复制工程 4.属性配置 1.application.properties 2.application.yml.. ...

  8. SpringBoot基础篇

    配置maven file-setting-build-build tool 将项目配置成可执行jar 1打包 mavenProject-lifecircle-package 2寻找 target 3执 ...

  9. SpringBoot基础篇配置信息之配置刷新

    2019独角兽企业重金招聘Python工程师标准>>> 更多Spring文章,欢迎点击 一灰灰Blog-Spring专题 配置的刷新,从第一篇就提出了这个问题,但是一直都没有说到,那 ...

最新文章

  1. RequisitePro SQL SERVER数据库的配置
  2. sklearn中的cross_val_score交叉验证
  3. 文本向量表示及TFIDF词汇权值
  4. 详解Scala函数也是对象的特性
  5. php调用C代码的方法详解和zend_parse_parameters函数详解
  6. 宜人贷蜂巢API网关技术解密之Netty使用实践
  7. 19个神经元控制自动驾驶汽车,MIT等虫脑启发新研究登Nature子刊
  8. 仅有 100k 参数的高效显著性目标检测方法
  9. 【elasticsearch】You AutoComplete Me
  10. 线性模型第4讲:弹性网络
  11. MyEclipse搭建安卓环境
  12. TheFatRat免杀
  13. 卡尔滤波算法 java_卡尔曼滤波算法及其代码
  14. _stdcall相关
  15. 环境猫室内监测仪——您的空气智能管家
  16. 基于word2vec+TextCNN 实现中文文本分类
  17. 【Excel 教程系列第 6 篇】制作斜线表头
  18. 2022国赛正式题nfs 解题
  19. 【T1T2】签到题集合
  20. python实现火车票查询_基于python的火车票查询工具

热门文章

  1. K8S——单master节点和基于单master节点的双master节点二进制部署(本机实验,防止卡顿,所以多master就不做3台了)
  2. hadoop 实战练习_Hadoop 实战实例
  3. linux递归创建文件夹_Python中并发请求创建文件夹带来的线程安全问题
  4. c++ 0x8000ffff灾难性故障_《可靠性设计》——故障模式影响分析
  5. Juniper 210 密码清不掉_三分钟学会如何找回mysql密码
  6. mysql触发器行锁_MySQL 之 视图、触发器、存储过程、函数、事物与数据库锁
  7. java宏定义_现代化的 Java (二十六)—— Akka Stream Graph
  8. 常用android的smali注入代码,android smali代码注入 实战一
  9. linux卸载模块驱动程序,Linux设备驱动程序 之 装载和卸载模块
  10. 未发现数据源名称_在Power BI中管理数据源Analysis Services