Spring及SpringBoot中AOP的使用

Spring中AOP示例

<dependencies><!--Spring核心包--><dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>5.3.6</version></dependency><!--引入SpringBean--><dependency><groupId>org.springframework</groupId><artifactId>spring-beans</artifactId><version>5.3.6</version></dependency><!--引入context包--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.6</version></dependency><!--引入表达式jar包--><dependency><groupId>org.springframework</groupId><artifactId>spring-expression</artifactId><version>5.3.6</version></dependency><!--引入日志依赖--><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.2</version></dependency><!--引入测试包--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency><!--引入AOP 以下三个包--><!--引入AOP包--><dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>5.3.6</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.3.6</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjrt</artifactId><version>1.9.6</version></dependency><!--当下所有的jar包都需要手动添加依赖,并且需要确定依赖的关系,对初学者不友好-->
</dependencies>

配置类

/*** @author TB* @date 2020/2/12 0:14*/
@Configuration//标识是个配置类
@ComponentScan("com")//包扫描路径
//SpringBoot的AOP是默认开启的,不需要加注解@EnableAspectJAutoProxy” 这里用的Spring所以要加
//让Spring认识加了@Aspect类的注解  不然Spring不认识切面@Aspect
//其实是启动AOP注解创建代理
//默认启用JDK动态代理,当目标对象没有实现接口时则采用CGlib代理
//该注解有个proxyTargetClass属性,默认是false 如果改成@EnableAspectJAutoProxy(proxyTargetClass=true)则使用CGlib代理
//JDK代理创建速度快,运行时稍慢,CBlib代理创建时速度慢,运行速度快
//可以通过属性的true或false来指定JDK代理和CGlib代理  默认CBG代理  找不到实现类的话会自动用CGlib代理
@EnableAspectJAutoProxy
//@EnableAsync//告诉spring框架启动时创建线程池 然后在需要异步请求时在池里拿线程直接用 然后需要用异步请求的方法上加@Async
public class SpringConfig {}

标注的AOP类

