解决spring mvc JSON 无限死循环

本文探讨了在使用Jackson库进行序列化时遇到的死循环问题,具体为相册和照片实体类之间的双向关联导致的JSON序列化失败。通过在相册类上添加@JsonIgnoreProperties注解来排除'photos'属性,成功解决了序列化问题。
2011-12-22 11:15:58 org.apache.catalina.core.StandardWrapperValve invoke
严重: Servlet.service() for servlet spring threw exception
org.codehaus.jackson.map.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: com.blog.pojo.Album["photos"]->org.hibernate.collection.PersistentBag[0]->com.blog.pojo.Photo["album"]->com.blog.pojo.Album["photos"]->org.hibernate.collection.PersistentBag[0]->com.blog.pojo.Photo["album"]->com.blog.pojo.Album["photos"]->org.hibernate.collection.PersistentBag[0]->com.blog.pojo.Photo["album"]->com.blog.pojo.Album["photos"]->org.hibernate.collection.PersistentBag[0]->com.blog.pojo.Photo["album"]->com.blog.pojo.Album["photos"]->org.hibernate.collection.PersistentBag[0]->com.blog.pojo.Photo["album"]->com.blog.pojo.Album["photos"]->org.hibernate.collection.PersistentBag[0]->com.blog.pojo.Photo["album"]->com.blog.pojo.Album["photos"]->org.hibernate.collection.PersistentBag[0]->com.blog.pojo.Photo["album"]->com.blog.pojo.Album["photos"]->org.hibernate.collection.PersistentBag[0]->com.blog.pojo.Photo["album"]->com.blog.pojo.Album["photos"]->org.hibernate.collection.PersistentBag[0]->com.blog.pojo.Photo["album"]->com.blog.pojo.Album["photos"]->org.hibernate.collection.PersistentBag[0]->com.blog.pojo.Photo["album"]->com.blog.pojo.Album["photos"]->org.hibernate.collection.PersistentBag[0]->com.blog.pojo.Photo["album"]->com.blog.pojo.Album["photos"]->org.hibernate.collection.PersistentBag[0]->com.blog.pojo.Photo["album"]->com.blog.pojo.Album["photos"]->org.hibernate.collection.PersistentBag[0]->com.blog.pojo.Photo["album"]->com.blog.pojo.Album["photos"]->org.hibernate.collection.PersistentBag[0]->com.blog.pojo.Photo["album"]->com.blog.pojo.Album["photos"])
	at org.codehaus.jackson.map.ser.BeanSerializer.serializeFields(BeanSerializer.java:189)
	at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:142)
	at org.codehaus.jackson.map.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:428)
	at org.codehaus.jackson.map.ser.BeanSerializer.serializeFields(BeanSerializer.java:175)
	at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:142)
	at org.codehaus.jackson.map.ser.ContainerSerializers$CollectionSerializer.serializeContents(ContainerSerializers.java:442)
	at org.codehaus.jackson.map.ser.ContainerSerializers$CollectionSerializer.serializeContents(ContainerSerializers.java:383)
	at org.codehaus.jackson.map.ser.ContainerSerializers$AsArraySerializer.serialize(ContainerSerializers.java:142)
	at org.codehaus.jackson.map.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:428)
	at org.codehaus.jackson.map.ser.BeanSerializer.serializeFields(BeanSerializer.java:175)
	at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:142)
	at org.codehaus.jackson.map.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:428)
	at org.codehaus.jackson.map.ser.BeanSerializer.serializeFields(BeanSerializer.java:175)
	at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:142)
	at org.codehaus.jackson.map.ser.ContainerSerializers$CollectionSerializer.serializeContents(ContainerSerializers.java:442)
	at org.codehaus.jackson.map.ser.ContainerSerializers$CollectionSerializer.serializeContents(ContainerSerializers.java:383)
	at org.codehaus.jackson.map.ser.ContainerSerializers$AsArraySerializer.serialize(ContainerSerializers.java:142)
	at org.codehaus.jackson.map.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:428)
	at org.codehaus.jackson.map.ser.BeanSerializer.serializeFields(BeanSerializer.java:175)
	at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:142)
	at org.codehaus.jackson.map.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:428)
	at org.codehaus.jackson.map.ser.BeanSerializer.serializeFields(BeanSerializer.java:175)
	at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:142)
	at org.codehaus.jackson.map.ser.ContainerSerializers$CollectionSerializer.serializeContents(ContainerSerializers.java:442)
	at org.codehaus.jackson.map.ser.ContainerSerializers$CollectionSerializer.serializeContents(ContainerSerializers.java:383)
	at org.codehaus.jackson.map.ser.ContainerSerializers$AsArraySerializer.serialize(ContainerSerializers.java:142)
	at org.codehaus.jackson.map.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:428)
	at org.codehaus.jackson.map.ser.BeanSerializer.serializeFields(BeanSerializer.java:175)
	at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:142)
	at org.codehaus.jackson.map.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:428)
	at org.codehaus.jackson.map.ser.BeanSerializer.serializeFields(BeanSerializer.java:175)
	at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:142)
	at org.codehaus.jackson.map.ser.ContainerSerializers$CollectionSerializer.serializeContents(ContainerSerializers.java:442)
	at org.codehaus.jackson.map.ser.ContainerSerializers$CollectionSerializer.serializeContents(ContainerSerializers.java:383)
	at org.codehaus.jackson.map.ser.ContainerSerializers$AsArraySerializer.serialize(ContainerSerializers.java:142)
	at org.codehaus.jackson.map.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:428)
	at org.codehaus.jackson.map.ser.BeanSerializer.serializeFields(BeanSerializer.java:175)
	at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:142)
	at org.codehaus.jackson.map.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:428)
	at org.codehaus.jackson.map.ser.BeanSerializer.serializeFields(BeanSerializer.java:175)
	at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:142)
	at org.codehaus.jackson.map.ser.ContainerSerializers$CollectionSerializer.serializeContents(ContainerSerializers.java:442)
	at org.codehaus.jackson.map.ser.ContainerSerializers$CollectionSerializer.serializeContents(ContainerSerializers.java:383)
	at org.codehaus.jackson.map.ser.ContainerSerializers$AsArraySerializer.serialize(ContainerSerializers.java:142)
	at org.codehaus.jackson.map.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:428)
	at org.codehaus.jackson.map.ser.BeanSerializer.serializeFields(BeanSerializer.java:175)
	at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:142)
	at org.codehaus.jackson.map.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:428)
	at org.codehaus.jackson.map.ser.BeanSerializer.serializeFields(BeanSerializer.java:175)
	at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:142)
	at org.codehaus.jackson.map.ser.ContainerSerializers$CollectionSerializer.serializeContents(ContainerSerializers.java:442)
	at org.codehaus.jackson.map.ser.ContainerSerializers$CollectionSerializer.serializeContents(ContainerSerializers.java:383)
	at org.codehaus.jackson.map.ser.ContainerSerializers$AsArraySerializer.serialize(ContainerSerializers.java:142)
	at org.codehaus.jackson.map.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:428)
	at org.codehaus.jackson.map.ser.BeanSerializer.serializeFields(BeanSerializer.java:175)
	at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:142)
	at org.codehaus.jackson.map.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:428)
	at org.codehaus.jackson.map.ser.BeanSerializer.serializeFields(BeanSerializer.java:175)
	at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:142)
	at org.codehaus.jackson.map.ser.ContainerSerializers$CollectionSerializer.serializeContents(ContainerSerializers.java:442)
	at org.codehaus.jackson.map.ser.ContainerSerializers$CollectionSerializer.serializeContents(ContainerSerializers.java:383)
	at org.codehaus.jackson.map.ser.ContainerSerializers$AsArraySerializer.serialize(ContainerSerializers.java:142)
	at org.codehaus.jackson.map.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:428)
	at org.codehaus.jackson.map.ser.BeanSerializer.serializeFields(BeanSerializer.java:175)
	at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:142)
	at org.codehaus.jackson.map.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:428)
	at org.codehaus.jackson.map.ser.BeanSerializer.serializeFields(BeanSerializer.java:175)
	at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:142)


