spring AOP 事务 过滤器(Filter)与拦截器(Interceptor)
目录
AOP
概念
核心术语
实现方式
基于代理的 AOP
基于 AspectJ 的 AOP
示例代码
1. 添加依赖
2. 定义业务接口和实现类
3. 定义切面类
4. 配置 Spring Boot 应用
5. 测试代码
应用场景
AOP 的优缺点
动态切换多数据源
spring事务
基本概念
事务传播行为
事务隔离级别
1. ISOLATION_DEFAULT
2. ISOLATION_READ_UNCOMMITTED
3. ISOLATION_READ_COMMITTED
4. ISOLATION_REPEATABLE_READ
5. ISOLATION_SERIALIZABLE
关键区别总结
实现方式
编程式事务管理
声明式事务管理
应用场景
注意事项
过滤器(Filter)与拦截器(Interceptor)
区别
1. 所属规范和框架
2. 作用范围
3. 实现机制
4. 依赖关系
5. 配置方式
使用场景
过滤器(Filter)的使用场景
拦截器(Interceptor)的使用场景
AOP
Spring AOP(Aspect-Oriented Programming,面向切面编程)是 Spring 框架的一个重要特性,它允许开发者在不修改原有代码的情况下,对程序进行增强。下面从概念、核心术语、实现方式、应用场景等方面进行详细介绍。
概念
在传统的面向对象编程(OOP)中,代码的组织是基于类和对象的,主要关注业务逻辑的实现。然而,有些功能(如日志记录、事务管理、权限验证等)会散布在多个类和方法中,导致代码的重复和耦合度增加。AOP 则是一种新的编程范式,它将这些横切关注点(如日志、事务等)从业务逻辑中分离出来,形成独立的模块(切面),从而提高代码的可维护性和可复用性。
核心术语
- 切面(Aspect):切面是一个模块化的关注点,它包含了一组通知和切入点。例如,日志记录可以作为一个切面,其中包含了在方法执行前后记录日志的逻辑。
- 通知(Advice):通知定义了在目标方法执行前后或抛出异常时要执行的代码。常见的通知类型包括:
- 前置通知(Before Advice):在目标方法执行之前执行。
- 后置通知(After Advice):在目标方法执行之后执行,无论目标方法是否抛出异常。
- 返回通知(After Returning Advice):在目标方法正常返回后执行。
- 异常通知(After Throwing Advice):在目标方法抛出异常后执行。
- 环绕通知(Around Advice):环绕目标方法的执行,可以在目标方法执行前后进行额外的处理。
- 切入点(Pointcut):切入点定义了哪些方法会被 AOP 增强。可以使用表达式来指定切入点,例如使用 AspectJ 表达式。例如,
execution(* com.example.service.*.*(..))
表示匹配com.example.service
包下的所有类的所有方法。- 连接点(Join Point):连接点是程序执行过程中的一个点,例如方法调用、异常抛出等。在 Spring AOP 中,连接点通常指的是方法调用。
- 目标对象(Target Object):目标对象是被 AOP 增强的对象。
- 代理对象(Proxy Object):代理对象是由 AOP 框架创建的对象,它包含了目标对象的功能,并在目标方法执行前后添加了额外的逻辑。
- 织入(Weaving):织入是将切面应用到目标对象并创建代理对象的过程。织入可以在编译时、类加载时或运行时进行。
实现方式
Spring AOP 支持两种实现方式:基于代理的 AOP 和基于 AspectJ 的 AOP。
基于代理的 AOP
Spring AOP 默认使用基于代理的 AOP 实现,它又分为 JDK 动态代理和 CGLIB 代理。
- JDK 动态代理:基于接口的代理,目标对象必须实现至少一个接口。Spring AOP 会创建一个实现了目标对象接口的代理对象,通过反射机制调用目标方法。
- CGLIB 代理:基于子类的代理,目标对象不需要实现接口。Spring AOP 会创建一个目标对象的子类作为代理对象,通过继承和方法拦截来实现 AOP 增强。
基于 AspectJ 的 AOP
Spring AOP 也支持使用 AspectJ 作为底层实现。AspectJ 是一个功能强大的 AOP 框架,提供了丰富的切面编程语法和工具。Spring AOP 可以与 AspectJ 集成,使用 AspectJ 的注解和语法来定义切面和通知。
示例代码
以下是一个使用 Spring AOP 和 AspectJ 注解实现日志记录的示例:
1. 添加依赖
在 pom.xml
中添加 Spring AOP 和 AspectJ 的依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2. 定义业务接口和实现类
// 业务接口
public interface UserService {
void addUser(String username);
}
// 业务实现类
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Override
public void addUser(String username) {
System.out.println("Adding user: " + username);
}
}
3. 定义切面类
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LoggingAspect {
// 前置通知
@Before("execution(* com.example.service.UserService.addUser(..))")
public void beforeAddUser(JoinPoint joinPoint) {
System.out.println("Before adding user: " + joinPoint.getArgs()[0]);
}
// 后置通知
@After("execution(* com.example.service.UserService.addUser(..))")
public void afterAddUser(JoinPoint joinPoint) {
System.out.println("After adding user: " + joinPoint.getArgs()[0]);
}
}
4. 配置 Spring Boot 应用
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
5. 测试代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class TestRunner implements CommandLineRunner {
@Autowired
private UserService userService;
@Override
public void run(String... args) throws Exception {
userService.addUser("John");
}
}
应用场景
- 日志记录:在方法执行前后记录日志,方便调试和监控。
- 事务管理:在方法执行前后开启和提交事务,确保数据的一致性。
- 权限验证:在方法执行前进行权限验证,确保用户有权限访问该方法。
- 性能监控:在方法执行前后记录方法的执行时间,用于性能分析。
综上所述,Spring AOP 是一个强大的工具,可以帮助开发者更好地管理横切关注点,提高代码的可维护性和可复用性。
AOP(Aspect-Oriented Programming)
面向切面编程,用于那些与业务无关,但对多个对象产生影响的公共行为和逻辑,抽取公共模块复用,降低耦合度,提高程序可重用性,提高开发效率。
主要功能:记录操作日志、安全控制、事务处理、异常处理等。
AOP实现:对方法前后进行拦截,在方法执行之前开启事务,在执行完目标方法之后根据执行情况提交或者回滚事务
在方法之前和之后实现处理。在运行期会为目标对象生成一个动态代理对象,并在代理对象中实现对目标对象的增强。[用户行为记录、参数的加解密、权限认证、日志记录]
Dynamic /daɪˈnæmɪk/ 动态的
反射机制通过反射我们可以直接操作类或者对象,比如获取某个对象的类定义,获取类声明的属性和方法,调用方法或者构造对象,甚至可以运行时修改类定义。
静态代理:代理类是我们自己定义好的,在程序运行之前就已经编译完成,但是动态代理的代理类是在程序运行时创建的。
SpringAOP思想的实现一般都是基于代理模式 ,在Java中采用JDK动态代理模式,但是JDK动态代理模式只能代理接口而不能代理类。因此SpringAOP会在CGLIB、JDK动态代理之间进行切换。
代理类型
代理类型包括:静态代理、动态代理。
动态代理
AOP框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。
JDK动态代理
要求被代理类必须实现一个接口,核心是InvocationHandler接口和Proxy类。[ˈprɒksi]
核心其实就是代理对象的生成,Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h),通过该方法生成字节码,动态的创建一个代理类。
ClassLoader,用于加载代理类的 Loader 类,
【通常这个 Loader 和被代理的类是同一个Loader 类】。
Interfaces,是要被代理的那些接口。
InvocationHandler,就是用于执行除了被代理接口中方法之外的用户自定义的操作,他 也是用户需要代理的最终目的。
用户调用目标方法都被代理到InvocationHandler 类中定义的唯一方法 invoke 中。
//获取代理类
Class cl = getProxyClass(loader, interfaces);
//获取带有InvocationHandler参数的构造方法
Constructor cons = cl.getConstructor(constructorParams);
//把handler传入构造方法生成实例
return(Object) cons.newInstance(newObject[] { h });
一个典型的动态代理创建对象过程可分为以下四个步骤:
1、通过实现InvocationHandler接口创建调用处理器
IvocationHandler handler = new InvocationHandlerImpl(...);
2、通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类
Class clazz = Proxy.getProxyClass(classLoader,new Class[]{...});
3、通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型
Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});
4、通过构造函数创建代理类实例,此时需将调用处理器对象作为参数被传入
Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));
为了简化对象创建过程,Proxy类中的newProxyInstance方法封装了2~4,
只需两步即可完成代理对象的创建。
Subject proxySubject = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader(),
new Class[]{Subject.class}, new InvocationHandlerImpl (real));
生成的proxySubject继承Proxy类实现Subject接口。实现的Subject的方法实际是调用处理器的invoke方法,而invoke方法利用反射调用的是被代理对象的方法(Object result=method.invoke(proxied,args));
重点Proxy.newProxyInstance,源码分析,会在其他文档中单独总结记录。类Proxy的getProxyClass方法调用ProxyGenerator的 generateProxyClass方法产生ProxySubject.class的二进制数据。
https://zhuanlan.zhihu.com/p/156209283
CGLIB动态代理
CGLIB(Code Generation Library)底层使用了ASM来操作字节码生成新的类,除了CGLIB库外,脚本语言(如Groovy和BeanShell)也使用ASM生成字节码。
CGLIB既能代理接口也能代理类,如果某个类被标记为final,是无法使用CGLIB做动态代理。
相关核心API:
net.sf.cglib.proxy.Enhancer:增强类,用来创建动态代理类。
net.sf.cglib.proxy.MethodProxy:可以方便的调用代理对象的方法。
net.sf.cglib.proxy.MethodInterceptor:方法拦截类,它是Callback接口的子接口需要用户实现。
ASM
是一种通用Java字节码操作和分析框架,它可以用于修改现有的class文件或动态生成class文件
应用场景有AOP(Cglib就是基于ASM)、热部署、修改其他jar包中的类等
何时使用JDK动态代理还是CGLIB?
如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP。
如果目标对象没有实现了接口,必须采用CGLIB库,Spring会自动在JDK动态代理和CGLIB之间转换。
如果目标类没有实现接口,那么SpringAOP会选择使用CGLIB动态代理目标类。
原文链接:Java动态代理原理与应用-CSDN博客
Jdk VS Cglib
Proxy /ˈprɒksi/ 代理
Enhancer /ɪnˈhɑːnsə(r)/ 增强器
静态代理
AspectJ(编译时增强)使用的是静态代理。所谓静态代理指的是,AOP框架会在编译阶段生成AOP代理类,它会在编译阶段将AspectJ植入到Java字节码中,运行的时候就是增强后的AOP对象。
事务实现方式
Spring系列之事务是如何管理的 - 程序员阿牛 - 博客园
- 声明式事务 :实际是通过 AOP 实现(基于@Transactional 的全注解方式使用最多), 在 XML 配置文件中配置或者直接基于注解 。
@Transactional注解中的rollbackFor属性进行配置,默认情况下会对RuntimeException 和Error进行回滚。
使用
切面中各通知方法执行顺序
Around->Before->切入点method->Around->After->afterReturning/afterThrowing
环绕前置----普通前置----环绕返回/异常----环绕后置----普通后置----普通返回/异常
注解版本AOP实现
@Aspect 指定一个类为切面类
@Pointcut("execution(* com.itmayiedu.service.UserService.add(..))") 指定切入点表达式@Before("pointCut_()") 前置通知: 目标方法之前执行
@After("pointCut_()") 后置通知:目标方法之后执行(始终执行)@AfterReturning("pointCut_()") 返回后通知: 执行方法结束前执行(异常不执行)
@AfterThrowing("pointCut_()") 异常通知: 出现异常时候执行
@Around("pointCut_()") 环绕通知: 环绕目标方法执行(可以不声明,若果声明返回值要用Object,否则返回为空,可以改变返回值慎用)
1、开启事物注解权限:
xml:<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
SpringBoot:@EnableAspectJAutoProxy
2.XML方式实现AOP
<!-- 切面类 --><bean id="aop" class="com.itmayiedu.aop2.AopLog2"></bean>
<!-- Aop配置 --><aop:config>
<!-- 定义一个切入点表达式: 拦截哪些方法 -->
<aop:pointcut expression="execution(* com.itmayiedu.service.UserService.*(..))"
id="pt" />
<!-- 切面 -->
<aop:aspect ref="aop">
<!-- 环绕通知 -->
<aop:around method="around" pointcut-ref="pt" />
<!-- 前置通知: 在目标方法调用前执行 -->
<aop:before method="begin" pointcut-ref="pt" />
<!-- 后置通知: -->
<aop:after method="after" pointcut-ref="pt" />
<!-- 返回后通知 -->
<aop:after-returning method="afterReturning"
pointcut-ref="pt" />
<!-- 异常通知 -->
<aop:after-throwing method="afterThrowing"
pointcut-ref="pt" />
</aop:aspect></aop:config>
链接:Spring AOP的底层原理 - 简书
HttpServletRequest常用获取URL的方法
request.getRequestURL() 返回完整的url,包括Http协议,端口号,servlet名字和映射路径,但它不包含请求参数
String getRequestURI():获取请求URI,等于项目名+Servlet路径:/Example/AServle
HttpServletRequest常用获取URL的方法_servletrequest 获取请求url-CSDN博客
AOP 的优缺点
- 优点:提高代码的可维护性和可复用性,降低业务逻辑模块之间的耦合度;便于对横切关注点进行统一管理和维护。
- 缺点:增加了系统的复杂性,调试和理解代码的难度相对较大;性能上会有一定的开销,尤其是在使用 CGLIB 代理时。
编辑
分享
动态切换多数据源
通过 spring AbstractRoutingDataSource 为我们抽象了一个 DynamicDataSource 解决这一问题
1、@Primary这个注解必须要加,因为不加的话spring将分不清楚那个为主数据源(默认数据源)
2、mapper的接口、xml形式以及dao层都需要两个分开
1.在配置文件中配置多个数据源,然后通过配置类来获取数据源以及mapper相关的扫描配置@MapperScan(basePackages = "com.jdkcb.mybatisstuday.mapper.one", sqlSessionFactoryRef = "PrimarySqlSessionFactory")//basePackages:接口文件的包路径
@Primary
主数据源相关配置:主要是指定主数据源、扫描的mapper地址、事务管理器等信息。
副数据源相关配置:主要是指定数据源、扫描的mapper地址、事务管理器等信息。
- mybatis-plus @DS实现动态切换数据源原理
@DS(“slave”)
【实战干货】Springboot实现多数据源整合的两种方式-腾讯云开发者社区-腾讯云
spring事务
Spring 事务管理是 Spring 框架中一个非常重要的特性,它为企业级应用提供了强大而灵活的事务处理能力。以下将从基本概念、事务传播行为、隔离级别、实现方式和应用场景几个方面详细介绍。
基本概念
事务是一组不可分割的操作序列,这些操作要么全部成功执行,要么全部失败回滚,以保证数据的一致性和完整性。在数据库操作中,事务尤为重要,例如在银行转账业务中,从一个账户扣款和向另一个账户存款这两个操作必须作为一个事务来处理,否则可能会出现数据不一致的问题。
事务传播行为
事务传播行为定义了在多个事务方法相互调用时,事务如何进行传播。Spring 定义了 7 种事务传播行为:
- PROPAGATION_REQUIRED(默认):如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。例如,方法 A 调用方法 B,若方法 A 开启了事务,方法 B 会加入到方法 A 的事务中;若方法 A 没有开启事务,方法 B 会创建一个新的事务。
- PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式执行。
- PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
- PROPAGATION_REQUIRES_NEW:无论当前是否存在事务,都会创建一个新的事务。如果当前存在事务,则将当前事务挂起。
- PROPAGATION_NOT_SUPPORTED:以非事务的方式执行操作。如果当前存在事务,则将当前事务挂起。
- PROPAGATION_NEVER:以非事务的方式执行操作。如果当前存在事务,则抛出异常。
- PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则创建一个新的事务。嵌套事务是外部事务的一部分,它有自己的保存点,可以独立回滚。
事务隔离级别
事务隔离级别用于定义事务之间的隔离程度,主要是为了解决并发事务可能出现的问题,如脏读、不可重复读和幻读。Spring 支持以下 4 种事务隔离级别:
- ISOLATION_DEFAULT:使用数据库默认的隔离级别。不同的数据库默认隔离级别可能不同,例如 MySQL 的默认隔离级别是
REPEATABLE_READ
,Oracle 的默认隔离级别是READ_COMMITTED
。- ISOLATION_READ_UNCOMMITTED:最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、不可重复读和幻读。
- ISOLATION_READ_COMMITTED:允许读取已经提交的数据变更,避免了脏读,但可能会出现不可重复读和幻读。
- ISOLATION_REPEATABLE_READ:确保在同一个事务中多次读取同一数据的结果是一致的,避免了脏读和不可重复读,但可能会出现幻读。
- ISOLATION_SERIALIZABLE:最高的隔离级别,强制事务串行执行,避免了脏读、不可重复读和幻读,但会降低并发性能。
Spring 支持的事务隔离级别及其特点如下:
1. ISOLATION_DEFAULT
- 描述:使用数据库默认的隔离级别(不同数据库默认值不同)。
- 示例:MySQL 默认
REPEATABLE_READ
,Oracle 默认READ_COMMITTED
。
2. ISOLATION_READ_UNCOMMITTED
- 描述:允许读取未提交的数据变更(脏读)。
- 问题:可能导致脏读、不可重复读、幻读。
- 适用场景:极少数对一致性要求极低的场景。
3. ISOLATION_READ_COMMITTED
- 描述:仅允许读取已提交的数据变更。
- 解决问题:避免脏读。
- 问题:可能存在不可重复读、幻读。
- 适用场景:大多数数据库默认级别,适用于中等一致性要求的场景。
4. ISOLATION_REPEATABLE_READ
- 描述:保证同一事务内多次读取结果一致。
- 解决问题:避免脏读、不可重复读。
- 问题:可能存在幻读。
- 适用场景:需要较高一致性的场景(如银行转账)。
5. ISOLATION_SERIALIZABLE
- 描述:强制事务串行执行,完全隔离。
- 解决问题:避免所有并发问题(脏读、不可重复读、幻读)。
- 缺点:性能最低。
- 适用场景:对一致性要求极高且并发较低的场景。
关键区别总结
隔离级别 | 脏读 | 不可重复读 | 幻读 | 性能 |
---|---|---|---|---|
READ_UNCOMMITTED | 允许 | 允许 | 允许 | 高 |
READ_COMMITTED | 禁止 | 允许 | 允许 | 中高 |
REPEATABLE_READ | 禁止 | 禁止 | 允许 | 中 |
SERIALIZABLE | 禁止 | 禁止 | 禁止 | 低 |
注意:实际使用时需结合业务需求和数据库特性选择隔离级别,避免过度牺牲性能或一致性。
实现方式
Spring 提供了两种事务管理的实现方式:编程式事务管理和声明式事务管理。
编程式事务管理
编程式事务管理需要在代码中显式地编写事务管理的代码,例如使用 TransactionTemplate
或 PlatformTransactionManager
。这种方式灵活性高,但代码复杂度也较高,适用于一些复杂的事务场景。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
@Service
public class UserService {
@Autowired
private TransactionTemplate transactionTemplate;
public void transferMoney() {
transactionTemplate.execute(new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus status) {
try {
// 执行数据库操作
// ...
return null;
} catch (Exception e) {
status.setRollbackOnly();
throw e;
}
}
});
}
}
声明式事务管理
声明式事务管理是通过 AOP 实现的,它允许开发者使用注解或 XML 配置来定义事务,而不需要在代码中显式地编写事务管理的代码。这种方式简单易用,是 Spring 中推荐的事务管理方式。
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserService {
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED)
public void transferMoney() {
// 执行数据库操作
// ...
}
}
应用场景
Spring 事务管理在很多场景下都有广泛的应用,例如:
- 数据库操作:在进行数据库的增删改查操作时,使用事务可以保证数据的一致性和完整性。
- 业务逻辑处理:在处理复杂的业务逻辑时,可能涉及多个数据库操作,使用事务可以确保这些操作要么全部成功,要么全部失败。
- 分布式系统:在分布式系统中,可能涉及多个服务的调用和多个数据库的操作,使用分布式事务管理可以保证数据的一致性。
注意事项
- 在使用声明式事务管理时,要注意事务方法的调用方式。如果事务方法是在同一个类的非事务方法中调用,事务可能不会生效,因为 Spring 的 AOP 是基于代理实现的。
- 要合理选择事务传播行为和隔离级别,根据业务需求和数据库的特性来进行配置,避免出现性能问题或数据不一致的问题。
过滤器(Filter)与拦截器(Interceptor)
在 Java Web 开发中,过滤器(Filter)和拦截器(Interceptor)都可用于对请求进行预处理和后处理,但它们存在诸多区别,且有不同的使用场景,下面为你详细介绍。
区别
1. 所属规范和框架
- 过滤器(Filter):属于 Servlet 规范的一部分,是 Servlet 容器提供的功能。它由
javax.servlet.Filter
接口定义,在 Servlet 容器(如 Tomcat、Jetty 等)中运行。- 拦截器(Interceptor):通常是 Spring 框架提供的功能,是 Spring MVC 框架的一部分。它由
org.springframework.web.servlet.HandlerInterceptor
接口定义,在 Spring MVC 框架中使用。2. 作用范围
- 过滤器(Filter):可以对所有请求进行过滤,包括对静态资源(如图片、CSS、JavaScript 文件等)的请求。它在 Servlet 容器接收请求后,在请求到达 Servlet 之前进行过滤处理。
- 拦截器(Interceptor):主要用于拦截 Spring MVC 框架中的控制器请求,只能对控制器方法进行拦截,无法拦截静态资源请求。它在 Spring MVC 框架处理请求的过程中,在控制器方法执行前后进行拦截处理。
3. 实现机制
- 过滤器(Filter):基于函数回调机制实现。当请求进入 Servlet 容器时,容器会调用过滤器的
doFilter()
方法,在该方法中可以对请求和响应进行处理,然后决定是否将请求继续传递给下一个过滤器或 Servlet。- 拦截器(Interceptor):基于 AOP(面向切面编程)思想实现。它通过实现
HandlerInterceptor
接口,重写其中的preHandle()
、postHandle()
和afterCompletion()
方法,在控制器方法执行前后和请求处理完成后进行拦截处理。4. 依赖关系
- 过滤器(Filter):不依赖于 Spring 框架,可以在非 Spring 项目中使用。它只需要 Servlet 容器的支持。
- 拦截器(Interceptor):依赖于 Spring 框架,只能在 Spring MVC 项目中使用。它可以使用 Spring 框架的依赖注入等特性。
5. 配置方式
- 过滤器(Filter):可以通过
web.xml
文件进行配置,也可以使用注解(如@WebFilter
)进行配置。
<filter>
<filter-name>myFilter</filter-name>
<filter-class>com.example.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>myFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter(urlPatterns = "/*")
public class MyFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 预处理逻辑
chain.doFilter(request, response);
// 后处理逻辑
}
}
- 拦截器(Interceptor):需要在 Spring MVC 配置类中进行配置,通过实现
WebMvcConfigurer
接口,重写addInterceptors()
方法来注册拦截器。
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/login");
}
}
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 预处理逻辑
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// 后处理逻辑
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 请求处理完成后的逻辑
}
}
使用场景
过滤器(Filter)的使用场景
- 字符编码处理:对所有请求和响应进行字符编码设置,确保中文等特殊字符的正确显示。
- 权限验证:对所有请求进行权限验证,例如检查用户是否登录,防止未登录用户访问敏感资源。
- 日志记录:记录所有请求的信息,如请求的 URL、请求参数、请求时间等,方便后续的调试和分析。
- 静态资源压缩:对静态资源(如 CSS、JavaScript 文件)进行压缩处理,减少传输数据量,提高网站性能。
拦截器(Interceptor)的使用场景
- 控制器方法权限验证:对特定的控制器方法进行权限验证,例如只有管理员用户才能访问某些管理页面。
- 请求参数验证:在控制器方法执行前,对请求参数进行验证,确保参数的合法性。
- 性能监控:记录控制器方法的执行时间,统计系统的性能指标。
- 数据预处理:在控制器方法执行前,对请求数据进行预处理,例如将日期字符串转换为日期对象。
综上所述,过滤器和拦截器在不同的场景下发挥着各自的作用,开发者可以根据具体需求选择合适的工具来处理请求。