百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

【Java面试最新】谈谈你对Spring的AOP的理解?

lipiwang 2024-11-13 13:39 36 浏览 0 评论

AOP 的概念

AOP(Aspect - Oriented Programming)即面向切面编程,是一种编程范式,它是对面向对象编程(OOP)的补充。在软件开发过程中,存在一些横切关注点(Cross - Cutting Concerns),这些关注点分散在多个对象或模块中,例如日志记录、事务管理、安全检查等。AOP 的目的是将这些横切关注点从业务逻辑中分离出来,使得业务逻辑更加纯粹,同时也提高了代码的可维护性和可重用性。

AOP 在 Spring 中的实现方式

  • 基于代理(Proxy)模式:Spring AOP 主要是通过代理模式来实现的。当一个目标对象(Target Object)需要被增强时,Spring 会为其创建一个代理对象。代理对象和目标对象实现相同的接口(如果目标对象是接口的实现类)或者继承自目标对象的类(如果目标对象是普通类)。在调用目标对象的方法时,实际上是通过代理对象来调用。代理对象在调用目标对象方法的前后可以添加额外的逻辑,从而实现对目标对象方法的增强。
  • 两种代理方式
  • JDK 动态代理:如果目标对象实现了接口,Spring 默认会使用 JDK 动态代理。JDK 动态代理是基于接口的代理,它通过反射机制在运行时动态地生成代理类。代理类实现了目标对象的接口,并且在调用接口方法时,会先执行切面逻辑,再调用目标对象的接口方法。例如,假设我们有一个接口UserService和它的实现类UserServiceImpl,当对UserServiceImpl进行 AOP 增强时,Spring 会使用 JDK 动态代理生成一个代理类,这个代理类实现了UserService接口,并且在调用接口方法时会添加切面逻辑。
  • CGLIB 代理:如果目标对象没有实现接口,Spring 会使用 CGLIB 代理。CGLIB 代理是基于子类的代理,它通过继承目标对象的类来生成代理对象。在代理对象的方法中,可以插入切面逻辑,并且在合适的时候调用目标对象的方法。例如,对于一个没有实现接口的普通类OrderService,Spring 会使用 CGLIB 代理生成一个OrderService的子类作为代理对象,这个代理对象可以在方法调用时添加切面逻辑。

AOP 的核心概念

  • 切面(Aspect):切面是一个模块化的横切关注点,它包含了切点(Pointcut)和通知(Advice)。例如,一个日志切面可以包含记录方法调用开始时间和结束时间的通知,以及定义在哪些方法上应用这些通知的切点。
  • 切点(Pointcut):切点用于定义在哪些连接点(Join Point)上应用切面的通知。连接点是程序执行过程中的一个点,例如方法的调用、方法的执行、异常的抛出等。切点可以通过表达式来指定,如使用 AspectJ 的切点表达式。例如,execution(* com.example.service.*.*(..))这个切点表达式表示匹配com.example.service包下所有类的所有方法。

配置 AOP 切面的切点

  • 基于 AspectJ 表达式配置切点(XML 和 Java 配置均可)
  • 精确匹配方法签名:可以使用execution关键字来定义切点,通过指定包名、类名和方法名来精确匹配方法。例如,要匹配com.example.service.UserService接口中的addUser方法,切点表达式可以写成execution(* com.example.service.UserService.addUser(..))。这里*表示返回值类型任意,(..)表示方法参数任意。
  • 匹配包下的所有方法:如果想匹配某个包下的所有类的所有方法,如com.example.service.impl包下的所有方法,可以使用execution(* com.example.service.impl.*.*(..))。这种方式在对整个服务层进行切面应用时比较常用,比如统一添加日志记录或事务管理。
  • 匹配特定类型的方法参数:除了匹配方法名和包名,还可以根据方法参数类型来定义切点。例如,要匹配参数为User类型的方法,可以写成execution(* com.example.service.*.*(com.example.entity.User))。如果方法有多个参数,并且要匹配特定顺序和类型的参数组合,可以按照实际参数顺序和类型依次写在括号内,如execution(* com.example.service.*.*(int, java.lang.String))。
  • 使用通配符:AspectJ 表达式支持通配符来简化切点表达式。*可以匹配任意字符(在包名、类名或方法名部分),..可以匹配任意数量的字符(在包名部分表示任意子包,在方法参数部分表示任意数量和类型的参数)。例如,execution(* com.example..*.*(..))可以匹配com.example包及其子包下的所有类的所有方法。
  • 在 Java 配置类中使用自定义方法定义切点(基于 AspectJ 注解)

