深入理解 Spring 框架中的 AOP 技术
一、引言
在 Java 开发领域,Spring 框架凭借其强大的功能和丰富的特性,成为了众多开发者构建企业级应用的首选。其中,面向切面编程(AOP)作为 Spring 框架的核心技术之一,为开发者提供了一种全新的程序结构组织方式,能够在不修改原有业务逻辑代码的基础上,实现对程序功能的统一维护和增强。本文将深入探讨 Spring 框架中的 AOP 技术,从概念引入到实际应用,全面解析其原理和使用方法。
二、AOP 概念的引入
在实际的应用开发中,我们常常会遇到这样的场景:在已有的业务逻辑之上,需要添加一些额外的功能,比如权限校验、日志记录、事务管理等。以常见的登录功能为例,假设我们已经实现了基本的登录逻辑,如果要在登录过程中添加权限校验功能,传统的做法可能是直接修改登录相关的源代码。然而,这种方式存在诸多弊端,比如代码的可维护性降低,一旦后续需求发生变化,再次修改代码可能会引发一系列潜在的问题。
而 AOP 技术则提供了另一种解决方案,即不通过修改源代码的方式来添加新的功能。它能够将这些与业务逻辑无关,但又需要在多个地方重复使用的功能(如权限校验)进行横向抽取,独立于业务逻辑之外进行管理,从而实现对程序功能的增强。这种方式不仅提高了代码的可维护性和可重用性,还大大提升了开发效率。
三、AOP 相关的概念
(一)AOP 的概述
AOP,即 Aspect Oriented Programming,面向切面编程。它是一种编程范式,属于软件工程范畴,主要指导开发者如何组织程序结构。AOP 最早由 AOP 联盟提出,并制定了一套规范。Spring 框架将 AOP 思想引入其中,遵循 AOP 联盟的规范。
从技术实现角度来看,AOP 通过预编译方式或者运行期动态代理来实现程序功能的统一维护。它是 OOP(面向对象编程)的延续,在软件开发领域备受关注,也是 Spring 框架的重要组成部分,同时也是函数式编程的一种衍生范型。
利用 AOP,我们可以将业务逻辑的各个部分进行隔离,降低业务逻辑各部分之间的耦合度。例如,将日志记录、事务管理等功能从核心业务逻辑中分离出来,使得这些功能可以独立开发、测试和维护。这样不仅提高了程序的可重用性,还加快了开发进度。AOP 采取横向抽取机制,有效取代了传统纵向继承体系中重复性代码(如事务管理、安全检查、缓存等方面的代码)。学习 AOP 的最大好处在于,我们能够在不修改源代码的前提下,对程序进行增强。
(二)AOP 的优势
- 减少重复的代码:在传统的开发模式中,诸如日志记录、权限校验等功能往往需要在多个业务方法中重复编写代码。而使用 AOP,我们可以将这些通用功能集中实现,通过切面的方式应用到相关的业务方法上,避免了大量重复代码的编写。
- 提高开发的效率:由于 AOP 能够将通用功能与业务逻辑分离,开发人员可以更加专注于业务逻辑的实现,而无需在每个业务方法中都关注那些通用功能的代码编写。这使得开发过程更加高效,能够更快地完成项目开发。
- 维护方便:当通用功能的需求发生变化时,例如日志记录的格式需要调整或者权限校验的规则发生改变,只需要在切面中进行修改,而无需逐个修改涉及到这些功能的业务方法。这大大降低了维护成本,提高了系统的可维护性。
(三)AOP 的底层原理
- JDK 的动态代理技术
-
- 为接口创建代理类的字节码文件:JDK 动态代理会根据目标接口动态生成一个代理类的字节码文件。在这个过程中,代理类会实现目标接口,并在代理方法中调用实际目标对象的方法,同时可以在调用前后添加额外的逻辑,即我们所说的通知。
-
- 使用 ClassLoader 将字节码文件加载到 JVM:生成的代理类字节码文件需要被加载到 Java 虚拟机(JVM)中才能使用。ClassLoader 负责将字节码文件加载到 JVM 内存中,使得程序可以使用该代理类。
-
- 创建代理类实例对象,执行对象的目标方法:当需要使用代理对象时,通过反射机制创建代理类的实例对象。在调用代理对象的方法时,实际会执行代理类中重写的方法,在这个方法中,会先执行通知逻辑,然后调用目标对象的实际方法。
- cglib 代理技术
cglib 代理技术则是为类生成代理对象,无论被代理类是否有接口都可以使用。它的底层原理是通过生成被代理类的子类来实现代理功能。在子类中,对被代理类的方法进行拦截和增强,同样可以在方法调用前后添加通知逻辑。
四、Spring 的 AOP 技术 - 配置文件方式
(一)AOP 相关的术语
- Joinpoint (连接点):在一个类中,那些可以被增强的方法被称为连接点。简单来说,就是程序执行过程中能够插入额外逻辑的位置,通常是方法调用。
- Pointcut (切入点):切入点是对哪些连接点进行拦截的定义。它通过切入点表达式来指定,用于确定具体要对哪些方法进行增强操作。例如,我们可以通过切入点表达式指定只对某个类中的特定方法进行增强。
- Advice (通知 / 增强):通知是指拦截到连接点之后所要执行的操作。通知可以分为前置通知、后置通知、异常通知、最终通知和环绕通知等类型。这些通知代表了切面要完成的具体功能。比如,前置通知在目标方法执行前执行,后置通知在目标方法执行成功后执行等。
- Aspect (切面):切面是切入点和通知的结合。在实际开发中,我们需要自己编写和配置切面,将切入点表达式与相应的通知关联起来,以实现对特定方法的增强功能。
(二)基本准备工作
AspectJ 是一个面向切面的框架,它对 Java 语言进行了扩展,定义了 AOP 语法。实际上,AspectJ 是对 AOP 编程思想的一种实践。在使用 Spring 的 AOP 技术时,我们常常会借助 AspectJ 的相关功能。
(三)AOP 配置文件方式的入门
- 创建 maven 项目,添加坐标依赖:首先,我们需要创建一个 maven 项目,并在项目的 pom.xml 文件中添加相关的依赖坐标。这些依赖包括 Spring 的核心上下文依赖、日志依赖、测试依赖以及 AOP 相关的依赖,如 aopalliance、spring-aspects 和 aspectjweaver 等。具体的依赖配置如下:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--AOP联盟-->
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<!--Spring Aspects-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--aspectj-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.3</version>
</dependency>
</dependencies>
- 创建被增强的类:接下来,我们创建一个被增强的类。例如,创建一个名为 User 的类,其中包含一些方法,这些方法将作为连接点供我们进行增强操作。
// 被增强的类
public class User {
//连接点/切入点
public void add(){
System.out.println("add......");
}
public void update(){
System.out.println("update......");
}
}
- 将目标类配置到 Spring 中:在 Spring 的配置文件(applicationContext.xml)中,将 User 类配置为一个 Bean,以便 Spring 容器进行管理。
<bean id="user" class="com.aopImpl.User"></bean>
- 定义切面类:创建一个切面类,例如 UserProxy 类,在该类中定义各种通知方法。比如,定义一个前置通知方法 before ()。
public class UserProxy {
//增强/通知 ---》前置通知
public void before(){
System.out.println("before.............");
}
}
- 在配置文件中定义切面类:同样在 Spring 配置文件中,将 UserProxy 类也配置为一个 Bean。
<bean id="userProxy" class="com.aopImpl.UserProxy"></bean>
- 在配置文件中完成 aop 的配置:最后,在配置文件中进行 AOP 的核心配置。通过<aop:config>标签来配置切面,将切入点和通知关联起来。例如,配置一个前置通知,在 User 类的 add () 方法执行前进行增强。
<!--配置切面-->
<aop:config>
<!--配置切面 = 切入点 + 通知组成-->
<aop:aspect ref="userProxy">
<!--前置通知:UserServiceImpl的save方法执行前,会增强-->
<!--pointcut:后边是切入点表达式,作用是知道对对面的那个方法进行增强-->
<aop:before method="before" pointcut="execution(public void com.aopImpl.User.add())"/>
</aop:aspect>
</aop:config>
- 完成测试:编写测试类,通过 Spring 的 ApplicationContext 获取 User 类的实例,并调用其 add () 方法,观察前置通知是否生效。
public class DemoTest {
@Test
public void aopTest1(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User) applicationContext.getBean("user");
user.add();
}
}
(四)切入点的表达式
在配置切入点时,需要使用切入点表达式来精确指定要增强的方法。切入点表达式的格式如下:
execution([修饰符] [返回值类型] [类全路径] [方法名 ( [参数] )])
- 修饰符:可以省略不写,不是必须出现的部分。
- 返回值类型:不能省略,需要根据实际方法的返回值类型编写,也可以使用*代替所有返回值类型。
- 包名、类名、方法名和参数的规则:
-
- 包名、类名和方法名通常不能省略,但可以使用*进行通配。例如,com.qcby.demo3.BookDaoImpl.save(),可以使用*来简化表示,如com.qcby.*.BookDaoImpl.save()表示com.qcby包下所有以BookDaoImpl结尾的类中的save方法。
-
- 中间的包名可以使用*号代替,类名也可以使用*号代替,还可以使用类似*DaoImpl的写法。方法同样可以使用*号代替。
-
- 参数部分,如果是一个参数可以使用*号代替,如果想代表任意参数则使用..。
以下是一些常见的切入点表达式示例:
- 比较通用的表达式:execution(* com.qcby.*.ServiceImpl.save(..)),表示对com.qcby包下所有以ServiceImpl结尾的类中的save方法进行增强,且方法可以接受任意类型和数量的参数。
- 对某个类中所有方法进行增强:execution(* com.qcby.*.ServiceImpl.*(..)),表示对com.qcby包下所有以ServiceImpl结尾的类中的所有方法进行增强,方法接受任意参数。
- 对某个包中所有方法进行增强:execution(* com.qcby.*.*.*(..)),表示对com.qcby包及其子包下的所有类中的所有方法进行增强,方法接受任意参数。
(五)AOP 的通知类型
- 前置通知:在目标方法执行前进行增强。如上述配置案例中,在 User 类的 add () 方法执行前,会执行 before () 方法中的逻辑。
- 环绕通知:在目标方法执行前后都可以进行增强。需要注意的是,在环绕通知方法中,目标对象的方法需要手动调用。例如:
// 环绕通知
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("before.............");
// 执行被增强的方法
proceedingJoinPoint.proceed();
System.out.println("after.............");
}
在 xml 配置中,使用<aop:around>标签来配置环绕通知:
<aop:around method="around" pointcut="execution(* com.*.User.add(..))"/>
- 最终通知:无论目标方法执行成功还是失败,都会进行增强。例如:
// 最终通知
public void after() {
System.out.println("after.............");
}
xml 配置如下:
<aop:after method="after" pointcut="execution(* com.*.User.add(..))"/>
- 后置通知:在目标方法执行成功后进行增强。例如:
//后置通知
public void afterReturning() {
System.out.println("afterReturning.............");
}
xml 配置:
<aop:after-returning method="afterReturning" pointcut="execution(public void com.aopImpl.User.add())"/>
- 异常通知:在目标方法执行失败后进行增强,只有当目标方法发生异常时才会执行。例如:
//异常通知
public void afterThrowing() {
System.out.println("afterThrowing.............");
}
需要注意的是,为了触发异常通知,需要在目标方法中故意制造异常。例如:
//连接点/切入点
public void add(){
int a = 10 / 0;
System.out.println("add......");
}
xml 配置:
<aop:after-throwing method="afterThrowing" pointcut="execution(public void com.aopImpl.User.add())"/>
五、Spring 的 AOP 技术 - 注解方式
(一)AOP 注解方式入门程序
- 创建 maven 工程,导入坐标:与配置文件方式类似,首先创建一个 maven 项目,并导入相关的依赖坐标,包括 Spring 的核心依赖、AOP 相关依赖等。
- 编写接口,完成 IOC 的操作:根据实际业务需求,编写相关的接口,并通过 Spring 的 IOC 机制进行配置和管理,这里步骤略。
- 编写切面类:创建一个切面类,例如 UserProxy 类。给切面类添加@Aspect注解,表明该类是一个切面。然后在类中编写各种增强方法,并使用相应的通知类型注解来声明。
- 配置 xml 扫描注解:在 Spring 的配置文件(applicationContext.xml)中,配置注解扫描,确保 Spring 容器能够识别并处理这些注解。同时,需要引入相关的命名空间。
5.配置文件中开启自动代理:在 Spring 配置文件中,通过<aop:aspectj-autoproxy>标签开启 AspectJ 自动代理功能,使得 Spring 能够基于注解创建代理对象来实现 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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--开启注解扫描-->
<context:component-scan base-package="com.aopImpl"></context:component-scan>
<!--开启Aspect生成代理对象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
- 通知类型注解:在切面类中,使用不同的通知类型注解来定义增强逻辑。
-
- @Before -- 前置通知:用于在目标方法执行前执行增强逻辑。例如:
@Component
@Aspect //生成代理对象
public class UserProxy {
//增强/通知 ---》前置通知
@Before(value = "execution(* com.*.User.add(..))")
public void before(){
System.out.println("before.............");
}
}
- @AfterReturning -- 后置通知:在目标方法成功执行返回后执行增强逻辑。
//后置通知
@AfterReturning(value = "execution(* com.*.User.add(..))")
public void afterReturning() {
System.out.println("afterReturning.............");
}
- @Around -- 环绕通知:可以在目标方法执行前后都执行增强逻辑,且目标对象的方法默认不执行,需要手动通过ProceedingJoinPoint的proceed()方法来触发。
// 环绕通知
@Around(value = "execution(* com.*.User.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("before.............");
// 执行被增强的方法
proceedingJoinPoint.proceed();
System.out.println("after.............");
}
- @After -- 最终通知:无论目标方法执行成功与否,都会执行该通知的增强逻辑。
// 最终通知
@After(value = "execution(* com.*.User.add(..))")
public void after() {
System.out.println("after.............");
}
- @AfterThrowing -- 异常抛出通知:当目标方法抛出异常时执行增强逻辑。
//异常通知
@AfterThrowing(value = "execution(* com.*.User.add(..))")
public void afterThrowing() {
System.out.println("afterThrowing.............");
}
- 测试类:编写测试类来验证 AOP 注解配置是否生效。通过ApplicationContext获取被增强的对象,并调用其方法,观察通知是否按照预期执行。
@Test
public void aopTest1(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = applicationContext.getBean(User.class);
user.add();
}
六、AOP 在实际项目中的应用场景
(一)日志记录
在企业级应用中,日志记录是非常重要的功能。通过 AOP,我们可以在不修改业务逻辑代码的情况下,为所有需要记录日志的方法添加日志记录功能。例如,在方法执行前记录方法的开始时间和入参,在方法执行后记录方法的结束时间和返回值。这样可以方便我们进行系统调试、性能分析以及问题排查。
@Component
@Aspect
public class LoggingAspect {
@Before("execution(* com.example.service.*.*(..))")
public void logBefore(JoinPoint joinPoint) {
System.out.println("开始执行方法:" + joinPoint.getSignature().getName());
System.out.println("入参:" + Arrays.toString(joinPoint.getArgs()));
}
@AfterReturning(pointcut = "execution(* com.example.service.*.*(..))", returning = "result")
public void logAfterReturning(JoinPoint joinPoint, Object result) {
System.out.println("方法 " + joinPoint.getSignature().getName() + " 执行成功,返回值:" + result);
}
}
(二)事务管理
事务管理是保证数据一致性和完整性的关键。使用 AOP 可以将事务管理的逻辑从业务代码中分离出来。通过配置事务切面,在方法执行前开启事务,在方法执行成功后提交事务,在方法抛出异常时回滚事务。
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="*" read-only="true"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="serviceMethods" expression="execution(* com.example.service.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethods"/>
</aop:config>
(三)权限校验
在系统中,不同的用户角色可能具有不同的操作权限。利用 AOP 可以在方法调用前进行权限校验,确保只有具有相应权限的用户才能执行特定的方法。例如,在一个电商系统中,只有管理员用户才能执行商品删除操作。
@Component
@Aspect
public class PermissionAspect {
@Before("execution(* com.example.controller.AdminController.deleteProduct(..))")
public void checkPermission(JoinPoint joinPoint) {
// 假设通过SecurityContext获取当前用户角色
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
if (!authorities.contains(new SimpleGrantedAuthority("ADMIN"))) {
throw new AccessDeniedException("没有权限执行该操作");
}
}
}
七、总结
Spring 框架中的 AOP 技术为开发者提供了一种强大而灵活的编程方式,能够有效地将通用功能与业务逻辑分离,提高代码的可维护性、可重用性和开发效率。通过配置文件方式和注解方式,我们可以轻松地实现 AOP 功能,包括定义切入点、通知和切面。在实际项目中,AOP 在日志记录、事务管理、权限校验等多个方面都有着广泛的应用。深入理解和熟练掌握 AOP 技术,将有助于我们构建更加健壮、高效的 Java 企业级应用。希望本文能够帮助读者全面掌握 Spring 框架中 AOP 技术的精髓,并在实际开发中灵活运用,提升项目的质量和开发效率。
相关文章:
深入理解 Spring 框架中的 AOP 技术
一、引言 在 Java 开发领域,Spring 框架凭借其强大的功能和丰富的特性,成为了众多开发者构建企业级应用的首选。其中,面向切面编程(AOP)作为 Spring 框架的核心技术之一,为开发者提供了一种全新的程序结构…...
磁盘清理工具-TreeSize Free介绍
TreeSizeFree是一个磁盘空间管理工具,主要用于分析磁盘使用情况,帮助用户找到占用空间大的文件和文件夹: 特点:按大小排序:快速找到占用空间最大的文件或文件夹 一般可以删除: 扫描 C:\Users\XXX\AppData\Local\Temp 或 C:\Window…...
redis MISCONF Redis is configured to save RDB snapshots报错解决
直接上解决方案 修改redis配置文件 stop-writes-on-bgsave-error no 重启redis...
c#知识点补充2
1.非静态类能否调用静态方法可以 2.对string类型扩展方法,如何进行 类用静态类,参数是this 调用如下 3.out的用法 一定要给a赋值 这种写法不行 这样才行 4.匿名类 5.委托的使用 无论是匿名委托,还是具命委托,委托实例化后一定要…...
循环神经网络(Recurrent Neural Network, RNN)与 Transformer
循环神经网络(RNN)与 Transformer 1. 循环神经网络(RNN)简介 1.1 RNN 结构 循环神经网络(Recurrent Neural Network, RNN)是一种适用于处理序列数据的神经网络。其核心特点是通过隐藏状态(Hi…...
力扣45.跳跃游戏
45. 跳跃游戏 II - 力扣(LeetCode) 代码区: #include<vector> class Solution {public:int jump(vector<int>& nums) {int ans[10005] ;memset(ans,1e4,sizeof(ans));ans[0]0;for(int i0;i<nums.size();i){for(int j1;j…...
招聘面试季--方法论--如何从零到-规划一个新的app产品
规划一个新APP产品的系统化步骤及关键要点: 一、需求验证阶段 明确目标用户与核心需求 通过用户调研(问卷、访谈)定义目标人群的痛点和场景,例如购物类APP需优先满足浏览、支付等核心需求。判断APP的必要性:若功…...
MacOS安装 nextcloud 的 Virtual File System
需求 在Mac上安装next cloud实现类似 OneDrive 那样,文件直接保存在服务器,需要再下载到本地。 方法 在 官网下载Download for desktop,注意要下对版本,千万别下 Mac OS默认的那个。 安装了登录在配置过程中千万不要设置任何同…...
OpenCV Imgproc 模块使用指南(Python 版)
一、模块概述 imgproc 模块是 OpenCV 的图像处理核心,提供从基础滤波到高级特征提取的全流程功能。核心功能包括: 图像滤波:降噪、平滑、锐化几何变换:缩放、旋转、透视校正颜色空间转换:BGR↔灰度 / HSV/Lab 等阈值…...
C/C++蓝桥杯算法真题打卡(Day6)
一、P8615 [蓝桥杯 2014 国 C] 拼接平方数 - 洛谷 方法一:算法代码(字符串分割法) #include<bits/stdc.h> // 包含标准库中的所有头文件,方便编程 using namespace std; // 使用标准命名空间,避免每次调用…...
ORACLE RAC ASM双存储架构下存储部分LUN异常的处理
早上接到用户电话,出现有表空间不足的告警,事实上此环境经常巡检并且有告警系统,一开始就带着有所疑惑的心理,结果同事在扩大表空间时,遇到报错 ORA-15401/ORA-17505,提示ASM空间满了: ALERT日志࿱…...
【设计模式】SOLID 设计原则概述
SOLID 是面向对象设计中的五大原则,不管什么面向对象的语言, 这个准则都很重要,如果你没听说过,赶紧先学一下。它可以提高代码的可维护性、可扩展性和可读性,使代码更加健壮、易于测试和扩展。SOLID 代表以下五个设计原…...
从边缘到核心:群联云防护如何重新定义安全加速边界?
一、安全能力的全方位碾压 1. 协议层深度防护 四层防御: 动态过滤畸形TCP/UDP包(如SYN Flood),传统CDN仅限速率控制。技术示例:基于AI的协议指纹分析,拦截异常连接模式。 七层防御: 精准识别业…...
others-rustdesk远程
title: others-rustdesk远程 categories: Others tags: [others, 远程] date: 2025-03-19 10:19:34 comments: false mathjax: true toc: true others-rustdesk远程, 替代 todesk 的解决方案 前篇 官方 服务器 - https://rustdesk.com/docs/zh-cn/self-host/rustdesk-server-o…...
记录 macOS 上使用 Homebrew 安装的软件
Homebrew 是 macOS 上最受欢迎的软件包管理器之一,能够轻松安装各种命令行工具和 GUI 应用。本文记录了我通过 Homebrew 安装的各种软件,并对它们的用途和基本使用方法进行介绍。 🍺 Homebrew 介绍 Homebrew 是一个开源的包管理器ÿ…...
springmvc中使用interceptor拦截
HandlerInterceptor 是Spring MVC中用于在请求处理之前、之后以及完成之后执行逻辑的接口。它与Servlet的Filter类似,但更加灵活,因为它可以访问Spring的上下文和模型数据。HandlerInterceptor 常用于日志记录、权限验证、性能监控等场景。 ### **1. 创…...
C++基础 [八] - list的使用与模拟实现
目录 list的介绍 List的迭代器失效问题 List中sort的效率测试 list 容器的模拟实现思想 模块分析 作用分析 list_node类设计 list 的迭代器类设计 迭代器类--存在的意义 迭代器类--模拟实现 模板参数 和 成员变量 构造函数 * 运算符的重载 运算符的重载 -- 运…...
使用excel.EasyExcel实现导出有自定义样式模板的excel数据文件,粘贴即用!!!
客户要求导出的excel文件是有好看格式的,当然本文举例模板文件比较简单,内容丰富的模板可以自行设置,话不多说,第一步设置一个"好看"的excel文件模板 上面要注意的地方是{.变量名} ,这里的变量名对应的就是…...
Spring Boot 集成 Elasticsearch怎样在不启动es的情况下正常启动服务
解释 在spingboot 集成es客户端后,每当服务启动时,服务默认都会查看es中是否已经创建了对应的索引,如果没有索引则创建。基于上面的规则我们可以通过配置不自动创建索引来达到在没有es服务的情况下正常启动服务。 解决办法 在entity类的Docu…...
Java面试黄金宝典8
1. 什么是 Spring MVC 定义 Spring MVC 是 Spring 框架里用于构建 Web 应用程序的模块,它严格遵循 MVC(Model - View - Controller)设计模式。这种设计模式把应用程序清晰地划分成三个主要部分: Model(模型࿰…...
JVM常见概念之条件移动
问题 当我们有分支频率数据时,有什么有趣的技巧可以做吗?什么是条件移动? 基础知识 如果您需要在来自一个分支的两个结果之间进行选择,那么您可以在 ISA 级别做两件不同的事情。 首先,你可以创建一个分支ÿ…...
Android AI ChatBot-v1.6.3-28-开心版[免登录使用GPT-4o和DeepSeek]
Android AI ChatBot- 链接:https://pan.xunlei.com/s/VOLi1Ua071S6QZBGixcVL5eeA1?pwdp3tt# 免登录使用GPT-4o和DeepSeek...
集成学习(上):Bagging集成方法
一、什么是集成学习? 在机器学习的世界里,没有哪个模型是完美无缺的。就像古希腊神话中的"盲人摸象",单个模型往往只能捕捉到数据特征的某个侧面。但当我们把多个模型的智慧集合起来,就能像拼图一样还原出完整的真相&a…...
DeepSeek R1 本地部署指南 (3) - 更换本地部署模型 Windows/macOS 通用
0.准备 完成 Windows 或 macOS 安装: DeepSeek R1 本地部署指南 (1) - Windows 本地部署-CSDN博客 DeepSeek R1 本地部署指南 (2) - macOS 本地部署-CSDN博客 以下内容 Windows 和 macOS 命令执行相同: Windows 管理员启动:命令提示符 CMD ma…...
【TI MSPM0】Timer学习
一、计数器 加法计数器:每进入一个脉冲,就加一减法计算器:每进入一个脉冲,就减一 当计数器减到0,触发中断 1.最短计时时间 当时钟周期为1khz时,最短计时时间为1ms,最长计时时间为65535ms 当时…...
Windows部署deepseek R1训练数据后通过AnythingLLM当服务器创建问答页面
如果要了解Windows部署Ollama 、deepseek R1请看我上一篇内容。 这是接上一篇的。 AnythingLLM是一个开源的全栈AI客户端,支持本地部署和API集成。它可以将任何文档或内容转化为上下文,供各种语言模型(LLM)在对话中使用。以下是…...
重删算法中的Bloom滤波器详解与C++实现
一、Bloom滤波器基础概念 Bloom滤波器(Bloom Filter)是一种空间高效的概率型数据结构,用于快速判断某个元素是否存在于集合中。其核心特性: 存在不确定性:可能出现假阳性(False Positive)&…...
信奥赛CSP-J复赛集训(模拟算法专题)(27):P5016 [NOIP 2018 普及组] 龙虎斗
信奥赛CSP-J复赛集训(模拟算法专题)(27):P5016 [NOIP 2018 普及组] 龙虎斗 题目背景 NOIP2018 普及组 T2 题目描述 轩轩和凯凯正在玩一款叫《龙虎斗》的游戏,游戏的棋盘是一条线段,线段上有 n n n 个兵营(自左至右编号 1 ∼ n 1 \sim n 1∼n),相邻编号的兵营之间…...
多模态大模型常见问题
1.视觉编码器和 LLM 连接时,使用 BLIP2中 Q-Former那种复杂的 Adaptor 好还是 LLaVA中简单的 MLP 好,说说各自的优缺点? Q-Former(BLIP2): 优点:Q-Former 通过查询机制有效融合了视觉和语言特征…...
SpringBoot项目实战(初级)
目录 一、数据库搭建 二、代码开发 1.pom.xml 2.thymeleaf模块处理的配置类 3.application配置文件 4.配置(在启动类中) 5.编写数据层 ②编写dao层 ③编写service层 接口 实现类 注意 补充(注入的3个注解) 1.AutoWir…...
