Spring的另一大的特征:AOP

2024-02-26 06:44
文章标签 java spring 特征 aop 一大

本文主要是介绍Spring的另一大的特征:AOP,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

  • AOP (Aspect Oriented Programming)
  • AOP 入门案例(注解版)
  • AOP 工作流程——代理
  • AOP切入点表达式
  • AOP 通知类型
  • AOP通知获取数据
    • 获取切入点方法的参数
    • 获取切入点方法返回值
    • 获取切入点方法运行异常信息
  • 百度网盘分享链接输入密码数据错误兼容性处理

AOP (Aspect Oriented Programming)

  • AOP (Aspect Oriented Programming) 面向切面编程 ,指导开发者如何组织程序结构

OOP (Object Oriented Programming) 面向对象编程

  • AOP 的作用: 在不惊动原始设计的基础上为其进行功能增强
  • Spring 理念: 无入侵式/无侵入式

在这里插入图片描述

  • 连接点(JointPoint): 程序执行过程中的任意位置,粒度为执行方法、抛出异常、设置变量等。

    • 在 SpringAOP 中,理解为方法的执行
  • 切入点(Pointcut): 匹配连接点的式子

    • 在 SpringAOP 中,一个切入点可以只描述一个具体方法,也可以匹配多个方法
      • 一个具体方法:eg : com.itheima.dao包下的BookDao接口中的无形参无返回值的save方法
      • 匹配多个方法:eg : 所有的save方法,所有的get开头的方法,所有以Dao结尾的接口中的任意方法,所有带有一个参数的方法
  • 通知(Advice): 在切入点处执行的操作,也就是共性功能

    • 在 SpringAOP 中,功能最终以方法的形式呈现
  • 通知类: 定义通知的类

  • 切面(Aspect): 描述通知与切入点的对应关系

AOP 入门案例(注解版)

- 导入 aop 相关坐标
    <dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.4</version></dependency>

spring-context 坐标依赖 spring-aop 坐标
在这里插入图片描述

    • 定义dao 接口与实现类
public interface BookDao {public void save();public void update();public void delete();public void select();
}
@Repository
public class BookDaoImpl implements BookDao {public void save() {//记录程序当前执行执行(开始时间)Long startTime = System.currentTimeMillis();//业务执行万次for (int i = 0;i<10000;i++) {System.out.println("book dao save ...");}//记录程序当前执行时间(结束时间)Long endTime = System.currentTimeMillis();//计算时间差Long totalTime = endTime-startTime;//输出信息System.out.println("执行万次消耗时间:" + totalTime + "ms");}public void update(){System.out.println("book dao update ...");}public void delete(){System.out.println("book dao delete ...");}public void select(){System.out.println("book dao select ...");}
}
    • 定义通知类,制作通知
    • 定义切入点:切入点定义依托一个不具有实际意义的方法进行,即无参数,无返回值,方法体无实际逻辑
    • 绑定切入点与通知关系,并指定通知添加到原始连接点的具体执行位置
    • 定义通知类受Spring容器管理,并定义当前类为切面类
@Component
@Aspect
public class MyAdvice {@Pointcut("execution(* com.itheima.dao.BookDao.*d*(..))")private void pt(){}@Around("pt()")public Object around(ProceedingJoinPoint pjp) throws Throwable {System.out.println("------------------------------");Long startTime = System.currentTimeMillis();for (int i = 0 ; i<10000 ; i++) {//调用原始操作pjp.proceed();}Long endTime = System.currentTimeMillis();Long totalTime = endTime-startTime;System.out.println("执行万次消耗时间:" + totalTime + "ms");return null;}}
    • 开启 Spring 对 AOP 注解驱动支持 : @EnableAspectJAutoProxy
@Configuration
@ComponentScan("com.itheima")
@EnableAspectJAutoProxy
public class SpringConfig {
}

AOP 工作流程——代理

  1. Spring 容器启动
  2. 读取所有切面配置中的切入点
  3. 初始化bean,判定bean对应的类中的方法是否匹配到任意切入点
    • 匹配失败,创建对象
    • 匹配成功,创建原始对象(目标对象)的代理对象
  4. 获取bean执行方法
    • 获取bean,调用方法并执行,完成操作
    • 获取bean是代理对象时, 根据代理对象的运行模式运行原始方法与增强的内容,完成操作
  • 目标对象(Target) : 原始功能去掉共性功能对应的类产生的对象,这种对象是无法直接完成最终工作的。
  • 代理(Proxy) : 目标对象无法直接完成工作,需要对其进行功能回填,通过原始对象的代理对象实现

AOP切入点表达式

  • 切入点:要进行增强的方法
  • 切入点表达式:要进行增强的方法的描述方式

在这里插入图片描述

