@ResponseBody不生效,你序列化了?序列化的几种方式

本文探讨了在使用@ResponseBody注解开发时遇到的问题,重点介绍了如何通过Java原生、Hessian及JSON序列化解决返回对象未被正确序列化的问题,适合前端与后端开发者参考。

问题

使用注解开发时,@ResponseBody不生效:即使用@ResponseBody注解后,并不返回JSON数据

原因

可能有:

  • 所返回的对象并没有被序列化,而这一步不可或缺

解决

序列化的方式:

  • Java原生:给返回的类实现Serializable接口。只需加上implements Serializable即可。
public class RestResponse implements Serializable {
	//....
}
  • Hessian序列化:请自行查阅资料。
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException; 
import com.caucho.hessian.io.HessianInput;
import com.caucho.hessian.io.HessianOutput;
 
public class Hessian {
	
	public static void main(String[] args) throws IOException {
		
		RestResponse resp = new RestResponse(); //无需implements Serializable
		
		ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
		
		//Hessian的序列化输出
		HessianOutput hessianOutput = new HessianOutput(outputStream);
		
		hessianOutput.writeObject(resp);
		
		byte[] outputStreamData = outputStream.toByteArray();
		
		ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStreamData);
		
		//Hessian的反序列化输入
		HessianInput hessianInput = new HessianInput(inputStream);
		RestResponse resp2 = (RestResponse)hessianInput.readObject();
	}
 
}
  • JSON序列化:如Fastjson、Jackjson等。请自行查阅资料。
  • 其他:略。
