SpringBoot之AOP详解

面向方面编程(AOP)通过提供另一种思考程序结构的方式来补充面向对象编程(OOP)。
OOP中模块化的关键单元是类,而在AOP中,模块化单元是方面。

文章目录

            • 准备工作
            • 1. @Pointcut 切入点
            • 2.@Before前置通知
            • 3.@After 后置通知
            • 4.@Around环绕通知
            • 5.@AfterReturning
            • 6.@AfterThrowing
            • 7.AOP用在全局异常处理
            • 8.以上用的是log4j2的日志处理

准备工作

首先,使用AOP要在pom.xml中加入依赖

//引入AOP依赖

        <!-- AOP --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency>

然后在application.yml中加入

spring:aop:proxy-target-class: true
1. @Pointcut 切入点

定义一个切点。
例如我们要在一个方法加上切入点,根据方法的返回的对象,方法名,修饰词来写成一个表达式或者是具体的名字

我们现在来定义一个切点

package com.example.aop;import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;/*** 类定义为切面类*/
@Aspect
@Component
public class AopTestController {private static final Logger logger = LoggerFactory.getLogger(AopTestController.class);/*** 定义一个切点*/@Pointcut(value = "execution(public String test (..))")public void cutOffPoint() {}
}

这里的切点定义的方法是

@GetMapping("hello")public String test(){logger.info("欢迎关注Java知音");return "i love java";}

如果你想写个切入点在所有返回对象为Area的方法,如下
@Pointcut(“execution(public com.example.entity.Area (…))”)
等很多写法,也可以直接作用在某些包下
注意:private修饰的无法拦截

2.@Before前置通知

在切入点开始处切入内容

在之前的AopTestController类中加入对test方法的前置通知

@Before("cutOffPoint()")public void beforeTest(){logger.info("我在test方法之前执行");}

这里@Before里的值就是切入点所注解的方法名
在这里插入图片描述
在方法左侧出现的图标跟过去以后就是所要通知的方法 这里就是配置正确了,我们来浏览器调用一下方法
在这里插入图片描述

联想一下,这样的效果可以用在哪里,想像如果要扩展一些代码,在不需要动源代码的基础之上就可以进行拓展,美滋滋

3.@After 后置通知

和前置通知相反,在切入点之后执行

@After("cutOffPoint()")public void doAfter(){logger.info("我是在test之后执行的");}

控制台执行结果
在这里插入图片描述
这里定义一个通知需要重启启动类,而修改通知方法的内容是可以热部署的

4.@Around环绕通知

和前两个写法不同,实现的效果包含了前置和后置通知。

当使用环绕通知时,proceed方法必须调用,否则拦截到的方法就不会再执行了

环绕通知=前置+目标方法执行+后置通知,proceed方法就是用于启动目标方法执行的

ThreadLocal<Long> startTime = new ThreadLocal<>();@Around("cutOffPoint()")public Object doAround(ProceedingJoinPoint pjp){startTime.set(System.currentTimeMillis());logger.info("我是环绕通知执行");Object obj;try{obj = pjp.proceed();logger.info("执行返回值 : " + obj);logger.info(pjp.getSignature().getName()+"方法执行耗时: " + (System.currentTimeMillis() - startTime.get()));} catch (Throwable throwable) {obj=throwable.toString();}return obj;}

执行结果:
在这里插入图片描述

1.环绕通知可以项目做全局异常处理
2.日志记录
3.用来做数据全局缓存
4.全局的事物处理 等
5.@AfterReturning

切入点返回结果之后执行,也就是都前置后置环绕都执行完了,这个就执行了

/*** 执行完请求可以做的* @param result* @throws Throwable*/@AfterReturning(returning = "result", pointcut = "cutOffPoint()")public void doAfterReturning(Object result) throws Throwable {logger.info("大家好,我是@AfterReturning,他们都秀完了,该我上场了");}

执行结果
在这里插入图片描述
应用场景可以用来在订单支付完成之后就行二次的结果验证,重要参数的二次校验,防止在方法执行中的时候参数被修改等等

6.@AfterThrowing