除了直接使用 AspectJ 表达式,还可以在切面类中定义方法来返回切点表达式。例如:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LogAspect {
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void serviceMethods() {}

    @Before("serviceMethods()")
    public void beforeMethod() {
        System.out.println("方法开始执行");
    }
}
  • 在上述代码中,@Pointcut注解定义了一个名为serviceMethods的切点方法,这个方法返回一个切点表达式。然后在通知注解(如@Before)中可以直接引用这个切点方法名,这样使得切点表达式的复用性更强,当需要修改切点范围时,只需要修改@Pointcut注解中的表达式即可。

通知(Advice):通知是切面在特定的连接点上执行的动作。Spring AOP 提供了五种通知类型:

  • 前置通知(Before Advice):在目标方法调用之前执行的通知。可以用于进行权限检查、参数验证等操作。例如,在一个用户登录方法调用之前,通过前置通知检查用户输入的账号和密码是否符合格式要求。
  • 后置通知(After Advice):在目标方法正常完成(没有抛出异常)之后执行的通知。可以用于记录方法的执行结果等操作。例如,在一个数据查询方法执行后,通过后置通知记录查询到的数据数量。
  • 返回通知(After - Returning Advice):在目标方法正常返回后执行的通知,和后置通知类似,但可以获取目标方法的返回值。例如,在一个计算服务的方法返回结果后,通过返回通知对结果进行格式化处理。
  • 异常通知(After - Throwing Advice):在目标方法抛出异常时执行的通知。可以用于进行异常处理或者记录异常信息。

在 AOP 切面中处理异常

  • 异常通知(After - Throwing Advice)的使用:在切面类中,可以使用@After - Throwing注解来定义异常通知方法。例如:
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class ExceptionAspect {
    @AfterThrowing(pointcut = "execution(* com.example.service.*.*(..))", throwing = "ex")
    public void handleException(JoinPoint joinPoint, Exception ex) {
        System.out.println("方法 " + joinPoint.getSignature().getName() + " 抛出异常: " + ex.getMessage());
        // 可以在这里进行更复杂的异常处理,如记录日志、发送通知等
    }
}
  • 在上述代码中,@AfterThrowing注解的pointcut属性指定了切点表达式,用于定义在哪些方法抛出异常时执行这个通知。throwing属性指定了异常参数的名称,在通知方法中可以通过这个参数来获取抛出的异常对象。这样,当目标方法在切点范围内抛出异常时,异常通知方法就会被执行,可以在这个方法中对异常进行处理。
  • 环绕通知(Around Advice)中处理异常:对于环绕通知,因为它可以完全控制目标方法的执行,所以也可以在其中处理异常。例如:
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LogAspect {
    @Around("execution(* com.example.service.*.*(..))")
    public Object aroundMethod(ProceedingJoinPoint pjp) {
        try {
            return pjp.proceed();
        } catch (Throwable t) {
            System.out.println("环绕通知 - 方法执行出现异常: " + t.getMessage());
            // 可以根据具体情况进行异常处理,如返回默认值、重新抛出异常等
            return null;
        }
    }
}
  • 在环绕通知的try - catch块中,可以捕获目标方法执行过程中抛出的异常,然后进行相应的处理。例如,可以返回一个默认值来避免异常向上层传播,或者对异常进行记录后重新抛出异常。

AOP 的优势

  • 解耦横切关注点和业务逻辑:将日志记录、事务管理等横切关注点从业务逻辑中分离出来,使得业务逻辑代码更加专注于核心业务功能。这样,业务逻辑的代码更加清晰,易于理解和维护。例如,在一个复杂的电商系统中,订单处理业务逻辑不再和事务管理代码混杂在一起,而是通过 AOP 将事务管理作为切面应用到订单处理方法上。
  • 代码复用性高:切面可以在多个不同的目标对象或方法上重复使用。例如,一个日志切面可以应用到系统中的多个服务类的方法上,而不需要在每个方法中重复编写日志记录代码。这大大提高了代码的复用性,减少了代码冗余。
  • 易于维护和扩展:当需要修改横切关注点的逻辑时,只需要在切面中进行修改,而不需要在每个受影响的业务逻辑方法中进行修改。例如,如果要修改日志记录的格式或者级别,只需要在日志切面中修改通知的逻辑,而不需要在所有的业务方法中进行调整。同时,也可以方便地添加新的切面来满足新的横切关注点需求。

相关推荐

一个简单便捷搭建个人知识库的开源项目(MDwiki)