如果你是一名专业的java高级架构师,现在你在面试知识,如下是Spring和SpringMVC的面试知识体系结构图 请按照这个体系给出每个知识点的学习理解方便记忆和消化,同时给出每个知识点的高频面试的标准面试答案,结合项目经验给出解释和实战 Spring和SpringMVC面试核心知识体系 ├── 一、Spring框架基础 │ ├── 核心概念 │ │ ├── Spring是什么?有哪些核心优势?(解耦、集成、AOP等) │ │ ├── IoC(控制反转)和DI(依赖注入):概念、区别与好处? │ │ └:Spring容器:BeanFactory vs ApplicationContext区别? │ ├── 配置方式 │ │ ├── 有哪几种配置方式?(XML、注解、Java Config) │ │ ├── @Configuration + @Bean 与 @Component 的区别? │ │ └── 如何混合使用不同的配置方式? │ └── Bean管理 │ ├── Bean的作用域:singleton, prototype, request等区别? │ ├── Bean的生命周期:从创建到销毁的完整流程? │ └── 循环依赖问题:什么是循环依赖?Spring如何解决(三级缓存)? ├── 二、IoC(控制反转)与DI(依赖注入)深度解析 │ ├── 依赖注入方式 │ │ ├── 构造器注入 vs Setter注入:推荐哪种?为什么? │ │ ├── 字段注入(@Autowired)的优缺点? │ │ └── @Autowired, @Resource, @Inject 注解的区别? │ ├── 自动装配 │ │ ├── 自动装配的模式(byName, byType等)? │ │ ├── @Primary 和 @Qualifier 注解的作用与区别? │ │ └:如何解决自动装配时的歧义性? │ └── 条件化装配 │ ├── @Profile:作用?如何激活不同环境的配置? │ └── @Conditional:原理?如何自定义条件? ├── 三、AOP(面向切面编程) │ ├── AOP核心概念 │ │ ├── AOP解决了什么问题?主要应用场景?(日志、事务、安全等) │ │ ├── 连接点(Joinpoint)、切点(Pointcut)、通知(Advice)、切面(Aspect)概念? │ │ └── 织入(Weaving):编译期、类加载期、运行期织入区别? │ ├── 代理机制 │ │ ├── Spring AOP默认使用哪种代理?JDK动态代理 vs CGLIB代理区别? │ │ └── 什么情况下使用CGLIB代理? │ └── 通知类型与使用 │ ├── 5种通知类型:@Before, @After, @AfterReturning, @AfterThrowing, @Around │ ├── @Around 和其他通知的区别?如何控制目标方法执行? │ └── 如何获取方法参数、方法名等信息(JoinPoint, ProceedingJoinPoint)? ├── 四、Spring事务管理 │ ├── 事务核心接口 │ │ ├── PlatformTransactionManager:作用?常见实现类(DataSourceTransactionManager等) │ │ ├── TransactionDefinition:定义事务属性(传播行为、隔离级别等) │ │ └── TransactionStatus:描述事务状态 │ ├── 声明式事务 │ │ ├── 如何开启声明式事务?(@EnableTransactionManagement) │ │ ├── @Transactional 注解可以作用在哪些地方?(类、方法)优先级? │ │ └── 事务失效的常见场景?(非public方法、自调用、异常被捕获等) │ └── 事务属性 │ ├── 传播行为(Propagation):REQUIRED, REQUIRES_NEW, NESTED等区别? │ ├── 隔离级别(Isolation):READ_UNCOMMITTED, READ_COMMITTED等与数据库关系? │ └── 回滚规则:如何设置回滚/不回滚的异常类型? ├── 五、Spring MVC核心架构 │ ├── 请求处理流程 │ │ ├── 描述一次请求的完整处理流程(DispatcherServlet -> 控制器 -> 视图)? │ │ ├── 核心组件:DispatcherServlet, HandlerMapping, HandlerAdapter, ViewResolver作用? │ │ └── 流程图的关键步骤是什么? │ ├── 控制器(Controller) │ │ ├── @Controller 和 @RestController 的区别? │ │ ├── 常用注解:@RequestMapping, @GetMapping, @PostMapping等 │ │ └── @ResponseBody 的作用?如何将返回值转为JSON(HttpMessageConverter)? │ └── 数据处理与视图解析 │ ├── 参数绑定:@RequestParam, @PathVariable, @RequestBody, @ModelAttribute区别? │ ├── 数据模型:Model, ModelMap, ModelAndView 的使用? │ └── 视图解析:InternalResourceViewResolver如何工作? ├── 六、Spring MVC高级特性 │ ├── 拦截器(Interceptor) │ │ ├── 拦截器 vs 过滤器(Filter)的区别? │ │ ├── 拦截器的三个方法:preHandle, postHandle, afterCompletion执行时机? │ │ └── 如何配置多个拦截器?执行顺序? │ ├── 统一异常处理 │ │ ├── @ControllerAdvice + @ExceptionHandler 如何实现全局异常处理? │ │ ├── HandlerExceptionResolver 接口的作用? │ │ └── @ResponseStatus 注解的使用? │ └── 文件上传与Restful API │ ├── 如何实现文件上传?(MultipartResolver) │ ├── Restful风格API的设计原则?(GET/POST/PUT/DELETE) │ └── 如何解决跨域问题?(@CrossOrigin, CorsFilter) ├── 七、Spring与第三方框架集成 │ ├── 数据访问 │ │ ├── Spring JDBC:JdbcTemplate的优势? │ │ ├── 集成MyBatis:需要哪些配置?@MapperScan作用? │ │ └── 集成JPA(Hibernate):如何配置? │ ├── 测试框架 │ │ ├── Spring TestContext Framework:@SpringBootTest, @DataJpaTest等 │ │ ├── 如何模拟MVC测试?(@WebMvcTest, MockMvc) │ │ └── 如何 mock Bean?(@MockBean) │ └── 其他集成 │ ├── 如何集成缓存?(@Cacheable, 支持Redis, Ehcache等) │ └── 如何集成任务调度?(@Scheduled, @Async) ├── 八、Spring新特性与原理进阶 │ ├── Spring Boot关联 │ │ ├── Spring Boot 和 Spring 的关系?自动配置原理? │ │ └── starter 的作用?如何自定义 starter? │ ├── 设计模式应用 │ │ ├── Spring中使用了哪些设计模式?(工厂、单例、代理、模板方法等) │ │ └── 举例说明模板方法模式在JdbcTemplate中的应用? │ └── 源码与原理 │ ├── Spring如何管理单例Bean的线程安全问题? │ ├── ApplicationContext的刷新流程(refresh()方法)大致过程? │ └── Spring事件机制(ApplicationEvent、ApplicationListener)? └── 九、常见问题与解决方案 ├── 性能与配置 │ ├── 如何优化Spring应用启动速度?(懒加载、组件扫描路径优化) │ └── 如何外部化配置?(@PropertySource, @Value, @ConfigurationProperties) ├── 开发实践 │ ├── 如何优雅地读取配置文件? │ ├── Spring中有哪些扩展点?(BeanPostProcessor, BeanFactoryPostProcessor) │ └── 如何自定义注解并使其生效? └── 疑难杂症 ├── @Transactional 和 @Async 在同一个类中调用为什么失效?(代理机制) └── 如何解决Spring应用中的内存泄漏问题?
最新发布
09-29
<think> 我们之前讨论的是若依plus框架中解决long类型在前端精度丢失的问题,用户现在提到使用hutool的tree进行序列化后仍然出现精度丢失。Hutool是一个Java工具库,它提供了TreeUtil等工具来构建树形结构。但是,当树节点ID为long类型且值超过JavaScript的安全整数范围时,同样会出现精度丢失问题。 解决思路与之前类似:需要将long类型序列化为字符串。Hutool的TreeUtil构建的树在序列化JSON时,默认使用的是项目配置的JSON序列化器(如Jackson)。因此,我们可以通过配置Jackson的全局序列化方式,将long类型转为字符串。 然而,用户提到“使用hutool的tree进行序列化后仍出现精度丢失”,说明可能已经尝试过一些序列化配置但未生效。以下提供两种解决方案: 解决方案1:在实体类ID字段上添加Jackson注解 即使使用Hutool的TreeUtil,最终返回给前端的是JSON字符串,所以我们可以直接在树节点实体类的ID字段上添加注解: ```java import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; public class TreeNode { // 使用ToStringSerializer将Long序列化为字符串 @JsonSerialize(using = ToStringSerializer.class) private Long id; // 其他字段... } ``` 解决方案2:全局配置Jackson(推荐) 在Spring Boot中配置一个全局的ObjectMapper,将所有Long类型序列化为字符串。这样无论哪里返回的JSON都会生效。 创建Jackson配置类: ```java import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; @Configuration public class JacksonConfig { @Bean public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) { ObjectMapper objectMapper = builder.createXmlMapper(false).build(); SimpleModule module = new SimpleModule(); // 将Long和long类型序列化为字符串 module.addSerializer(Long.class, ToStringSerializer.instance); module.addSerializer(long.class, ToStringSerializer.instance); objectMapper.registerModule(module); return objectMapper; } } ``` 注意:如果项目中已经自定义了ObjectMapper,需要确保这个配置被加载。 解决方案3:使用Hutool的JSONConfig(如果使用Hutool的JSONUtil) 如果项目中直接使用Hutool的JSONUtil来序列化树,那么可以通过JSONConfig来设置序列化选项。但是,通常Spring Boot项目使用Jackson,所以不推荐这种方式。如果确实使用,可以这样: ```java // 创建JSONConfig并设置对Long类型使用ToStringSerializer JSONConfig jsonConfig = JSONConfig.create() .setSerializer(Long.class, (value, jsonWriter) -> jsonWriter.writeString(value.toString())); // 使用JSONUtil.toJsonStr时传入配置 String json = JSONUtil.toJsonStr(tree, jsonConfig); ``` 但是,在Spring MVC中,我们通常使用@ResponseBody返回JSON,由Jackson处理,所以解决方案1和2更通用。 验证: 配置后,查看返回的JSON中,ID字段是否为字符串(用双引号包围)。 如果问题仍未解决,请检查: 1. 配置是否生效?例如,是否被其他配置覆盖。 2. 是否在返回给前端前,数据已经被转换(例如,先调用了其他序列化工具)? 总结:使用Jackson的全局配置或字段注解,将long类型序列化为字符串,是解决前端精度丢失问题的通用方法,无论是否使用Hutool的TreeUtil。 根据用户提供的引用[3],他们可能已经处理了null值的情况,但并未处理long类型。因此,需要添加针对long类型的序列化器。 另外,引用[1]中提到了树的序列化,但那是关于树结构的字符串表示,与JSON序列化不同。引用[2]是关于HashMap的,与当前问题无关。 因此,针对用户的问题,核心答案就是配置Jackson将long转为字符串。 生成相关问题: 1. 如何在Spring Boot中自定义Jackson的ObjectMapper? 2. Hutool的TreeUtil如何构建树形结构? 3. 除了序列化为字符串,还有哪些方法解决JavaScript大数精度丢失? 4. 如何验证Jackson的全局配置已经生效? </think> ### Hutool Tree序列化后Long类型精度丢失的解决方案 当使用Hutool的`TreeUtil`构建树形结构后,若序列化时仍出现Long类型精度丢失(如ID值被截断或末尾变为0),根本原因是**Hutool默认使用Java序列化而非JSON序列化**。以下是完整解决方案: #### 1. 核心问题分析 - **问题本质**:Hutool的`Tree<T>`对象通过`toString()`序列化时调用Java原生序列化,未处理Long类型精度问题 - **根本矛盾**:Java的Long类型(最大$2^{63}-1$)与JavaScript的Number类型(安全整数仅$2^{53}-1$)精度范围不匹配 - **典型表现**:ID超过15位(如`15555666666000001`)时,前端接收变为`15555666666000000` #### 2. 推荐解决方案:强制JSON序列化 在返回树形数据给前端前,主动将Hutool的Tree对象转为JSON字符串,并配置Jackson序列化规则: ```java import cn.hutool.core.lang.tree.Tree; import cn.hutool.json.JSONUtil; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; // 配置全局序列化规则 ObjectMapper objectMapper = new ObjectMapper(); SimpleModule module = new SimpleModule(); module.addSerializer(Long.class, new ToStringSerializer()); // 序列化Long为字符串 objectMapper.registerModule(module); // Hutool构建树 List<Tree<Long>> tree = TreeUtil.build(nodes, rootId); // 转换为JSON字符串(关键步骤) String json = objectMapper.writeValueAsString(tree); // 返回前端(示例) response.setContentType("application/json;charset=UTF-8"); response.getWriter().write(json); ``` #### 3. 实体类注解方案(单点控制) 若无法修改全局序列化规则,可在树节点的实体类字段添加注解: ```java public class Dept { @JsonSerialize(using = ToStringSerializer.class) // 序列化为字符串 private Long id; private String name; // getters/setters... } ``` #### 4. 验证步骤 1. 后端输出序列化后的JSON字符串,确认ID是否为字符串格式: ```json [{ "id": "15555666666000001", // 注意引号 "name": "研发部", "children": [...] }] ``` 2. 前端通过`typeof id === 'string'`验证数据类型 #### 5. 原理说明 | 方法 | Hutool默认行为 | 改造后行为 | |------|----------------|------------| | **序列化方式** | Java对象序列化 | Jackson JSON序列化 | | **Long处理** | 保持数值类型 | 转为字符串类型 | | **精度保障** | 丢失精度 | 完整保留 | | **前端兼容** | 需要额外处理 | 直接使用字符串ID | > **关键点**:通过`objectMapper.writeValueAsString()`强制使用Jackson序列化,结合`ToStringSerializer`实现Long到String的转换,规避JavaScript精度限制[^1]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值