  • 切入点表达式标准格式: 动作关键字 (访问修饰符 返回值 包名.类/接口名.方法名(参数)异常名)
execution (public User com.itheima.service.UserService.findById (int))
    • 动作关键字: 描述切入点的行为动作,例如execution表示执行到指定切入点
    • 访问修饰符: public , private 等 可省略
    • 返回值
    • 包名
    • 类/接口名
    • 方法名
    • 参数
    • 异常名:方法定义中抛出指定异常,可省略
  • 可以使用通配符描述切入点,快速描述:

    • eg: * : 单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现
execution (public * com.itheima.*.UserService.find* (*))

匹配com.itheima包下的任意包中的UserService类或接口中所有find开头的带有一个参数的方法

    • eg: . . :多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写
execution (public User com..UserService.findById (..))

匹配com包下的任意包中的UserService类或接口中所有名称为findByld的方法

    • eg: + :专用于匹配子类类型
execution (* *..*Service+.*(..))

AOP 通知类型

  • AOP 通知描述了抽取的共性功能,根据共性功能抽取的位置不同,最终运行代码时要将其加入到合理的位置
  • 5 种类型
    • 前置通知
    • 后置通知
    • 环绕通知 (重点) :通过ProceedingJoinPoint 对原始方法调用,获得返回值,最终返回这个返回值
    • 返回后通知 (了解)
    • 抛出异常后通知 (了解)
@Pointcut("execution(void com.itheima.dao.BookDao.update())")private void pt(){}@Pointcut("execution(int com.itheima.dao.BookDao.select())")private void pt2(){}//@Before:前置通知,在原始方法运行之前执行
//    @Before("pt()")public void before() {System.out.println("before advice ...");}//@After:后置通知,在原始方法运行之后执行
//    @After("pt2()")public void after() {System.out.println("after advice ...");}//@Around:环绕通知,在原始方法运行的前后执行
//    @Around("pt()")public Object around(ProceedingJoinPoint pjp) throws Throwable {System.out.println("around before advice ...");//表示对原始操作的调用Object ret = pjp.proceed();System.out.println("around after advice ...");return ret;}//    @Around("pt2()")public Object aroundSelect(ProceedingJoinPoint pjp) throws Throwable {System.out.println("around before advice ...");//表示对原始操作的调用Integer ret = (Integer) pjp.proceed();System.out.println("around after advice ...");return ret;}//@AfterReturning:返回后通知,在原始方法执行完毕后运行,且原始方法执行过程中未出现异常现象
//    @AfterReturning("pt2()")public void afterReturning() {System.out.println("afterReturning advice ...");}//@AfterThrowing:抛出异常后通知,在原始方法执行过程中出现异常后运行@AfterThrowing("pt2()")public void afterThrowing() {System.out.println("afterThrowing advice ...");}
}
  • @Around 注意事项
    • 环绕通知必须依赖形参 ProceedingJoinPoint 才能实现对原始方法的调用,进而实现原始方法调用前后同时添加通知
    • 通知中如果未使用 ProceedingJoinPoint 对原始方法进行调用将跳过原始方法的执行
    • 对原始方法的调用可以不接收返回值,通知方法设置成void即可,如果接收返回值,必须设定为Object类型
    • 原始方法的返回值如果是void类型,通知方法的返回值类型可以设置成void,也可以设置成Object
    • 由于无法预知原始方法运行后是否会抛出异常,因此环绕通知方法必须抛出Throwable对象

AOP通知获取数据

获取切入点方法的参数

  • JoinPoint: 适用于前置、后置、返回后、抛出异常后通知
  • JoinPoint 对象描述了连接点方法的运行状态,可以获取到原始方法的调用参数
	@Before("pt()")public void before(JoinPoint jp) {Object[] args = jp.getArgs();System.out.println(Arrays.toString(args));System.out.println("before advice ..." );}
  • ProceedJoinPoint: 适用于环绕通知, 是 JoinPoint 的子类
	@Around("pt()")public Object around(ProceedingJoinPoint pjp) throws Throwable{Object[] args = pjp.getArgs();System.out.println(Arrays.toString(args));ret = pjp.proceed(args);return ret;}

获取切入点方法返回值

  • 返回后通知
    //设置返回后通知获取原始方法的返回值,要求returning属性值必须与方法形参名相同@AfterReturning(value = "pt()",returning = "ret")public void afterReturning(JoinPoint jp, String ret) {System.out.println("afterReturning advice ..."+ret);}
  • 环绕通知 可以手工书写对原始方法的调用,得到的结果即为原始方法的返回值。
	@Around("pt()")public Object around(ProceedingJoinPoint pjp) throws Throwable{ret = pjp.proceed(args);return ret;}

获取切入点方法运行异常信息

