You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 05:05:48