这个是在切入执行报错的时候执行

// 声明错误e时指定的抛错类型法必会抛出指定类型的异常// 此处将e的类型声明为Throwable,对抛出的异常不加限制@AfterThrowing(throwing = "e",pointcut = "cutOffPoint()")public void doAfterReturning(Throwable e) {logger.info("大家好,我是@AfterThrowing,他们犯的错误,我来背锅");logger.info("错误信息"+e.getMessage());}

在其他切入内容中随意整个错误出来,制造一个环境。

下面是@AfterThrowing的执行结果
在这里插入图片描述

7.AOP用在全局异常处理

定义切入点拦截ResultBean或者PageResultBean

@Pointcut(value = "execution(public com.example.beans.PageResultBean *(..)))")public void handlerPageResultBeanMethod() {}@Pointcut(value = "execution(public com.example.beans.ResultBean *(..)))")public void handlerResultBeanMethod() {}

下面是AopController.java

package com.example.aop;import com.example.beans.PageResultBean;
import com.example.beans.ResultBean;
import com.example.entity.UnloginException;
import com.example.exception.CheckException;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;/*** 使用@Aspect注解将此类定义为切面类* 根据晓风轻著的ControllerAOP所修改* 晓风轻大佬(很大的佬哥了):https://xwjie.github.io/*/
@Aspect
@Component
public class AopController {private static final Logger logger = LoggerFactory.getLogger(AopController.class);ThreadLocal<ResultBean> resultBeanThreadLocal = new ThreadLocal<>();ThreadLocal<PageResultBean<?>> pageResultBeanThreadLocal = new ThreadLocal<>();ThreadLocal<Long> start = new ThreadLocal<>();/*** 定义一个切点*/@Pointcut(value = "execution(public com.example.beans.PageResultBean *(..)))")public void handlerPageResultBeanMethod() {}@Pointcut(value = "execution(public com.example.beans.ResultBean *(..)))")public void handlerResultBeanMethod() {}@Around("handlerPageResultBeanMethod()")public Object handlerPageResultBeanMethod(ProceedingJoinPoint pjp) {start.set(System.currentTimeMillis());try {pageResultBeanThreadLocal.set((PageResultBean<?>)pjp.proceed());logger.info(pjp.getSignature() + " 方法执行耗时:" + (System.currentTimeMillis() - start.get()));} catch (Throwable e) {ResultBean<?> resultBean = handlerException(pjp , e);pageResultBeanThreadLocal.set(new PageResultBean<>().setMsg(resultBean.getMsg()).setCode(resultBean.getCode()));}return pageResultBeanThreadLocal.get();}@Around("handlerResultBeanMethod()")public Object handlerResultBeanMethod(ProceedingJoinPoint pjp) {start.set(System.currentTimeMillis());try {resultBeanThreadLocal.set((ResultBean<?>)pjp.proceed());logger.info(pjp.getSignature() + " 方法执行耗时:" + (System.currentTimeMillis() - start.get()));} catch (Throwable e) {resultBeanThreadLocal.set(handlerException(pjp , e));}return resultBeanThreadLocal.get();}/*** 封装异常信息,注意区分已知异常(自己抛出的)和未知异常*/private ResultBean<?> handlerException(ProceedingJoinPoint pjp, Throwable e) {ResultBean<?> result = new PageResultBean();logger.error(pjp.getSignature() + " error ", e);// 已知异常if (e instanceof CheckException) {result.setMsg(e.getLocalizedMessage());result.setCode(ResultBean.FAIL);} else if (e instanceof UnloginException) {result.setMsg("Unlogin");result.setCode(ResultBean.NO_LOGIN);} else {result.setMsg(e.toString());result.setCode(ResultBean.FAIL);}return result;}
}

用上面的环绕通知可以对所有返回ResultBean或者PageResultBean的方法进行切入,这样子就不用在业务层去捕捉错误了,只需要去打印自己的info日志。

看下面一段代码

@Transactional@Overridepublic int insertSelective(Area record) {record.setAddress("test");record.setPostalcode(88888);record.setType(3);int i=0;try {i = areaMapper.insertSelective(record);}catch (Exception e){logger.error("AreaServiceImpl insertSelective error:"+e.getMessage());}return i;}

假如上面的插入操作失败出错了? 你认为会回滚吗?

答案是:不会。

为什么?

因为你把错误捕捉了,事物没检测到异常就不会回滚。

那么怎么才能回滚呢?

在catch里加throw new RuntimeException().

可是那么多业务方法每个设计修改的操作都加,代码繁琐,怎么进行处理呢?

在这里用到上面的AOP切入处理,错误不用管,直接抛,抛到控制层进行处理,这样的话,接口调用的时候,出错了,接口不会什么都不返回,而是会返回给你错误代码,以及错误信息,便于开发人员查错。

8.以上用的是log4j2的日志处理

先移除springboot自带的log日志处理

在pom.xml中增加

 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-log4j2</artifactId><exclusions><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId></exclusion></exclusions></dependency>

然后在application.yml中增加

logging:level:com:example:dao: debugconfig: classpath:log4j2-spring.xml

log4j2-spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出-->
<!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数-->
<configuration status="INFO" monitorInterval="30"><!--先定义所有的appender--><appenders><!--这个输出控制台的配置--><console name="Console" target="SYSTEM_OUT"><!--输出日志的格式--><PatternLayout pattern="%highlight{[ %p ] [%-d{yyyy-MM-dd HH:mm:ss}] [ LOGID:%X{logid} ] [%l] %m%n}"/></console><!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,这个也挺有用的,适合临时测试用--><File name="Test" fileName="logs/test.log" append="false"><PatternLayout pattern="%highlight{[ %p ] %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] [%l] %m%n}"/></File><RollingFile name="RollingFileInfo" fileName="logs/log.log" filePattern="logs/info.log.%d{yyyy-MM-dd}"><!-- 只接受level=INFO以上的日志 --><ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/><PatternLayout pattern="%highlight{[ %p ] [%-d{yyyy-MM-dd HH:mm:ss}] [ LOGID:%X{logid} ] [%l] %m%n}"/><Policies><TimeBasedTriggeringPolicy modulate="true" interval="1"/><SizeBasedTriggeringPolicy/></Policies></RollingFile><RollingFile name="RollingFileError" fileName="logs/error.log" filePattern="logs/error.log.%d{yyyy-MM-dd}"><!-- 只接受level=WARN以上的日志 --><Filters><ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY" /></Filters><PatternLayout pattern="%highlight{[ %p ] %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] [%l] %m%n}"/><Policies><TimeBasedTriggeringPolicy modulate="true" interval="1"/><SizeBasedTriggeringPolicy/></Policies></RollingFile></appenders><!--然后定义logger,只有定义了logger并引入的appender,appender才会生效--><loggers><!--过滤掉spring和mybatis的一些无用的DEBUG信息--><logger name="org.springframework" level="INFO"></logger><logger name="org.mybatis" level="INFO"></logger><root level="all"><appender-ref ref="Console"/><appender-ref ref="Test"/><appender-ref ref="RollingFileInfo"/><appender-ref ref="RollingFileError"/></root></loggers>
</configuration>

