使用Spring的AOP

使用Spring的AOP

  • 一、AOP 的常用注解
    • 1.切面类@Aspect
    • 2.@Pointcut
    • 3.前置通知@Before
    • 4.后置通知@AfterReturning
    • 5.环绕通知@Around
    • 6.异常通知@AfterThrowing
    • 7.最终通知@After
    • 8.切面顺序@Order
    • 9.启用自动代理@EnableAspectJAutoProxy
  • 二、AOP注解方式开发
  • 三、AOP 全注解开发
  • 四、基于XML配置方式的AOP(了解)


  • Spring 对 AOP 的实现包括以下3种方式:
    • 第一种方式:Spring框架结合AspectJ框架实现的AOP,基于注解方式。
    • 第二种方式:Spring框架结合AspectJ框架实现的AOP,基于XML方式。
    • 第三种方式:Spring框架自己实现的AOP,基于XML方式。
  • 实际开发种都是Spring + AspectJ来实现的AOP。
  • 什么是AspectJ?(Eclipse组织的一个支持AOP的框架。AspectJ框架是独立于Spring框架之外的一个框架,Spring框架用了AspectJ)
  • AspectJ项目起源于帕洛阿尔托(Palo Alto)研究中心(缩写为PARC)。该中心由Xerox集团资助,Gregor Kiczales领导,从1997年开始致力于AspectJ的开发,1998年第一次发布给外部用户,2001年发布1.0 release。为了推动AspectJ技术和社团的发展,PARC在2003年3月正式将AspectJ项目移交给了Eclipse组织,因为AspectJ的发展和受关注程度大大超出了PARC的预期,他们已经无力继续维持它的发展。

一、AOP 的常用注解

1.切面类@Aspect

  • @Aspect作用是把当前类标识为一个切面供容器读取。

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE})
    public @interface Aspect {String value() default "";
    }
    

2.@Pointcut

  • @Pointcut注解标注在方法上面,用来定义切入点。
  • 可以这样做:将切点表达式单独的定义出来,在需要的位置引入即可。
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD})
    public @interface Pointcut {String value() default "";String argNames() default "";
    }
    

3.前置通知@Before

  • @Before目标方法执行之前的通知
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD})
    public @interface Before {String value();String argNames() default "";
    }
    

4.后置通知@AfterReturning

  • @AfterReturning目标方法执行之后的通知
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD})
    public @interface AfterReturning {String value() default "";String pointcut() default "";String returning() default "";String argNames() default "";
    }
    

5.环绕通知@Around

  • @Around目标方法之前添加通知,同时目标方法执行之后添加通知。
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD})
    public @interface Around {String value();String argNames() default "";
    }
    

6.异常通知@AfterThrowing

  • @AfterThrowing发生异常之后执行的通知
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD})
    public @interface AfterThrowing {String value() default "";String pointcut() default "";String throwing() default "";String argNames() default "";
    }
    

7.最终通知@After

  • @After放在finally语句块中的通知
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD})
    public @interface After {String value();String argNames() default "";
    }
    

8.切面顺序@Order

  • 我们知道,业务流程当中不一定只有一个切面,可能有的切面控制事务,有的记录日志,有的进行安全控制,如果多个切面的话,顺序如何控制:可以使用@Order注解来标识切面类,为@Order注解的value指定一个整数型的数字,数字越小,优先级越高。
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
    @Documented
    public @interface Order {/*** The order value.* <p>Default is {@link Ordered#LOWEST_PRECEDENCE}.* @see Ordered#getOrder()*/int value() default Ordered.LOWEST_PRECEDENCE;
    }
    

9.启用自动代理@EnableAspectJAutoProxy

  • 开启自动代理之后,凡事带有@Aspect注解的bean都会生成代理对象。
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Import(AspectJAutoProxyRegistrar.class)
    public @interface EnableAspectJAutoProxy {boolean proxyTargetClass() default false;boolean exposeProxy() default false;
    }
    