/***  AOP(面向切面编程) 主要利用**动态代理**的模式 **降低程序的耦合度,扩展业务功能方法.*** 1.AOP需要被Spring容器管理 @Component* 2.标识该类是AOP切面   @Aspect* 关于AOP名词介绍* 1).连接点: 用户可以被扩展的方法    其实我们将自定义注解放到目标方法上做标识,那么该注解其实就是个连接点* 2).切入点: 用户实际扩展的方法      确定了连接点,那么该方法也就是个切入点* 3).通知:  扩展方法的具体实现       5个通知* 4).切面: 将通知应用到切入点的过程**   通知类型(必会)* 1. before:  在目标方法执行之前执行* 2. afterReturning: 在目标方法执行之后返回时执行* 3. afterThrowing:  在目标方法执行之后,抛出异常时执行* 4. after:	无论程序是否执行成功,都要最后执行的通知* 5. around: 在目标方法执行前后 都要执行的通知(完美体现了动态代理模式)* 	功能最为强大 只有环绕通知可以控制目标方法的执行** 关于通知方法总结:* 	1.环绕通知是处理业务的首选.  可以修改程序的执行轨迹* 	2.另外的四大通知一般用来做程序的监控.(监控系统) 只做记录* @author TB* @date 2020/2/12 0:24*/
@Component
//虽然标识了该类为AOP切面 但是Spring容器默认不能识别切面注解,需要手动配置
//需要在配置类SpringConfig里加上注解@EnableAspectJAutoProxy
@Aspect
public class SpringAOP {/*** 切入点表达式* 概念:当程序满足切入点表达式,才能进入切面,执行通知方法.** 1.bean("bean的ID")  根据beanId进行拦截  只能匹配一个* 2.within("包名.类名") 可以使用通配符*?      能匹配多个.* 	粒度: 上述的切入点表达式 粒度是类级别的.  粗粒度.* 3.execution(返回值类型   包名.类名.方法名(参数列表...))* 	粒度: 控制的是方法参数级别. 所以粒度较细.   最常用的.* 4.@annotation(包名.注解名)     只拦截注解.* 	粒度: 注解是一种标记 根据规则标识某个方法/属性/类    细粒度*//*** 切入点表达式练习* within:*  1.within(com.jt.*.DeptServiceImpl)   一级包下的类*  2.within(com.jt..*.DeptServiceImpl)  ..代表多级包下的类*  3.within(com.jt..*)  包下的所有的类** execution(返回值类型 包名.类名.方法名(参数列表))*  1.execution(* com.jt..*.DeptServiceImpl.add*())*  注释: 返回值类型任意的, com.jt下的所有包中的DeptServiceImpl的类*        的add开头的方法 ,并且没有参数.**  2.execution(* com.jt..*.*(..))*  注释: 返回值类型任意,com.jt包下的所有包的所有类的所有方法 任意参数.**  3.execution(int com.jt..*.*(int))*  4.execution(Integer com.jt..*.*(Integer))*  强调: 在Spring表达式中没有自动拆装箱功能! 注意参数类型** @annotation(包名.注解名)*     @Before("@annotation(com.jt.anno.Cache)")*    只拦截特定注解的内容.*///1.定义before通知//@Before("bean(deptServiceImpl)")//扫描的是一个类 因此该类里所有方法都被扩展到了//@Before("within(com.jt.service.DeptServiceImpl)")//和上面效果一样//@Before("execution(* com.jt.service.DeptServiceImpl.add*())")//*表示返回值类型任意 add*表示以add开头的方法名 最后()表示参数是空的//@Before("@annotation(com.jt.anno.Cache)")//意思有该注解 就作为切入点   因此用注解标识最常用(自定义个注解)/*** spring为了AOP动态获取目标对象及方法中的数据,则通过Joinpoint* JoinPoint是所有通知的公共参数,无论哪种通知里都可以使用* 在Before里可以获取* 对象做数据传递获取如:* 1.获取目标对象的类型* 2.获取目标方法的名称* 3.获取目标方法的参数* @param joinPoint*/@Before("pointcut()")public void before(JoinPoint joinPoint){//前置方法一般作用获取参数,方法名,等等System.out.println("目标对象的Class类对象: "+joinPoint.getTarget().getClass());System.out.println("获取目标方法的方法签名: "+joinPoint.getSignature());System.out.println("获取目标对象的类名: "+ joinPoint.getSignature().getDeclaringTypeName());System.out.println("获取目标对象方法名: "+ joinPoint.getSignature().getName());System.out.println("获取目标方法参数: "+ Arrays.toString(joinPoint.getArgs()));System.out.println("我是before通知");}//1.定义一个切入点@Pointcut("@annotation(com.jt.anno.Cache)")public void pointcut(){}//如果每个通知前都加个切入点表达式  那么也太冗余了 因此我们可以定义个切入点 其他通知都围绕切入点//@BafterReturning("@annotation(com.jt.anno.Cache)")/*** JoinPoint参数是所有通知方法公有的*AfterReturning是目标方法返回执行之后返回时执行* 可以记录方法的返回值* AfterReturning注解里 value和pointcut是相同的效果:也就是说* @AfterReturning(value="pointcut()",returning="result")和@AfterReturning(pointcut="pointcut()",returning="result")* 效果一样* returning:将方法的返回值,通过形参result(这个随便取名)来进行传递(Spring会将返回值赋值给你定义的这个变量)*/@AfterReturning(value="pointcut()",returning="result")public void afterReturning(JoinPoint joinPoint,Object result){//这里注意 如果有需要用到JointPoint参数 那么必须放在第一个位置  不用可以去掉System.out.println("目标返回值结果是: "+result);System.out.println("我是AfterReturning的通知");}@AfterThrowing(pointcut = "pointcut()",throwing="e")//当目标方法执行时,抛出异常时,可以用AfterThrowing记录public void afterThrowing(Exception e){System.out.println("获取目标异常信息: "+e.getMessage());System.out.println("获取目标异常类型: "+e.getClass());System.out.println("我是AfterThrowing的通知,出现异常了");}@After("pointcut()")public void after(){System.out.println("我是After的通知");}/*** 关于环绕通知的说明* 作用: 可以控制目标方法是否执行.* 参数: ProceedingJoinPoint 通过proceed方法控制目标方法执行.* 注意事项:*  ProceedingJoinPoint 只能适用环绕通知* @return*/@Around("pointcut()")public Object around(ProceedingJoinPoint joinPoint){//注意多参情况ProceedingJoinPoint要放第一位Object result = null;try {System.out.println("环绕通知开始");//1.执行下一个通知  2.执行目标方法 3.接收返回值Long start = System.currentTimeMillis();result = joinPoint.proceed();Long end = System.currentTimeMillis();System.out.println("耗时:"+(end-start));} catch (Throwable throwable) {throwable.printStackTrace();}System.out.println("环绕通知结束");return result;}
}

切入点注解类

//控制注解的生命周期,什么时候起作用
@Retention(RetentionPolicy.RUNTIME)
//注解的作用对象:  类上   方法上 变量上
@Target({ElementType.METHOD,ElementType.TYPE,ElementType.FIELD})
public @interface Cache {}

测试

SpringBoot中使用AOP

/*** @author hrui* @date 2023/10/30 21:58*/
@Component
@Aspect
public class SpringAOP {@Before("pointcut()")public void before(JoinPoint joinPoint){//前置方法一般作用获取参数,方法名,等等
//        System.out.println("目标对象的Class类对象: "+joinPoint.getTarget().getClass());
//        System.out.println("获取目标方法的方法签名: "+joinPoint.getSignature());
//        System.out.println("获取目标对象的类名: "+ joinPoint.getSignature().getDeclaringTypeName());
//        System.out.println("获取目标对象方法名: "+ joinPoint.getSignature().getName());
//        System.out.println("获取目标方法参数: "+ Arrays.toString(joinPoint.getArgs()));System.out.println("我是before通知");}//1.定义一个切入点@Pointcut("@annotation(com.example.demo.aop.AnyAnno)")public void pointcut(){}@AfterReturning(value="pointcut()",returning="result")public void afterReturning(JoinPoint joinPoint, Object result){//这里注意 如果有需要用到JointPoint参数 那么必须放在第一个位置  不用可以去掉// System.out.println("目标返回值结果是: "+result);System.out.println("我是AfterReturning的通知");}@AfterThrowing(pointcut = "pointcut()",throwing="e")//当目标方法执行时,抛出异常时,可以用AfterThrowing记录public void afterThrowing(Exception e){
//        System.out.println("获取目标异常信息: "+e.getMessage());
//        System.out.println("获取目标异常类型: "+e.getClass());System.out.println("我是AfterThrowing的通知,出现异常了");}@After("pointcut()")public void after(){System.out.println("我是After的通知");}/*** 关于环绕通知的说明* 作用: 可以控制目标方法是否执行.* 参数: ProceedingJoinPoint 通过proceed方法控制目标方法执行.* 注意事项:*  ProceedingJoinPoint 只能适用环绕通知* @return*/@Around("pointcut()")public Object around(ProceedingJoinPoint joinPoint){//注意多参情况ProceedingJoinPoint要放第一位Object result = null;try {System.out.println("环绕通知开始");//1.执行下一个通知  2.执行目标方法 3.接收返回值result = joinPoint.proceed();System.out.println("环绕通知里方法执行完了");} catch (Throwable throwable) {throwable.printStackTrace();}System.out.println("环绕通知结束");return result;}
}

切入点注解类

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})
public @interface AnyAnno {}

自己随便测试下

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

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

相关文章

GORM:在Go中轻松管理数据库

GORM综合介绍 - Go对象关系映射库 在现代软件开发中&#xff0c;高效的数据库管理对于构建强大的应用程序至关重要。GORM是Go开发人员寻求与数据库进行交互的简化方式的宝贵工具。GORM是Go对象关系映射的缩写&#xff0c;它为Go的面向对象世界与数据库的关系世界之间提供了桥梁…

运行项目报错error in ./node_modules/marked/lib/marked.umd.js

今天跑项目时发现一个报错&#xff0c;问题出在marked这个包&#xff0c;然后翻看package.json里面也没有这个包&#xff0c;全局搜索项目也没有这个包相关的信息&#xff0c;可它就是报错&#xff0c;索性直接把它给卸载发现还是报错 报错原因&#xff1a;包的版本太高 解决…

Ribbon负载均衡原理

一、先看下流程图 备注&#xff1a;红色后面都为拦截器的逻辑&#xff0c;主要是加载配置文件【LoadBalancerAutoConfiguration】&#xff0c;对发送http请求的RestTemplate进行包装拦截&#xff0c;逻辑拦在拦截器里面。 二、LoadBalancerAutoConfiguration 负载均衡用到配置…

数据结构 - ArrayList - 动态修改的数组

目录 实现一个通用的顺序表 总结 包装类 装箱 / 装包 和 拆箱 / 拆包 ArrayList 与 顺序表 ArrayList基础功能演示 add 和 addAll &#xff0c;添加元素功能 ArrayList的扩容机制 来看一下&#xff0c;下面的代码是否存在缺陷 模拟实现 ArrayList add 功能 add ind…

山东高新技术企业申报当年下证的知识产权能用吗?

一、申报当年下证的知识产权能用吗&#xff1f; 申报当年下证的知识产权不建议用&#xff0c;可以来年再申报。 二、我们目前有10件实用新型专利证书&#xff0c;但近三年只有5件缴纳年费了&#xff0c;还能用吗&#xff1f; 实用新型专利没有缴纳年费&#xff0c;是不能用的&a…

什么是 DevOps

DevOps是一套融合软件开发&#xff08;Dev&#xff09;和 IT 运营&#xff08;Ops&#xff09;的实践&#xff0c;旨在缩短应用程序开发周期并确保以高软件质量持续交付&#xff0c;通过采用 DevOps 实践&#xff0c;您可以帮助组织更可靠、更快速、更高效地交付软件。 什么是…

SLAM从入门到精通(计算点到直线的距离)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 大家一开始看到这个题目的时候&#xff0c;也许比较吃惊。为什么会有这样的题目&#xff1f;但是做过slam的同学&#xff0c;应该就比较熟悉和了解…

