SpringBoot系列 - 使用AOP
AOP(面向切面编程)是Spring的两大核心功能之一,功能非常强大,为解耦提供了非常优秀的解决方案。 现在就以springboot中aop的使用来了解一下如何使用aop。 写几个简单的Spring RESTful服务接口方法,实现方法前面或后面打印日志。
AOP术语定义
Spring的AOP中有几个重要概念搞清楚就行
执行点(Executepoint) - 类初始化,方法调用。
连接点(Joinpoint) - 执行点+方位的组合,可确定Joinpoint,比如类开始初始化前,类初始化后,方法调用前,方法调用后。
切点(Pointcut) - 在众多执行点中,定位感兴趣的执行点。Executepoint相当于数据库表中的记录,而Pointcut相当于查询条件。
增强(Advice) - 织入到目标类连接点上的一段程序代码。除了一段程序代码外,还拥有执行点的方位信息。
目标对象(Target) - 增强逻辑的织入目标类
引介(Introduction) - 一种特殊的增强(advice),它为类添加一些额外的属性和方法,动态为业务类添加其他接口的实现逻辑,让业务类成为这个接口的实现类。
代理(Proxy) - 一个类被AOP织入后,产生一个结果类,它便是融合了原类和增强逻辑的代理类。
切面(Aspect) - 切面由切点(Pointcut)和增强(Advice/Introduction)组成,既包括横切逻辑定义,也包括连接点定义。
AOP工作重点:
如何通过切点(Pointcut)和增强(Advice)定位到连接点(Jointpoint)上;
如何在增强(Advice)中编写切面的代码。
添加maven依赖
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.0.4.RELEASE</version>
</parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><exclusions><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jetty</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><executions></executions></plugin></plugins></build>
</dependencies>
配置application.yml
server.port: 8092
创建Controller
没有结果返回的示例:
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** Description:*/public class UserController {("/first")public Object first() {return "first controller";}("/doError")public Object error() {return 1 / 0;}
}
创建切面类
/*** 日志切面*/public class LogAspect {("execution(public * com.xncoding.aop.controller.*.*(..))")public void webLog(){}("webLog()")public void deBefore(JoinPoint joinPoint) throws Throwable {// 接收到请求,记录请求内容ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = attributes.getRequest();// 记录下请求内容System.out.println("URL : " + request.getRequestURL().toString());System.out.println("HTTP_METHOD : " + request.getMethod());System.out.println("IP : " + request.getRemoteAddr());System.out.println("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());System.out.println("ARGS : " + Arrays.toString(joinPoint.getArgs()));}(returning = "ret", pointcut = "webLog()")public void doAfterReturning(Object ret) throws Throwable {// 处理完请求,返回内容System.out.println("方法的返回值 : " + ret);}//后置异常通知("webLog()")public void throwss(JoinPoint jp){System.out.println("方法异常时执行.....");}//后置最终通知,final增强,不管是抛出异常或者正常退出都会执行("webLog()")public void after(JoinPoint jp){System.out.println("方法最后执行.....");}//环绕通知,环绕增强,相当于MethodInterceptor("webLog()")public Object arround(ProceedingJoinPoint pjp) {System.out.println("方法环绕start.....");try {Object o = pjp.proceed();System.out.println("方法环绕proceed,结果是 :" + o);return o;} catch (Throwable e) {e.printStackTrace();return null;}}
}
测试效果
启动项目,执行如下mvn命令:
mvn spring-boot:run
模拟正常执行的情况,访问http://localhost:8092/first,看控制台结果:
方法环绕start…
URL : http://localhost:8092/first
HTTP_METHOD : GET
IP : 0:0:0:0:0:0:0:1
CLASS_METHOD : com.xncoding.aop.controller.UserController.first
ARGS : []
方法环绕proceed,结果是 :first controller
方法最后执行…
方法的返回值 : first controller
模拟出现异常时的情况,访问http://localhost:8092/doError,看控制台结果:
方法环绕start…
URL : http://localhost:8092/doError
HTTP_METHOD : GET
IP : 0:0:0:0:0:0:0:1
CLASS_METHOD : com.xncoding.aop.controller.UserController.error
ARGS : []
java.lang.ArithmeticException: / by zero
…中间省略…
方法最后执行…
方法的返回值 : null
通过上面的简单的例子,可以看到aop的执行顺序。知道了顺序后,就可以在相应的位置做切面处理了。
切面注解说明
@Aspect 作用是把当前类标识为一个切面供容器读取
@Pointcut 定义切点,切点方法不用任何代码,返回值是void,重要的是条件表达式
@Before 标识一个前置增强方法,相当于BeforeAdvice的功能
@AfterReturning 后置增强,相当于AfterReturningAdvice,方法退出时执行
@AfterThrowing 异常抛出增强,相当于ThrowsAdvice
@After final增强,不管是抛出异常或者正常退出都会执行
@Around 环绕增强,相当于MethodInterceptor
方法参数说明:
除了@Around外,每个方法里都可以加或者不加参数JoinPoint。
JoinPoint里包含了类名、被切面的方法名,参数等属性,可供读取使用。
@Around参数必须为ProceedingJoinPoint,pjp.proceed相应于执行被切面的方法。
@AfterReturning方法里,可以加returning = “xxx”,xxx即为在controller里方法的返回值,本例中的返回值是”first controller”。
@AfterThrowing方法里,可以加throwing = “XXX”,读取异常信息,如本例中可以改为:
//后置异常通知
(throwing = “ex”, pointcut = “webLog()”)
public void throwss(JoinPoint jp, Exception ex){
System.out.println(“方法异常时执行…”);
}
一般常用的有before和afterReturn组合,或者单独使用Around,即可获取方法开始前和结束后的切面。
关于切点PointCut
execution函数用于匹配方法执行的连接点,语法为:
execution(方法修饰符(可选) 返回类型 方法名 参数 异常模式(可选))
参数部分允许使用通配符:
匹配任意字符,但只能匹配一个元素
… 匹配任意字符,可以匹配任意多个元素,表示类时,必须和*联合使用
必须跟在类名后面,如Horseman+,表示类本身和继承或扩展指定类的所有类
除了execution(),Spring中还支持其他多个函数,这里列出名称和简单介绍
@annotation() 表示标注了指定注解的目标类方法
args() 通过目标类方法的参数类型指定切点
@args() 通过目标类参数的对象类型是否标注了指定注解指定切点
within() 通过类名指定,它将匹配指定类型
target() 通过类名指定,同时包含所有子类
@within() 匹配标注了指定注解的类及其子类,必须是在目标对象上声明这个注解,在接口上声明的对它不起作用
@target() 匹配标注了指定注解的类,必须是在目标对象上声明这个注解,在接口上声明的对它不起作用
逻辑运算符
表达式可由多个切点函数通过逻辑运算组成,与(&&)、 或(||)、 非(!)
比如execution(* chop(…)) && target(Horseman) 表示Horseman及其子类的chop方法
自定义注解
一般多用于某些特定的功能,我们来自定义一个注解:
({ElementType.METHOD, ElementType.TYPE})
(RetentionPolicy.RUNTIME)
public UserAccess {String desc() default "无信息";
}
在Controller里加个方法:("/second")
(desc = "second")
public Object second() {return "second controller";
}
创建一个新的切面类:
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;public class UserAccessAspect {(value = "@annotation(com.xncoding.aop.aspect.UserAccess)")public void access() {}("access()")public void deBefore(JoinPoint joinPoint) throws Throwable {System.out.println("second before");}("@annotation(userAccess)")public Object around(ProceedingJoinPoint pjp, UserAccess userAccess) {//获取注解里的值System.out.println("second around:" + userAccess.desc());try {return pjp.proceed();} catch (Throwable throwable) {throwable.printStackTrace();return null;}}
}
主要看一下@Around注解这里,如果需要获取在controller注解中赋给UserAccess的desc里的值,就需要这种写法,这样UserAccess参数就有值了。
执行:mvn spring-boot:run
启动项目,访问http://localhost:8092/second,看控制台:
方法环绕start…
URL : http://localhost:8092/second
HTTP_METHOD : GET
IP : 0:0:0:0:0:0:0:1
CLASS_METHOD : com.xncoding.aop.controller.UserController.second
ARGS : []
second around:second
second before
方法环绕proceed,结果是 :second controller
方法最后执行…
方法的返回值 : second controller
通知结果可以看到,两个aop切面类都工作了,顺序呢就是下面的
spring aop就是一个同心圆,要执行的方法为圆心,最外层的order最小。从最外层按照AOP1、AOP2的顺序依次执行doAround方法,doBefore方法。然后执行method方法,最后按照AOP2、AOP1的顺序依次执行doAfter、doAfterReturn方法。也就是说对多个AOP来说,先before的,一定后after。
对于上面的例子就是,先外层的就是对所有controller的切面,内层就是自定义注解的。 那不同的切面,顺序怎么决定呢,尤其是同格式的切面处理,譬如两个execution的情况,那spring就是随机决定哪个在外哪个在内了。
所以大部分情况下,我们需要指定顺序,最简单的方式就是在Aspect切面类上加上@Order(1)注解即可,order越小最先执行,也就是位于最外层。像一些全局处理的就可以把order设小一点,具体到某个细节的就设大一点。
GitHub源码
https://github.com/yidao620c/SpringBootBucket/tree/master/springboot-aop
AOP实现入参出参加密解密参考
https://blog.csdn.net/github_36086968/article/details/103424945
SpringBoot系列 - 使用AOP相关推荐
- Springboot系列之Shiro、JWT、Redis 进行认证鉴权
Springboot系列之Shiro.JWT.Redis 进行认证鉴权 Shiro架构 Apache Shiro是一个轻量级的安全框架 Shiro可以非常容易的开发出足够好的应用,其不仅可以用在Jav ...
- SpringBoot系列-- SpringBoot中使用lombok简化开发
目录 1. 项目环境 2.添加依赖和安装插件 3. 实体类常用注解总结 3.1 @Setter.@Getter 3.1.1 注解在属性上 3.1.2 注解在类上 3.1.3 注解对静态属性和final ...
- 十、springboot注解式AOP(@Aspect)统一日志管理
springboot注解式AOP(@Aspect)统一日志管理 简介 AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功 ...
- SpringBoot系列: RestTemplate 快速入门
==================================== 相关的文章 ==================================== SpringBoot系列: 与Sprin ...
- Java工程师之SpringBoot系列教程前言目录
前言 与时俱进是每一个程序员都应该有的意识,当一个Java程序员在当代步遍布的时候,你就行该想到我能多学点什么.可观的是后端的框架是稳定的,它们能够维持更久的时间在应用中,而不用担心技术的更新换代.但 ...
- 【j360-boot】Spring-boot系列三(崩溃模式,不是你崩就是电脑崩)
2019独角兽企业重金招聘Python工程师标准>>> j360-boot spring-boot入门工程之j360-boot:(欢迎star.fork) https://githu ...
- springboot 系列技术教程目录
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家.点击跳转到教程. 一.教程目录地址: springboot系列技术教程目录 二.教程内容: springboot2.X ...
- springboot系列六、springboot配置错误页面及全局异常
springboot系列六.springboot配置错误页面及全局异常 参考文章: (1)springboot系列六.springboot配置错误页面及全局异常 (2)https://www.cnbl ...
- SpringBoot系列五:SpringBoot错误处理(数据验证、处理错误页、全局异常)
SpringBoot系列五:SpringBoot错误处理(数据验证.处理错误页.全局异常) 参考文章: (1)SpringBoot系列五:SpringBoot错误处理(数据验证.处理错误页.全局异常) ...
- 服务 自动启动参数_使用ansible部署springboot系列02服务托管与jvm参数管理
@[toc] 这是使用ansible部署springboot系列 的第二篇文章. 本系列文章介绍基于springboot的java程序如何自动化部署.该CI/CD方案基于Jenkins+Ansible ...
最新文章
- Tensorflow函数——tf.variable_scope()
- 国内最牛的Java面试八股,真offer收割机!
- 基于Cobbler实现多版本系统批量部署
- ​人工神网络来解码皮质电图(ECoG)数据
- MIT机器狗再进化,碎石冰面上跑也不打滑,这次真的稳如狗了
- SocketAPI,CAsyncSocket,CSocket内幕及其用法
- python PIL 生成照片墙
- 对Bridge模式的理解
- 「Self-driving: Perception」多传感器融合之Camera、Lidar 雷达融合
- 2.4.4 案例理解4种事务的隔离级别
- vue 前期准备,项目结构
- 关于搞技术的一点思考
- linux中telnet工具,linux中telnet工具的介绍和使用
- 和自己“卷”才是王道
- 小米案例分析PPT模板
- 京东大数据平台产品体系揭秘
- 【学习OpenCV4】聊聊图像格式
- Android 支付宝支付功能实现
- UNITY中帧动画的制作
- 鸡兔同笼python
热门文章
- Jar包常见的反编译工具介绍与使用
- 上手TaoTronics家用暖风机体验
- Android下拉状态栏 快捷开关的添加与删除
- android恶意积分墙代码,传统积分墙的忧虑:微信积分墙刷榜优化分析
- 计算机潮流分析22节点,第三章电力系统潮流分析与计算第七讲电力网络方程和矩阵及功率方程_255903070...
- 静态路由 华三静态路由
- 重启osd服务失败:Start request repeated too quickly
- win10电脑插上U盘不显示盘符如何办?
- 面试热问——你的职业规划是什么?
- Segmentation笔记4-Boundary-Aware Network for Fast and High-Accuracy Portrait Segmentation