二、AOP注解方式开发

  • 注意:本文使用了 log4j2 日志,如果不知道可以看我的博客 ===> Spring对IoC的实现中的第一个Spring程序

  • 注意本文也使用了 junit 进行单元测试。

  • 使用Spring+AspectJAOP需要引入的依赖如下:

    <!--spring的核心依赖 aop core beans jcl expression 等-->
    <dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>6.1.4</version>
    </dependency>
    <!-- AOP 依赖的AspectJ -->
    <dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>6.1.4</version>
    </dependency>
    
  • Spring配置文件中添加context命名空间和aop命名空间

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"></beans>
    
  • 第一步:定义目标类以及目标方法

    package com.gdb.service;import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Service;//目标类
    @Service
    public class OrderService {private static final Logger logger = LoggerFactory.getLogger(OrderService.class);//目标方法public void detail() {logger.info("正在打印订单详情......");}
    }
    
  • 第二步:编写切面类

    package com.gdb.aspect;import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;//切面类(通知+切点 = 切面)
    @Aspect
    @Component
    public class MyAspect {private static final Logger logger = LoggerFactory.getLogger(MyAspect.class);//这是需要增强的代码(通知)@Before("execution(* com.gdb.service..* (..))") // com.gdb.service包下的所有方法public void beforeAdvice() {logger.info("前置通知执行了");}@AfterReturning("execution(* com.gdb.service..* (..))") // com.gdb.service包下的所有方法,public void afterReturningAdvice() {logger.info("后置通知执行了");}@Around("execution(* com.gdb.service..* (..))") // com.gdb.service包下的所有方法,public void aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {logger.info("前置环绕通知执行了");proceedingJoinPoint.proceed(); // 执行目标方法logger.info("后置环绕通知执行了");}@AfterThrowing("execution(* com.gdb.service..* (..))") // com.gdb.service包下的所有方法,public void afterThrowingAdvice() {logger.info("异常通知执行了");}@After("execution(* com.gdb.service..* (..))") // com.gdb.service包下的所有方法,public void afterAdvice() {logger.info("最终通知执行了");}
    }
    
  • 第三步:在配置文件中启动包扫描启用自动代理

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"><!--开启组件扫描--><context:component-scan base-package="com.gdb"/><!--开启自动代理--><aop:aspectj-autoproxy proxy-target-class="false"/><!--<aop:aspectj-autoproxy  proxy-target-class="true"/> 开启自动代理之后,凡事带有@Aspect注解的bean都会生成代理对象。proxy-target-class="true" 表示采用cglib动态代理。proxy-target-class="false" 表示采用jdk动态代理。默认值是false。即使写成false,当没有接口的时候,也会自动选择cglib生成代理类。-->
    </beans>
    
  • 第四步:编写测试程序

    @Test
    public void test(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");OrderService orderService = applicationContext.getBean("orderService", OrderService.class);orderService.detail();
    }
    
  • 第五步:执行结果
    在这里插入图片描述

    • 通过上面的执行结果就可以判断他们的执行顺序了,这里不再赘述。
  • 第六步:结果中没有异常通知,这是因为目标程序执行过程中没有发生异常。我们尝试让目标方法发生异常:

    package com.gdb.service;import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Service;@Service
    public class OrderService {private static final Logger logger = LoggerFactory.getLogger(OrderService.class);public void detail() {logger.info("正在打印订单详情......");throw new RuntimeException();}
    }
    
  • 第七步:执行结果:
    在这里插入图片描述

    • 通过测试得知,当发生异常之后,最终通知也会执行,因为最终通知@After会出现在finally语句块中。出现异常之后,后置通知环绕通知的结束部分不会执行。
  • 优化使用切点表达式:

    • 上面编写的切面类的缺点是:
      • 第一:切点表达式重复写了多次,没有得到复用。
      • 第二:如果要修改切点表达式,需要修改多处,难维护。
  • 可以这样做:将切点表达式单独的定义出来,在需要的位置引入即可。

    package com.gdb.aspect;import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;@Aspect
    @Component
    public class MyAspect {private static final Logger logger = LoggerFactory.getLogger(MyAspect.class);@Pointcut("execution(* com.gdb.service..* (..))")public void pointcut() {}@Before("pointcut()") // com.gdb.service包下的所有方法public void beforeAdvice() {logger.info("前置通知执行了");}@AfterReturning("pointcut()") // com.gdb.service包下的所有方法,public void afterReturningAdvice() {logger.info("后置通知执行了");}@Around("pointcut()") // com.gdb.service包下的所有方法,public void aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {logger.info("前置环绕通知执行了");proceedingJoinPoint.proceed();logger.info("后置环绕通知执行了");}@AfterThrowing("pointcut()") // com.gdb.service包下的所有方法,public void afterThrowingAdvice() {logger.info("异常通知执行了");}@After("pointcut()") // com.gdb.service包下的所有方法,public void afterAdvice() {logger.info("最终通知执行了");}
    }
    
  • 使用@Pointcut注解来定义独立的切点表达式。

  • 注意这个@Pointcut注解标注的方法随意,只是起到一个能够让@Pointcut注解编写的位置。