Echats-自定义图表1

效果图&#xff1a; 代码&#xff1a; <!DOCTYPE html> <html lang"zh-cmn-Hans"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>…

day49

省市联动jQuery快速入门 jQuery介绍 1.jQuery是一个轻量级、兼容多浏览器的JavaScript库 2.jQuery使用户能够更方便地处理HTML Document、Events、实现动画效果、方便地进行Ajax交互&#xff0c;能够极大地简化JavaScript编程。它的宗旨就是&#xff1a;“Write less, do mor…

uniapp生命周期

uniapp生命周期包括应用生命周期、页面生命周期和组件生命周期&#xff1b; 1、应用生命周期 app.vue/uvue是uni-app的朱组件。所有页面都是在app.vue下进行切换&#xff0c;是应用入口文件。但app.vue本身不是页面&#xff0c;这里补鞥编写视图元素&#xff0c;也就没有。 这…

vue回到顶部组件

组件代码 <!--返回顶部组件--> <template><transition :name"transitionName"><divv-show"visible":style"customStyle"class"back-to-ceiling"click"backToTop"><svgwidth"16"heigh…

vue生命周期

1、vue声明周期及生命周期函数 vue声明周期 每一个vue实例从创建到销毁的过程&#xff0c;就是这个vue实例的生命周期。在这个过程中&#xff0c;他经历了从开始创建、初始化数据、编译模板、挂载Dom、渲染→更新→渲染、卸载等一系列过程&#xff1b; 生命周期&#xff1a;…

postgresql数组重叠(有共同元素)查询

直接上最终代码&#xff1a; select distinct id from a where string_to_array(in_area,,) && (select ARRAY_AGG( code) from areas where code like 11% or code 100000)::TEXT[] pg语法&#xff1a; 表 9.48显示了可用于数组类型的运算符。 表 9.48。数组运算符…

大数据之LibrA数据库系统告警处理(ALM-12012 NTP服务异常)

告警解释 当节点NTP服务无法与主OMS节点NTP服务正常同步时间时产生该告警。 当节点NTP服务与主OMS节点NTP服务正常同步时间时恢复该告警。 告警属性 告警ID 告警级别 可自动清除 12012 严重 是 告警参数 参数名称 参数含义 ServiceName 产生告警的服务名称。 Rol…

分布式多主关系数据库的底线业务优势

当今的应用程序&#xff08;包括企业应用程序&#xff09;需要始终开启且始终可用&#xff0c;并且通常必须为全球用户提供服务&#xff0c;这些用户无论身在何处都希望获得几乎即时的响应时间。 应对这些挑战不仅仅意味着让用户更满意&#xff1a;每个能够解决低延迟和超高可…

唇形驱动媲美头部厂商,青否数字人SaaS系统6.0重磅发布!

青否数字人SaaS系统6.0重磅发布&#xff01;唇形驱动效果及清晰度媲美硅基等头部厂商&#xff0c;同时优化数字人短视频模块。 唇形驱动媲美头部 青否数字人SaaS系统6.0版本重点优化了唇形驱动&#xff0c;AI技术已经实现与真人形象的1:1克隆&#xff0c;唇形、牙齿和舌头高清…

当科技遇上神器:用Streamlit定制AI可视化问答界面

Streamlit是一个开源的Python库&#xff0c;利用Streamlit可以快速构建机器学习应用的用户界面。 本文主要探讨如何使用Streamlit构建大模型外部知识检索的AI问答可视化界面。 我们先构建了外部知识检索接口&#xff0c;然后让大模型根据检索返回的结果作为上下文来回答问题。…

【AUTOSAR】【以太网】DoIp

AUTOSAR专栏——总目录_嵌入式知行合一的博客-CSDN博客文章浏览阅读217次。本文主要汇总该专栏文章,以方便各位读者阅读。https://xianfan.blog.csdn.net/article/details/132072415 目录 一、概述 二、功能描述 2.1 Do

DNS 域名解析协议

作用 将域名转化位IP地址 域名 用’ . ’ 隔开的字符串&#xff0c;如&#xff1a;www.badu.com,就是为了赋予IP特殊含义。 一级域名 .com &#xff1a;公用 .cn&#xff1a;中国 .gov&#xff1a;政府 .us&#xff1a;美国 .org&#xff1a;组织 .net&#xff1a;网站 对应一级…

HTML脚本、字符实体、URL

HTML脚本&#xff1a; JavaScript 使 HTML 页面具有更强的动态和交互性。 <script> 标签用于定义客户端脚本&#xff0c;比如 JavaScript。<script> 元素既可包含脚本语句&#xff0c;也可通过 src 属性指向外部脚本文件。 JavaScript 最常用于图片操作、表单验…