之后在你要打印日志的类中增加

private static final Logger logger = LoggerFactory.getLogger(你的类名.class);public static void main(String[] args) {logger.error("error级别日志");logger.warn("warning级别日志");logger.info("info级别日志");}

有了日志后就很方便了,在你的方法接收对象时打印下,然后执行了逻辑之后打印下, 出错之后很明确了,就会很少去Debug的,养成多打日志的好习惯,多打印一点info级别的日志,用来在开发环境使用,在上线的时候把打印的最低级别设置为warning,这样你的info级别日志也不会影响到项目的重要Bug的打印

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/519362.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HUAWEI华为笔记本电脑MateBook D 14 2022款 i5 集显 非触屏(NbDE-WFH9)原装出厂Windows11系统21H2

链接&#xff1a;https://pan.baidu.com/s/1-tCCFwZ0RggXtbWYBVyhFg?pwdmcgv 提取码&#xff1a;mcgv 华为MageBookD14原厂WIN11系统自带所有驱动、出厂状态主题壁纸、Office办公软件、华为电脑管家、华为应用市场等预装软件程序 文件格式&#xff1a;esd/wim/swm 安装方式…

远程办公首日企业通讯软件崩溃、紧急扩容,云办公怎么了?

作者 | 阿文责编 | 伍杏玲来源 | 程序人生&#xff08;ID&#xff1a;coder_life&#xff09;受新型冠状病毒性肺炎的疫情影响&#xff0c;各地实施了严格的控制手段&#xff1a;封村、封路、封小区、暂停省际、县级、村级班车等措施。1月27日&#xff0c;国务院办公厅发布关于…