这里两个实体类: Album(相册) 和 photo(照片)   是一对多双向关联

死循环原因: JSON无法对Album的photos属性进去JSON转换

解决办法:

     在Album实体类上加注释@JsonIgnoreProperties(value={"photos"})

 

@Entity
@SuppressWarnings("serial")
@JsonIgnoreProperties(value={"photos"})   
public class Album implements java.io.Serializable {


 

结束!

 

<think>首先,用户的问题是关于JWT拦截器在Spring容器中如何实现拦截功能及其执行路径。具体来说,他们想知道JWT拦截器注册到Spring容器后如何实现请求拦截,以及执行流程和路径。 我需要确保我的回答结构清晰,帮助用户逐步解决问题,并保证真实可靠。回答必须使用中文。 关键点: - JWT拦截器通常是基于Spring框架的拦截器(Interceptor)实现的。 - 在Spring Boot或Spring MVC中,拦截器用于在请求处理前后执行自定义逻辑。 - JWT(JSON Web Token)用于认证和授权,所以拦截器会检查请求中的JWT token。 步骤: 1. **注册拦截器**:在Spring中,通过实现WebMvcConfigurer接口并重写addInterceptors方法,将自定义拦截器注册到容器中。 2. **拦截器实现**:自定义拦截器需要实现HandlerInterceptor接口,并重写preHandle、postHandle等方法。对于JWT,通常在preHandle中验证token。 3. **执行流程**:当请求到达时,拦截器链会依次执行每个拦截器的preHandle方法。如果验证失败,可以中断请求;如果成功,继续到控制器。 4. **路径配置**:在注册拦截器时,可以指定拦截的路径模式,比如"/api/**"。 详细执行路径: - 请求到达DispatcherServlet。 - DispatcherServlet调用HandlerMapping找到处理程序。 - 然后,执行拦截器链(Interceptor chain)。 - 在拦截器的preHandle方法中,验证JWT token: - 如果token无效,返回错误响应(如401 Unauthorized)。 - 如果有效,继续处理。 - 之后,请求到达控制器方法。 - 控制器处理完,返回响应时,会执行postHandle和afterCompletion方法。 在回答中,我需要引用相关概念,但用户没有提供具体的引用,所以我将基于一般知识。如果有必要,我可以添加通用引用标识如[^1],但用户示例中没有实际引用,所以我可以省略或使用占位符。 生成相关问题:根据系统指令,我需要在回答末尾生成相关问题。这应该基于当前主题。 回答结构: 1. 介绍JWT拦截器的作用。 2. 解释如何注册拦截器。 3. 描述拦截器的实现细节。 4. 详细说明执行流程和路径。 5. 提供代码示例(可选,但有助于理解)。 6. 结尾生成相关问题。 使用LaTeX:对于数学表达式,但在这个上下文中,可能没有太多数学内容。如果有变量或概念,确保使用$...$格式。例如,提到路径模式时,可以用$...$包裹。 例如: - 路径模式如$/api/**$。 代码示例:用户示例中包含了Python代码,所以我应该提供一个Java代码示例,展示拦截器的实现。 最终,回答必须用中文。</think>### JWT拦截器在Spring容器中的实现与执行路径 在Spring框架中,JWT拦截器用于拦截HTTP请求,验证JSON Web Token(JWT)的合法性,确保只有授权用户才能访问受保护资源。其核心是通过Spring MVC的拦截器机制实现的。下面我将逐步解释JWT拦截器的注册过程、实现细节和执行流程,确保内容真实可靠(基于Spring Boot 3.x+版本)。 --- ### 1. **JWT拦截器的注册到Spring容器** JWT拦截器需要注册到Spring容器中,才能生效。这通常通过实现`WebMvcConfigurer`接口完成,在配置类中重写`addInterceptors`方法。注册时,可以指定拦截的路径模式(如只拦截API请求),并排除不需要拦截的路径(如登录接口)。 - **示例代码(Java)**: ```java 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(jwtInterceptor()) .addPathPatterns("/api/**") // 拦截所有以/api/开头的路径 .excludePathPatterns("/api/login"); // 排除登录接口,避免死循环 } @Bean public JwtInterceptor jwtInterceptor() { return new JwtInterceptor(); // 创建自定义拦截器实例 } } ``` - **关键点**: - `addInterceptor` 方法将自定义的`JwtInterceptor`实例添加到拦截器链。 - `addPathPatterns` 定义拦截路径,支持Ant风格模式(如$/api/**$)。 - `excludePathPatterns` 用于排除公共路径(如登录或健康检查)。 - 这个配置类在Spring启动时自动加载,将拦截器注入容器[^1]。 --- ### 2. **JWT拦截器的实现细节** JWT拦截器是一个自定义类,需实现`HandlerInterceptor`接口。核心逻辑在`preHandle`方法中,用于验证JWT token。如果验证失败,直接中断请求;验证成功,则允许请求继续。 - **示例代码(Java)**: ```java import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; @Component public class JwtInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 从请求头获取JWT token(通常存储在Authorization头) String token = request.getHeader("Authorization"); if (token == null || !token.startsWith("Bearer ")) { response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "缺失或无效的Token"); return false; // 中断请求 } // 提取并验证Token(例如使用JWT库如jjwt) String jwt = token.substring(7); if (!JwtUtil.validateToken(jwt)) { // 假设JwtUtil是自定义工具类 response.sendError(HttpServletResponse.SC_FORBIDDEN, "Token验证失败"); return false; } // 验证通过,将用户信息存储到请求属性(供后续控制器使用) request.setAttribute("user", JwtUtil.extractUser(jwt)); return true; // 继续执行请求 } } ``` - **关键组件**: - **`preHandle`方法**:在请求处理前执行,返回`true`表示继续,`false`表示中断。 - **Token验证**:使用库(如`io.jsonwebtoken`)解析和验证JWT签名、过期时间等。 - **错误处理**:直接设置HTTP响应状态码(如401或403),避免进入控制器。 - **数据传递**:验证后,将用户信息存储在请求属性中,便于控制器使用[^1]。 --- ### 3. **执行流程和路径** 当请求到达Spring应用时,JWT拦截器作为拦截器链的一部分被触发。执行路径严格遵循Spring MVC的生命周期,具体如下: 1. **请求到达DispatcherServlet**: - 用户发送HTTP请求(如`GET /api/user`)。 - DispatcherServlet(前端控制器)接收请求,并查找匹配的HandlerMapping。 2. **拦截器链执行**: - DispatcherServlet调用注册的拦截器链。 - JWT拦截器的`preHandle`方法被优先执行: - 如果Token验证失败:直接返回错误响应(如401),流程终止,不会进入控制器。 - 如果验证成功:继续执行下一个拦截器或控制器。 - 路径匹配基于注册时的模式(例如,只拦截$/api/**$)。 3. **控制器处理**: - 所有拦截器`preHandle`通过后,请求路由到目标控制器方法。 - 控制器执行业务逻辑(如查询数据库)。 4. **响应阶段**: - 控制器返回响应后,执行拦截器的`postHandle`方法(可选,用于日志记录等)。 - 最后,执行`afterCompletion`方法(清理资源)。 **执行路径示意图**: ``` HTTP Request → DispatcherServlet → HandlerMapping → Interceptor Chain (JWT拦截器 preHandle) → 验证失败: 返回错误响应 (401/403) → 验证成功: 继续 → Controller 方法 → Interceptor postHandle (可选) → 返回HTTP响应 ``` - **关键特性**: - **顺序依赖**:拦截器按注册顺序执行;如果多个拦截器存在,JWT验证通常放在最前。 - **性能影响**:`preHandle`验证在控制器前完成,避免无效请求占用资源。 - **路径灵活性**:通过Ant模式(如$/secure/**$)动态控制拦截范围[^1]。 --- ### 总结 JWT拦截器通过Spring的拦截器机制实现高效请求拦截:注册到容器后,它在请求生命周期的早期验证Token,确保安全性和性能。核心是`preHandle`方法中的验证逻辑,执行路径清晰可控。实际应用中,建议结合Spring Security进行更细粒度的授权管理。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值