SpringBoot基础篇AOP之基本使用姿势小结
一般来讲,谈到Spring的特性,绕不过去的就是DI(依赖注入)和AOP(切面),在将bean的系列中,说了DI的多种使用姿势;接下来看一下AOP的玩法
<!-- more -->
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 www.mcyulegw.com before method called! args: " + JSON.toJSONString(joinPoint.getArgs()));
}
b. after
在方法调用完毕之后,再执行一些操作,这个时候after就可以派上用场,为了考虑切点的通用性,我们可以考虑声明一个切点,使用@Pointcut注解
@Pointcut("execution(public * com.git.hui.boot.aop.demo.*.*(*))")
public void point(www.yongshiyule178.com) {
}
使用pointcut的方式也比较简单,如下
@After("point(www.dfgjpt.com)")
public void doAfter(JoinPoint joinPoint) {
System.out.println("do in Aspect www.yongshi123.cn 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(www.baohuayule.net/) && args(time)", returning = "result")
public void doAfterReturning(www.michenggw.com long time, String result) {
System.out.println("do in Aspect after method www.jiahuayulpt.com 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. 项目
工程:https://www.dashuju2.cn github.com/liuyueyi/spring-boot-demo
项目: https://www.yibaoyule1.com www.tianjuyuLe.cn github.com/liuyueyi/spring-boot-demo/tree/master/spring-boot/010-aop
1. 一灰灰Blog
一灰灰Blog个人博客 https://www.maituyul1.cn blog.hhui.top
一灰灰Blog-Spring专题博客 http://www.wujirongyaoy.com spring.hhui.top
一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛
2. 声明
尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激
微博地址: 小灰灰Blog
SpringBoot基础篇AOP之基本使用姿势小结的更多相关文章
- SpringCloud基础篇AOP之拦截优先级详解
前面两篇分别介绍了AOP的基本使用姿势和一些高级特性,当时还遗留了一个问题没有说明,即不同的advice,拦截同一个目标方法时,优先级是怎样的,本篇博文将进行详细分析 同一个切面中,不同类型的advi ...
- (二)SpringBoot基础篇- 静态资源的访问及Thymeleaf模板引擎的使用
一.描述 在应用系统开发的过程中,不可避免的需要使用静态资源(浏览器看的懂,他可以有变量,例:HTML页面,css样式文件,文本,属性文件,图片等): 并且SpringBoot内置了Thymeleaf ...
- 视频作品《springboot基础篇》上线了
1.场景描述 第一个视频作品出炉了,<springboot基础篇>上线了,有需要的朋友可以直接点击链接观看.(如需购买,请通过本文链接购买) 2. 课程内容 课程地址:https://ed ...
- SpringBoot基础篇-SpringBoot快速入门
SpringBoot基础 学习目标: 能够理解Spring的优缺点 能够理解SpringBoot的特点 能够理解SpringBoot的核心功能 能够搭建SpringBoot的环境 能够完成applic ...
- Springboot基础篇
Springboot可以说是当前最火的java框架了,非常适合于"微服务"思路的开发,大幅缩短软件开发周期. 概念 过去Spring充满了配置bean的xml文件,随着spring ...
- (一)SpringBoot基础篇- 介绍及HelloWorld初体验
1.SpringBoot介绍: 根据官方SpringBoot文档描述,BUILD ANYTHING WITH SPRING BOOT (用SPRING BOOT构建任何东西,很牛X呀!),下面是官方文 ...
- (三)SpringBoot基础篇- 持久层,jdbcTemplate和JpaRespository
一.介绍 SpringBoot框架为使用SQL数据库提供了广泛的支持,从使用JdbcTemplate的直接JDBC访问到完整的"对象关系映射"技术(如Hibernate).Spri ...
- SpringBoot基础篇(一)
1.前言 什么是SpringBoot:springboot是当下一套流行的J2EE框架.借助微服务的思想.将业务分成一个个的服务.通过spring-cloud进行整合.最后通过spring-data进 ...
- SpringBoot基础系列之自定义配置源使用姿势实例演示
[SpringBoot基础系列]自定义配置源的使用姿势介绍 前面一篇博文介绍了一个@Value的一些知识点,其中提了一个点,@Value对应的配置,除了是配置文件中之外,可以从其他的数据源中获取么,如 ...
随机推荐
- 查看历史命令 history
生产上有文件被清空了,想查查是谁操作的? 通过history查看历史命令: $history |more 也可以通过文件查看历史命令: $vi ~/.bash_history 只显示历史命令,像查一查 ...
- 大数据入门第二十一天——scala入门(一)并发编程Actor
注:我们现在学的Scala Actor是scala 2.10.x版本及以前版本的Actor. Scala在2.11.x版本中将Akka加入其中,作为其默认的Actor,老版本的Actor已经废弃 一. ...
- 基于TLS证书手动部署kubernetes集群(下)
一.master节点组件部署 承接上篇文章--基于TLS证书手动部署kubernetes集群(上),我们已经部署好了etcd集群.flannel网络以及每个节点的docker,接下来部署master节 ...
- Libgdx学习记录14——数据保存Preferences,XmlReader
在游戏过程过程中,常常需要对某些数据进行保存,以确保下次运行时能够正确读取.如游戏中的金币.道具.游戏分数.已通过的关卡等. Libgdx中常用的数据保存方式有Preferences和XmlReade ...
- Linux常用rmp包网址
* HA:http://download.opensuse.org/repositories/network:/ha-clustering:/Stable/ * Openstack:https://r ...
- [APIO2013]机器人[搜索、斯坦纳树]
题意 题目链接 分析 记 g(d,x,y) 表示从 (x,y) 出发,方向为 d 到达的点,这个可以通过记忆化搜索求出,注意如果转移成环(此时向这个方向走没有意义)要特判. 记 f(l,r,x,y) ...
- 测试leader职责
一. 负责软件产品/项目测试工作的组织 参加软件产品开发前的需求调研和分析 根据需求规格说明书,概要设计和开发计划编写项目总体测试计划,详细测试计划,测试大纲和测试文档结构表[测试计划 a.已上线产品 ...
- Flask学习-Flask app接受第一个HTTP请求
一.__call__() 在Flask app启动后,一旦uwsgi收到来自web server的请求,就会调用后端app,其实此时就是调用app的__call__(environ,start_res ...
- 数位DP模板详解
// pos = 当前处理的位置(一般从高位到低位) // pre = 上一个位的数字(更高的那一位) // status = 要达到的状态,如果为1则可以认为找到了答案,到时候用来返回, // 给计 ...
- 软件工程第二次作业(One who wants to wear the crown, Bears the crown.)
小镓自述Eclipse使用及自动单元测试技术 因为本人对JAVA有一些兴趣,所以就决定用Eclipse来完成这次作业,从安装Eclipse到学习写代码,最后学会用Junit来进行单元测试.这段过程给我 ...