我在阿里云做前端

前言 今年是我毕业的第10个年头&#xff0c;半路出家做了前端&#xff0c;title一直是前端&#xff0c;你可以说我很专注&#xff0c;有时候也有些遗憾。一直以来&#xff0c;当别人问起你是做什么的&#xff0c;我说前端或者全栈&#xff0c;别人说&#xff1a;哦&#xff0c…

Mybatis-Plus的SQL语句组拼原理

前言&#xff1a;记录查找自动组拼SQL语句的过程 首先在BaseMapper其中的一个方法下打个断点 在断点显示的值栏找到相关的SQL 发现SQL语句在MappedStatement对象中&#xff0c;而sqlSource存的就是相关的sql语句 然后在MappedStatement这个对象打断点&#xff0c;看看到底是…

云漫圈 | 什么是字符串匹配算法?

作者 | 程序员小灰来源 | 程序员小灰&#xff08;ID&#xff1a;chengxuyuanxiaohui &#xff09;————— 第二天 —————什么意思呢&#xff1f;让我们来举一个例子&#xff1a;在上图中&#xff0c;字符串B是A的子串&#xff0c;B第一次在A中出现的位置下标是2&#…

MSSQL实践-数据库备份加密

摘要 在SQL Server安全系列专题月报分享中&#xff0c;我们已经分享了&#xff1a;如何使用对称密钥实现SQL Server列加密技术、使用非对称密钥实现SQL Server列加密、使用混合密钥实现SQL Server列加密技术、列加密技术带来的查询性能问题以及相应解决方案、行级别安全解决方…

Kubernetes 如何打赢容器之战?

Kubernetes 近几年很热门&#xff0c;在各大技术论坛上被炒的很火。它提供了强大的容器编排能力&#xff0c;与此同时 DevOps 的概念也来到大家身边&#xff0c;广大的开发同学也能简单地运维复杂的商业化分布式系统&#xff0c;打破了传统开发和运维之间的界限。 本文会以初学…

一行Python代码能干什么?有意思!

我下班最大的乐趣就是刷抖音一开始刷的都是一些无聊小剧场后来看看能不能刷一些技术上的内容因为本人觉得Java是世界上最好的编程语言可是我在刷抖音的时候刷到了一行Python代码能干什么我突然觉得爱上Python了之前是听说Python比较简单同样的内容Java、C语言需要十几行Python只…

自然语言生成(NLG)的好处是什么,它如何影响BI?

近年来&#xff0c;我们已经看到了大数据的成功应用&#xff0c;但根据研究&#xff0c;只有20&#xff05;的员工能够真正的使用BI工具。此外&#xff0c;由于在统计思维方面缺乏培训且图表和图表形式的数据不是很清晰&#xff0c;决策者往往会出现误解和决策失误。而这背后的…

ida 字符串查找_IDA 搜索中文字符串

IDA 的字符串窗口默认只能显示英文&#xff0c;网上的一些方法是指定启动时的参数可以显示中文ida64 -DCULTUREallida -DCULTUREall还有就是修改 cfg/ida.cfg 文件&#xff0c;但是这两种方法都没试成功。实际上没有那么麻烦&#xff0c;IDA 7.0 操作很方便&#xff0c;在字符串…

“练好内功坚持被集成”,阿里云发布SaaS加速器