  • 抛出异常后通知:可以获取切入点方法中出现的异常信息,使用形参可以接收对应的异常对象
    //设置抛出异常后通知获取原始方法运行时抛出的异常对象,要求throwing属性值必须与方法形参名相同@AfterThrowing(value = "pt()",throwing = "t")public void afterThrowing(Throwable t) {System.out.println("afterThrowing advice ..."+t);}
  • 环绕通知
	@Around("pt()")public Object around(ProceedingJoinPoint pjp) {Object ret = null;try {ret = pjp.proceed(args);} catch (Throwable t) {t.printStackTrace();}return ret;}

百度网盘分享链接输入密码数据错误兼容性处理

@Around("DataAdvice.servicePt()")
public Object trimString(ProceedingJoinPoint pjp) throws Throwable {Object[] args = pjp.getArgs();//对原始参数中的每一个参数进行操作for (int i = 0; i < args.length; i++) {if (args[i].getClass().equals(String.class)) {args[i] = args[i].toString().trim();}}return pjp.proceed(args);
}

这篇关于Spring的另一大的特征:AOP的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
http://www.chinasem.cn/article/747978

相关文章

Java Predicate接口定义详解

《JavaPredicate接口定义详解》Predicate是Java中的一个函数式接口,它代表一个判断逻辑,接收一个输入参数,返回一个布尔值,:本文主要介绍JavaPredicate接口的定义... 目录Java Predicate接口Java lamda表达式 Predicate<T>、BiFuncti

Spring Security基于数据库的ABAC属性权限模型实战开发教程

《SpringSecurity基于数据库的ABAC属性权限模型实战开发教程》:本文主要介绍SpringSecurity基于数据库的ABAC属性权限模型实战开发教程,本文给大家介绍的非常详细,对大... 目录1. 前言2. 权限决策依据RBACABAC综合对比3. 数据库表结构说明4. 实战开始5. MyBA

Spring Security方法级安全控制@PreAuthorize注解的灵活运用小结

《SpringSecurity方法级安全控制@PreAuthorize注解的灵活运用小结》本文将带着大家讲解@PreAuthorize注解的核心原理、SpEL表达式机制,并通过的示例代码演示如... 目录1. 前言2. @PreAuthorize 注解简介3. @PreAuthorize 核心原理解析拦截与

一文详解JavaScript中的fetch方法

《一文详解JavaScript中的fetch方法》fetch函数是一个用于在JavaScript中执行HTTP请求的现代API,它提供了一种更简洁、更强大的方式来处理网络请求,:本文主要介绍Jav... 目录前言什么是 fetch 方法基本语法简单的 GET 请求示例代码解释发送 POST 请求示例代码解释

Java图片压缩三种高效压缩方案详细解析

《Java图片压缩三种高效压缩方案详细解析》图片压缩通常涉及减少图片的尺寸缩放、调整图片的质量(针对JPEG、PNG等)、使用特定的算法来减少图片的数据量等,:本文主要介绍Java图片压缩三种高效... 目录一、基于OpenCV的智能尺寸压缩技术亮点:适用场景:二、JPEG质量参数压缩关键技术:压缩效果对比

Java调用C++动态库超详细步骤讲解(附源码)

《Java调用C++动态库超详细步骤讲解(附源码)》C语言因其高效和接近硬件的特性,时常会被用在性能要求较高或者需要直接操作硬件的场合,:本文主要介绍Java调用C++动态库的相关资料,文中通过代... 目录一、直接调用C++库第一步:动态库生成(vs2017+qt5.12.10)第二步:Java调用C++

springboot+dubbo实现时间轮算法

《springboot+dubbo实现时间轮算法》时间轮是一种高效利用线程资源进行批量化调度的算法,本文主要介绍了springboot+dubbo实现时间轮算法,文中通过示例代码介绍的非常详细,对大家... 目录前言一、参数说明二、具体实现1、HashedwheelTimer2、createWheel3、n

Java利用docx4j+Freemarker生成word文档

《Java利用docx4j+Freemarker生成word文档》这篇文章主要为大家详细介绍了Java如何利用docx4j+Freemarker生成word文档,文中的示例代码讲解详细,感兴趣的小伙伴... 目录技术方案maven依赖创建模板文件实现代码技术方案Java 1.8 + docx4j + Fr

SpringBoot首笔交易慢问题排查与优化方案

《SpringBoot首笔交易慢问题排查与优化方案》在我们的微服务项目中,遇到这样的问题:应用启动后,第一笔交易响应耗时高达4、5秒,而后续请求均能在毫秒级完成,这不仅触发监控告警,也极大影响了用户体... 目录问题背景排查步骤1. 日志分析2. 性能工具定位优化方案:提前预热各种资源1. Flowable

基于SpringBoot+Mybatis实现Mysql分表

《基于SpringBoot+Mybatis实现Mysql分表》这篇文章主要为大家详细介绍了基于SpringBoot+Mybatis实现Mysql分表的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可... 目录基本思路定义注解创建ThreadLocal创建拦截器业务处理基本思路1.根据创建时间字段按年进