Spring Boot 50用户压测返回其他用户数据、空指针异常如何解决?
问题根因
你遇到的并发串数据、空指针问题核心是Spring Bean作用域使用错误:默认@Component标注的类都是单例(singleton),整个应用运行周期内仅存在一个实例。你将Employee、EmployeePerformance这类承载请求业务数据的POJO声明为单例Bean,还直接在Controller、Service中通过@Autowired注入全局使用,多并发请求时不同线程会同时修改同一个实例的属性,就会出现数据互相覆盖、拿到其他用户信息的情况,甚至属性赋值到一半被其他线程修改就会触发空指针异常。
具体问题点
- Controller中
@Autowired注入的Employee是全局单例,每次请求调用set方法赋值时都会覆盖该实例的原有值,并发场景下多个请求同时赋值,后续逻辑自然会拿到其他用户的参数 - Service中
@Autowired注入的EmployeePerformance是全局单例,且你将请求返回的结果赋值给了类成员变量this.empPerformance,多线程同时调用时会互相覆盖该成员变量的指向,也会导致数据错乱
修改方案
- 移除业务数据类的
@Component注解,这类纯数据承载的POJO不需要交给Spring管理,每次使用时直接new新实例即可
// 删除Employee、EmployeePerformance类上的@Component注解 public class Employee { // 原有属性、get/set方法全部保留 } public class EmployeePerformance { // 原有属性、get/set方法全部保留 }
- Controller中不要注入Employee,每次请求直接创建新实例,保证线程隔离
@RequestMapping("/employee") public class EmployeeController { @Autowired EmployeeService employeeservice; // 删除这行注入代码 // @Autowired // Employee employee; @RequestMapping("/getDetails") public void getEmployeeDetails() throws HttpException, IOException, JAXBException { // 每次请求新建独立的Employee实例 Employee employee = new Employee(); employee.setEmpId("100"); employee.setEmpName("Nuno"); employee.setDept("Research"); employee.setLocation("India"); EmployeePerformance emp = employeeservice.EmployeeDetails(employee); System.out.println(emp.toString()); } }
- Service中删除EmployeePerformance的成员变量声明和注入代码,所有请求相关的变量都放在方法内部定义,避免多线程共享
@Component public class EmployeeService { // 删除这两行成员变量代码 // @Autowired // EmployeePerformance empPerformance; @Autowired private AutowireCapableBeanFactory beanFactory; public EmployeePerformance EmployeeDetails(Employee emp) throws HttpException, IOException, JAXBException { // 直接返回方法调用结果,不需要赋值给类成员变量 return getDetails(emp); } private EmployeePerformance getDetails(Employee emp) throws HttpException, IOException, JAXBException { String result; String url=emp.getEmpName(); HttpClient client = new HttpClient(); GetMethod method = new GetMethod(url); method.addRequestHeader("3454362523", emp.getEmpId()); client.executeMethod(method); result = method.getResponseBodyAsString(); JAXBContext jaxbContext = JAXBContext.newInstance(EmployeePerformance.class); Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller(); EmployeePerformance empPerformance = (EmployeePerformance) jaxbUnmarshaller .unmarshal(new InputSource(new StringReader(result))); empPerformance.setProject("Banking"); empPerformance.setRating("Good"); return empPerformance; } }
- 可选优化:如果确实需要将POJO交给Spring管理,给对应类加上
@Scope("prototype")注解,设置为原型作用域,每次注入都会创建新的实例;不过对于纯数据类,更推荐直接new,性能更高逻辑也更清晰。
内容的提问来源于stack exchange,提问作者Krishna
相关产品推荐
相关产品推荐