在3月21日的2019阿里云峰会北京上&#xff0c;阿里云发布新产品SaaS加速器&#xff1a;人工智能、虚拟现实等技术能力被集成为模块&#xff0c;ISV和开发者只要简单拖拽&#xff0c;就可以快速搭建SaaS应用。 发布现场&#xff0c;阿里云智能产品管理部总经理马劲进行简单演示…

2020 年,云游戏将爆发?各大科技公司云游戏布局大曝光!

作者 | 年素清责编 | 伍杏玲出品 | CSDN云计算&#xff08;ID&#xff1a;CSDNcloud&#xff09; 伴随5G技术加速落地&#xff0c;云游戏作为5G应用落地的最佳场景&#xff0c;已经成为全球游戏厂商和云服务厂商布局的重要战场。根据艾媒咨询数据显示&#xff0c;2018年中国云游…

Springboot整合Quartz集群部署以及配置Druid数据源

参考链接&#xff1a; https://blog.csdn.net/wangmx1993328/article/details/105441308 https://blog.csdn.net/qq_39669058/article/details/90411497 参数配置连接&#xff1a;https://github.com/wangmaoxiong/quartzjdbc/blob/master/src/main/resources/application-clu…

我是如何拿到蚂蚁金服实习 offer 的

我是2018年3月入职蚂蚁的应届前端工程师&#xff0c;来自于北京邮电大学。2年前的此刻&#xff0c;我也是实习求职大潮中的一员&#xff0c;在这里&#xff0c;分享一下我从准备面试到实习&#xff0c;再到最终正式入职的经历。 选择蚂蚁 阿里的前端水平在业界有着很高的评价…

java 客户化排序_第八部分_客户化JSP标签

EL语言(减少JSP页面中的Java代码)String password request.getParameter("password");%>username: password: username: ${param.username }password: ${param.password }属性范围->在EL中的名称Page->pageScope&#xff1b;Request->requestScope&…

十大类疫情服务紧缺 阿里广发英雄帖抗疫小程序开发者最高可获50万元奖励

全民积极响应国家抗击新冠肺炎疫情的号召&#xff0c;正催生出越来越多新的互联网服务缺口。基于对用户、政府、企事业单位抗疫服务需求的紧缺情况调查&#xff0c;支付宝今日面向社会各界开发者发布“10大疫情期最急需服务开发清单”&#xff0c;号召更多开发者投入进来开发更…

信用算力基于 RocketMQ 实现金融级数据服务的实践

微服务架构已成为了互联网的热门话题之一&#xff0c;而这也是互联网技术发展的必然阶段。然而&#xff0c;微服务概念的提出者 Martin Fowler 却强调&#xff1a;分布式调用的第一原则就是不要分布式。 纵观微服务实施过程中的弊端&#xff0c;可以推断出作者的意图&#xff…

六年打磨!阿里开源混沌工程工具 ChaosBlade

阿里妹导读&#xff1a;减少故障的最好方法就是让故障经常性的发生。通过不断重复失败过程&#xff0c;持续提升系统的容错和弹性能力。今天&#xff0c;阿里巴巴把六年来在故障演练领域的创意和实践汇浓缩而成的工具进行开源&#xff0c;它就是 “ChaosBlade”。如果你想要提升…

jmeter-5.3 测试http接口动态数据 windows+Linux双环境

文章目录一、基础组件1. 企业需求2. 创建线程组&#xff1a;3. 创建HTTP请求4. CSV 数据文件组件5. 响应断言二、添加监听器2.1. 添加察看结果树2.2. 添加聚合报告2.3. 监听每秒事务数2.4. 监听 随时间变化的响应时间三、HTTP请求配置3.1. 基础参数四、 响应断言配置四、 CSV 数…

阿里达摩院又火了!引入AI确诊肺炎提速16倍,仅需半小时!网友神回复了

针对新型冠状病毒的确诊&#xff0c;全国大多数医院基本都采用核酸检测。果壳网发文称&#xff1a;从病人样本送到实验室即刻开始测试&#xff0c;到出检测报告&#xff0c;差不多需要8个小时。但是8个小时太长&#xff0c;在现在这种病毒传播速度下&#xff0c;让人焦急万分。…