三、AOP 全注解开发

  • 第一步:就是编写一个类,在这个类上面使用大量注解来代替spring的配置文件,spring配置文件消失了,如下:
    package com.gdb.config;import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.EnableAspectJAutoProxy;@Configuration // 配置类
    @ComponentScan("com.gdb")
    @EnableAspectJAutoProxy(proxyTargetClass = false)
    public class SpringConfig {
    }
    
  • 第二步:测试程序也变化了
    @Test
    public void test() {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);OrderService orderService = applicationContext.getBean("orderService", OrderService.class);orderService.detail();
    }
    
  • 第三步:执行结果
    在这里插入图片描述

四、基于XML配置方式的AOP(了解)

  • 第一步:编写目标类
package com.gdb.service;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class OrderService {private static final Logger logger = LoggerFactory.getLogger(OrderService.class);public void detail() {logger.info("正在打印订单详情......");}
}
  • 第二步:编写切面类,并且编写通知
package com.gdb.aspect;import org.aspectj.lang.ProceedingJoinPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class MyAspect {private static final Logger logger = LoggerFactory.getLogger(MyAspect.class);public void beforeAdvice() {logger.info("前置通知执行了");}public void afterReturningAdvice() {logger.info("后置通知执行了");}public void aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {logger.info("前置环绕通知执行了");proceedingJoinPoint.proceed();logger.info("后置环绕通知执行了");}public void afterThrowingAdvice() {logger.info("异常通知执行了");}public void afterAdvice() {logger.info("最终通知执行了");}
}
  • 第三步:编写spring配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"><bean id="myAspect" class="com.gdb.aspect.MyAspect"/><bean id="orderService" class="com.gdb.service.OrderService"/><!--aop配置--><aop:config><!--切点表达式--><aop:pointcut id="p" expression="execution(* com.gdb.service..* (..))"/><!--切面--><aop:aspect ref="myAspect"><!--切面=通知 + 切点--><aop:before method="beforeAdvice" pointcut-ref="p"/><aop:after-returning method="afterReturningAdvice" pointcut-ref="p"/><aop:around method="aroundAdvice" pointcut-ref="p"/><aop:after-throwing method="afterThrowingAdvice" pointcut-ref="p"/><aop:after method="afterAdvice" pointcut-ref="p"/></aop:aspect></aop:config>
</beans>
  • 第四步:编写测试程序
@Test
public void test() {ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");OrderService orderService = applicationContext.getBean("orderService", OrderService.class);orderService.detail();
}
  • 第四步:执行结果
    在这里插入图片描述
  • 通过结果可以看出来顺序和前面的不一样了,我感觉是配置文件中的顺序有关系,由于主要都是使用注解的方式。

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

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

相关文章

5G网络助力智慧文旅发展:实现旅游资源的优化配置与高效利用

目录 一、5G网络在智慧文旅中的关键作用 1、高速率传输提升数据处理能力 2、低时延助力实时决策与调度 3、大连接实现全面覆盖与精细化管理 二、5G网络助力实现旅游资源的优化配置 1、精准匹配游客需求与旅游资源 2、促进旅游资源的跨区域合作与共享 三、5G网络助力实现…

【Python】科研代码学习:七 TrainingArguments,Trainer

【Python】科研代码学习&#xff1a;七 TrainingArguments&#xff0c;Trainer TrainingArguments重要的方法 Trainer重要的方法使用 Trainer 的简单例子 TrainingArguments HF官网API&#xff1a;Training 众所周知&#xff0c;推理是一个大头&#xff0c;训练是另一个大头 之…

XSS-Labs靶场“11-13、15-16”关通关教程

君衍. 一、第十一关 referer参数注入二、第十二关 user-agent参数注入三、第十三关 cookie参数绕过四、第十五关 ng-include文件包含五、第十六关 回车代替空格 点击跳转&#xff1a; XSS-Labs靶场“1-5”关通关教程 XSS-Labs靶场“6-10”关通关教程 一、第十一关 referer参数…

[uni-app ] createAnimation锚点旋转 及 二次失效问题处理

记录一下: 锚点定位到左下角, 旋转动画 必须沿Z轴,转动 但是,此时会出现 后续动画在微信小程序失效问题 解决: 清空 this.animationData

201912青少年软件编程(Scratch)等级考试试卷(一级)

201912 青少年软件编程&#xff08;Scratch&#xff09;等级考试试卷&#xff08;一级&#xff09; 第1题&#xff1a;【 单选题】 关于造型和背景&#xff0c;下面说法不正确的是&#xff1f; A:造型编号从1开始 B:有四个背景&#xff0c;删除第二个背景&#xff0c;背景编…

11_Http

文章目录 HttpHttp协议网络模型Http协议的工作流程Http请求报文请求行请求方法请求资源协议版本 请求头空行请求体抓包软件&#xff1a;Fiddler Http响应报文响应行状态码 响应头响应体 请求完整的处理流程 Https 整体流程图&#xff1a; 前端&#xff1a;负责获取数据&#xf…

雷赛控制卡获取轴当前位置的值不正确问题处理

现像 从雷赛控制卡中获取当前轴位置值时发现轴在向零点的右边走时显示的值是负数。正常来就一般是要反馈正数的。一般轴零点右边是正方向&#xff0c;限位是正限位&#xff0c;反馈的位置也应该是正数。 如果雷赛软件中的【单轴参数】中的基本设置中的【脉冲模式】设置的是对的…

【C语言基础】:深入理解指针(终篇)

文章目录 深入理解指针一、函数指针变量4.1 函数指针变量的创建4.2 函数指针变量的使用4.3 typedef关键字 二、函数指针数组三、转移表四、回调函数4.1 什么是回调函数4.2 qsort使用举例4.2.1 使用qsort函数排序整形数据4.2.2 使用qsort排序结构数据4.2.3 qsort函数的模拟实现 …

elasticsearch 深度分页查询 Search_after(图文教程)

Search_after使用 一. 简介二. 不带PIT的search_after查询2.1 构造数据2.2 search_after分页查询2.2 问题 三. 带PIT的search_after查询3.1 构建第一次查询条件3.2 进行下一页查询3.3 删除PIT 四.参考文章 前言 这是我在这个网站整理的笔记,有错误的地方请指出&#xff0c;关注…

傅里叶变换pytorch使用

参考视频&#xff1a;1 傅里叶变换原理_哔哩哔哩_bilibili 傅里叶变换是干嘛的&#xff1a; 傅里叶得到低频、高频信息&#xff0c;针对低频、高频处理能够实现不同的目的。 傅里叶过程是可逆的&#xff0c;图像经过傅里叶变换、逆傅里叶变换后&#xff0c;能够恢复到原始图像…

【管理干部竞聘上岗】某星级酒店中层干部竞聘上岗管理咨询项目纪实