这里我通过自动翻译软件,搬运总结MDwiki官网的部署和使用方法。第一步:下载编译好的后MDwiki文件,只有一个HTML文件“mdwiki.html”。第二步:在mdwiki.html同级目录创建“...

强大、简洁、快速、持续更新 PandaWiki新一代 AI 驱动的开源知识库

PandaWiki是什么PandaWiki是一款AI大模型驱动的开源知识库搭建系统,帮助你快速构建智能化的产品文档、技术文档、FAQ、博客系统,借助大模型的力量为你提供AI创作、AI问答...

DeepWiki-Open: 开源版Deepwiki,可自己构建github文档库

Deepwiki是Devin团队开发的github文档库,用户能免费使用,但代码不是开源,而DeepWiki-Open侧是开源版本的实现。DeepWiki-Open旨在为GitHub和GitLa...

最近爆火的wiki知识管理开源项目PandaWiki

项目介绍PandaWiki是一款AI大模型驱动的开源知识库搭建系统,帮助你快速构建智能化的产品文档、技术文档、FAQ、博客系统,借助大模型的力量为你提供AI创作、AI问答、AI搜索等...

轻量级开源wiki系统介绍(轻量开源论坛系统)

wiki系统有很多DokuWiki、MediaWiki、MinDoc等等都是开源的wiki系统。商业版的wiki,像很多企业在用的confluence等。今天我们讲的是一款轻量级且开源的文档管理系统:...

DNS解析错误要怎么处理(dns解析状态异常怎么办)

在互联网时代,网络已经成为人们生活和工作中不可或缺的一部分。然而,当遇到DNS解析错误时,原本畅通无阻的网络访问会突然陷入困境,让人感到十分困扰。DNS,即域名系统,它如同互联网的电话簿,将人们易于...

网页加载慢?这些方法让你秒开网页!

打开浏览器,信心满满地准备查资料、看视频或者追剧,却发现网页怎么都打不开!是不是瞬间感觉手足无措?别慌,这个问题其实挺常见,而且解决起来并没有你想象的那么复杂。今天就来聊聊网页打不开究竟是怎么回事,以...

windows11 常用CMD命令大全(windows11msdn)

Windows11中的命令提示符(CMD)是一个强大的工具,可以通过命令行执行各种系统操作和管理任务。以下是一些常用的CMD命令,按功能分类整理,供你参考:一、系统信息与状态systeminfo显...

电脑提示DNS服务器未响应怎么解决?

我们在使用电脑的时候经常会遇到各种各样的网络问题,例如最近就有Win11电脑用户在使用的时候遇到了DNS未响应的问题,遇到这种情况我们应该怎么解决呢?  方法一:刷新DNS缓存  1、打开运行(W...

宽带拨号错误 651 全解析:故障定位与修复方案

在使用PPPoE拨号连接互联网时,错误651提示「调制解调器或其他连接设备报告错误」,通常表明从用户终端到运营商机房的链路中存在异常。以下从硬件、系统、网络三层维度展开排查:一、故障成因分类图...

如何正确清除 DNS 缓存吗?(解决你访问延时 )

DNS缓存是一个临时数据库,用于存储有关以前的DNS查找的信息。换句话说,每当你访问网站时,你的操作系统和网络浏览器都会保留该域和相应IP地址的记录。这消除了对远程DNS服务器重复查询的...

网络配置命令:ipconfig和ifconfig,两者有啥区别?

在计算机网络的世界里,网络接口就像是连接你电脑和外部网络的桥梁,而网络配置则是确保这座桥梁稳固、通信顺畅的关键。提到网络配置工具,ipconfig和ifconfig绝对是两个绕不开的名字。它们一...

救急的命令 你会几个?(救急一下)

很多人都说小编是注册表狂魔,其实不完全是,小编常用的命令行才是重点。其实所谓的命令行都是当初DOS时代的标准操作方式,随着Windows不断演化,DOS的命令早已成为Windows的一部分了——开始菜...

电脑有网却访问不了GitHub原来是这样

当满心欢喜打开电脑,准备在GitHub这个“开源宝藏库”里挖掘点超酷的项目,却遭遇了网页无法访问的尴尬。看着屏幕上那令人无奈的提示,原本高涨的热情瞬间被泼了一盆冷水,是不是感觉世界都不美好了...

rockstargames更新慢| r星更新速度 怎么办 解决办法

rockstargames更新慢|r星更新速度怎么办解决办法说到RockstarGames,那可是游戏界的大佬,作品个顶个的经典。但话说回来,每当新内容更新时,那蜗牛般的下载速度,真是让人急得...

取消回复欢迎 发表评论: