Spring Boot RestController重复创建对象致重复购票检测失效问题
问题:Spring Boot中RestController每次请求重置座位状态,无法检测重复购票
我在Spring Boot项目里碰到个问题:RestController的每次Post请求都会导致座位对象被重新创建,根本没法基于初始的座位状态做重复购票检测。核心需求是调用Post接口时,如果用户重复请求同一个已售出的座位,就抛出异常,但现在每次请求都会重置所有座位状态,完全触发不了这个异常逻辑。
相关代码
Controller代码
@RestController public class ControllerRoom { private final Room cinema; public ControllerRoom() { this.cinema = new Room(9,9); } }
Post购票接口代码
@PostMapping("/purchase") public ResponseEntity<Seats> sellSeat(@RequestBody Seats seat){ System.out.println("Requested body =" + seat.getRow() +" "+ seat.getColumn() + " " + seat.getPrice() ); for (Seats s : cinema.getSeats()){ if (s.getColumn() == seat.getColumn() && s.getRow() == seat.getRow()){ if (!s.isTaken()) { System.out.println(s.setTaken(true)); return new ResponseEntity<>(s, HttpStatus.OK); } if (s.isTaken()){ throw new SeatRequestException("The ticket has been already purchased!"); } } } throw new SeatRequestException("The number of a row or a column is out of bounds!"); }
问题根源
排查后发现问题出在Room类的getSeats()方法里——这个方法每次被调用时,都会重新往available_seats列表里添加全新的Seats对象,相当于每次请求都重置了所有座位的状态:
@JsonGetter(value = "available_seats") public ArrayList<Seats> getSeats() { for (int i=1; i<=total_columns; i++){ for (int j=1; j<=total_rows; j++){ if (i<=4) { available_seats.add(new Seats(i, j, false, 10)); } else { available_seats.add(new Seats(i,j, false,8)); } } } System.out.println(available_seats.size()); return available_seats; }
解决方案
1. 将座位初始化逻辑移到Room类的构造方法
把创建所有座位的代码从getSeats()移到Room的构造方法中,确保只在Room对象实例化时生成一次座位列表,后续调用getSeats()直接返回已初始化好的列表:
public class Room { private ArrayList<Seats> available_seats; private int total_rows; private int total_columns; public Room(int total_rows, int total_columns) { this.total_rows = total_rows; this.total_columns = total_columns; this.available_seats = new ArrayList<>(); // 仅在构造时初始化座位 for (int i=1; i<=total_columns; i++){ for (int j=1; j<=total_rows; j++){ if (i<=4) { available_seats.add(new Seats(i, j, false, 10)); } else { available_seats.add(new Seats(i,j, false,8)); } } } } @JsonGetter(value = "available_seats") public ArrayList<Seats> getSeats() { // 直接返回已有的座位列表,不再重复创建 return available_seats; } // 其他必要方法... }
2. 确认Controller的单例特性(默认已满足)
Spring Boot中@RestController注解的类默认是单例Bean,所以Controller里的Room对象只会被创建一次,不会每次请求都实例化新的Controller和Room实例,这一步不需要额外代码,只需确保没有手动修改Controller的作用域。
3. 可选:优化座位查找逻辑(提升性能)
如果座位数量较多,遍历列表查找座位的效率较低,可以改用Map存储座位,以行_列作为key直接定位:
public class Room { private ArrayList<Seats> available_seats; private Map<String, Seats> seatMap; private int total_rows; private int total_columns; public Room(int total_rows, int total_columns) { this.total_rows = total_rows; this.total_columns = total_columns; this.available_seats = new ArrayList<>(); this.seatMap = new HashMap<>(); // 初始化座位列表和Map for (int i=1; i<=total_columns; i++){ for (int j=1; j<=total_rows; j++){ Seats seat; if (i<=4) { seat = new Seats(i, j, false, 10); } else { seat = new Seats(i,j, false,8); } available_seats.add(seat); seatMap.put(i + "_" + j, seat); } } } @JsonGetter(value = "available_seats") public ArrayList<Seats> getSeats() { return available_seats; } // 根据行和列快速获取座位 public Seats getSeat(int row, int column) { return seatMap.get(row + "_" + column); } }
修改后的Controller购票接口:
@PostMapping("/purchase") public ResponseEntity<Seats> sellSeat(@RequestBody Seats seat){ System.out.println("Requested body =" + seat.getRow() +" "+ seat.getColumn() + " " + seat.getPrice() ); Seats targetSeat = cinema.getSeat(seat.getRow(), seat.getColumn()); if (targetSeat == null) { throw new SeatRequestException("The number of a row or a column is out of bounds!"); } if (!targetSeat.isTaken()) { targetSeat.setTaken(true); return new ResponseEntity<>(targetSeat, HttpStatus.OK); } else { throw new SeatRequestException("The ticket has been already purchased!"); } }
内容的提问来源于stack exchange,提问作者huga721
相关产品推荐
相关产品推荐