在这次项目合作中&#xff0c;我们的目的主要是设计一次公开、透明的竞聘活动&#xff0c;通过科学、公正的方法选拔出公司管理级岗位的最佳候选人。基于华恒智信的专业性&#xff0c;我们再次选择与其合作开展项目。在项目合作中&#xff0c;专家团队为我们进行了专业性的培训…

AIGC实战——GPT(Generative Pre-trained Transformer)

AIGC实战——GPT 0. 前言1. GPT 简介2. 葡萄酒评论数据集3. 注意力机制3.1 查询、键和值3.2 多头注意力3.3 因果掩码 4. Transformer4.1 Transformer 块4.2 位置编码 5. 训练GPT6. GPT 分析6.1 生成文本6.2 注意力分数 小结系列链接 0. 前言 注意力机制能够用于构建先进的文本…

【网络原理】TCP 协议中比较重要的一些特性(一)

目录 1、TCP 协议 2、确认应答 2.1、确认序号 3、超时重传 4、连接管理 4.1、建立连接&#xff08;三次握手&#xff09; 4.2、断开连接&#xff08;四次挥手&#xff09; 1、TCP 协议 TCP 是工作中最常用到的协议&#xff0c;也是面试中最常考的协议&#xff0c;具有面…

Electron程序如何在MacOS下获取相册访问权限

1.通过entitiment.plist&#xff0c;在electron-builder签名打包时&#xff0c;给app包打上签名。最后可以通过codesign命令进行验证。 TestPhotos.plist electron-builder配置文件中加上刚刚的plist文件。 通过codesign命令验证&#xff0c;若出现这个&#xff0c;则说明成…

Fortran语法介绍(三)

个人专栏—ABAQUS专栏 Abaqus2023的用法教程——与VS2022、oneAPI 2024子程序的关联方法 Abaqus2023的用法教程——与VS2022、oneAPI 2024子程序的关联方法Abaqus有限元分析——有限元网格划分基本原则 Abaqus有限元分析——有限元网格划分基本原则各向同性线弹性材料本构模型…

《手把手教你》系列技巧篇(二十七)-java+ selenium自动化测试- quit和close的区别(详解教程)

1.简介 尽管有的小伙伴或者童鞋们觉得很简单&#xff0c;不就是关闭退出浏览器&#xff0c;但是宏哥还是把两个方法的区别说一下&#xff0c;不然遇到坑后根本不会想到是这里的问题。 2.源码 本文介绍webdriver中关于浏览器退出操作。driver中有两个方法是关于浏览器关闭&…

SQL28 计算用户8月每天的练题数量

&#x1f468;‍&#x1f4bb; 大唐coding&#xff1a;个人主页 &#x1f381; 个人专栏: 《力扣高频刷题宝典》《SQL刷题记录》 ⛵ 既然选择远方&#xff0c;当不负青春&#xff0c;砥砺前行&#xff01; 大家好&#xff0c;我是大唐&#xff0c;今天我们来做一道牛客题库SQL…

MySQL-----存储过程

▶ 介绍 存储过程是事先经过编译并存储在数据库中的一段SQL语句的集合&#xff0c;调用存储过程可以简化应用开发人员的很多工作&#xff0c;减少数据在数据库和应用服务器之间的传输&#xff0c;对于提高数据处理的效率是有好处的。 存储过程思想上很简单&#xff0c;…

C switch 语句

一个 switch 语句允许测试一个变量等于多个值时的情况。每个值称为一个 case&#xff0c;且被测试的变量会对每个 switch case 进行检查。 语法 C 语言中 switch 语句的语法&#xff1a; switch(expression){case constant-expression :statement(s);break; /* 可选的 */ca…

C语言中的UTF-8编码转换处理

C语言UTF-8编码的转换 1.C语言简介2.什么是UTF-8编码&#xff1f;2.1 UTF-8编码特点&#xff1a; 3.C语言中的UTF-8编码转换处理步骤1&#xff1a;获取UTF-8编码的字节流步骤2&#xff1a;解析UTF-8编码步骤3&#xff1a;Unicode码点转换为汉字 4.总结 1.C语言简介 C语言是一门…