Spring boot:无法打破bean之间的依赖循环

问题描述 投票:0回答:1

我正在使用 Spring Boot 创建一个航空公司网站。我有一个

Seat
类和
Flight
类,它们代表数据库中的表。这个想法是,每当添加航班时,都会自动创建该航班的座位并将其添加到表中。

以下是我添加航班和创建座位的服务类别:

座位服务实施:

package com.example.airline.flight.seat;

import com.example.airline.flight.FlightService;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class SeatServiceImp implements SeatService {
    private final SeatRepository seatRepository;
    private final @Lazy FlightService flightService; // Used @Lazy to break the cycle

    @Override
    public void createSeat(long flightId) {
        flightService.getSeatNumbers(flightId).forEach((key, value) -> {
            int rowNumber = 1;
            char seatLetter = 'A';
            for (int i = 1; i <= value; i++) {
                String seatNumber = seatLetter + String.valueOf(rowNumber);
                Seat seat = Seat.builder()
                        .seatNumber(seatNumber)
                        .seatClass(SeatClass.valueOf(key))
                        .flight(flightService.getFlightById(flightId))
                        .seatStatus(SeatStatus.AVAILABLE)
                        .build();
                seatRepository.save(seat);

                seatLetter++;
                if (seatLetter > 'F') {
                    seatLetter = 'A';
                    rowNumber++;
                }
            }
        });
    }
}

航班服务实施:

package com.example.airline.flight;

import com.example.airline.flight.seat.SeatService;
import com.example.airline.plane.Plane;
import com.example.airline.plane.PlaneService;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import java.util.Map;

@Service
@RequiredArgsConstructor
public class FlightServiceImp implements FlightService {
    private final FlightRepository flightRepository;
    private final PlaneService planeService;
    private final @Lazy SeatService seatService; // Used @Lazy to break the cycle

    @Override
    public Map<String, Integer> getSeatNumbers(long flightId) {
        long planeId = flightRepository.findById(flightId).orElseThrow().getPlane().getId();
        return planeService.getSeatNumbers(planeId);
    }

    @Override
    public Flight getFlightById(long id) {
        return flightRepository.findById(id).orElseThrow();
    }

    @Override
    public void addFlight(Flight flight) {
        if (planeService.findPlaneById(flight.getPlane().getId()) == null) {
            throw new IllegalArgumentException("Plane not found");
        }
        Plane existingPlane = planeService.findPlaneById(flight.getPlane().getId());
        if (existingPlane == null || !existingPlane.getCurrentAirport().equals(flight.getDepartureAirport())) {
            throw new IllegalArgumentException("Plane not found");
        }
        flightRepository.save(flight);
        seatService.createSeat(flight.getId());
    }
}

问题:

当我运行应用程序时,出现循环依赖错误。这是错误消息:

The dependencies of some of the beans in the application context form a cycle:

DBSeeder defined in file [C:\Users\Omar\Desktop\Airline_Backend\target\classes\com\example\airline\DBSeeder.class]
|  flightServiceImp defined in file [C:\Users\Omar\Desktop\Airline_Backend\target\classes\com\example\airline\flight\FlightServiceImp.class]
↑     ↓
|  seatServiceImp defined in file [C:\Users\Omar\Desktop\Airline_Backend\target\classes\com\example\airline\flight\seat\SeatServiceImp.class]

我尝试过的:

  1. 使用
    @Lazy
    注释
    :我尝试在依赖项上使用
    @Lazy
    注释来打破循环,但问题仍然存在。
  2. 重构代码:我考虑了一种基于事件的方法来解耦依赖关系,但这会显着改变我的应用程序的工作方式。

目标:

我需要一个更简单的解决方案,可以解决循环依赖关系,而无需显着改变应用程序的结构。我怎样才能实现这个目标?

附加信息:

这是我的

DBSeeder
和其他相关课程,可能有助于提供更多上下文:

DBSeeder 实施:

package com.example.airline;

import com.example.airline.plane.aircraft.Aircraft;
import com.example.airline.plane.aircraft.AircraftService;
import com.example.airline.plane.Plane;
import com.example.airline.plane.PlaneService;
import com.example.airline.flight.Flight;
import com.example.airline.flight.FlightService;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

import java.sql.Date;
import java.sql.Time;
import java.time.LocalDate;

@Component
@RequiredArgsConstructor
public class DatabaseSeeder implements CommandLineRunner {

    private final AircraftService aircraftService;
    private final PlaneService planeService;
    private final @Lazy FlightService flightService; // Added @Lazy here to prevent early initialization

    @Override
    public void run(String... args) throws Exception {
        // Add Aircraft
        Aircraft aircraft1 = Aircraft.builder()
                .model("Boeing 737")
                .businessSeats(20)
                .businessSeatPrice(2000)
                .economySeats(150)
                .economySeatPrice(500)
                .firstClassSeats(10)
                .firstClassSeatPrice(5000)
                .rangeKm(6000)
                .build();

        aircraftService.addAircraft(aircraft1);

        Aircraft aircraft2 = Aircraft.builder()
                .model("Airbus A320")
                .businessSeats(15)
                .businessSeatPrice(1800)
                .economySeats(160)
                .economySeatPrice(600)
                .firstClassSeats(12)
                .firstClassSeatPrice(4500)
                .rangeKm(5800)
                .build();

        aircraftService.addAircraft(aircraft2);

        // Add Planes
        Plane plane1 = Plane.builder()
                .id(1L)
                .aircraft(aircraft1)
                .name("Plane 1")
                .lastMaintenanceDate(Date.valueOf(LocalDate.now()))
                .nextMaintenanceDate(Date.valueOf(LocalDate.now().plusDays(30))) // 30 days from now
                .hoursOnAir(1000)
                .flightsCompleted(200)
                .currentAirport("JFK")
                .operationlaStatus(OperationlaStatus.ACTIVE)
                .build();

        Plane plane2 = Plane.builder()
                .id(2L)
                .aircraft(aircraft1)
                .name("Plane 2")
                .lastMaintenanceDate(Date.valueOf(LocalDate.now()))
                .nextMaintenanceDate(Date.valueOf(LocalDate.now().plusDays(30))) // 30 days from now
                .hoursOnAir(1200)
                .flightsCompleted(220)
                .currentAirport("LAX")
                .operationlaStatus(OperationlaStatus.ACTIVE)
                .build();

        Plane plane3 = Plane.builder()
                .id(3L)
                .aircraft(aircraft2)
                .name("Plane 3")
                .lastMaintenanceDate(Date.valueOf(LocalDate.now()))
                .nextMaintenanceDate(Date.valueOf(LocalDate.now().plusDays(30))) // 30 days from now
                .hoursOnAir(800)
                .flightsCompleted(180)
                .currentAirport("JFK")
                .operationlaStatus(OperationlaStatus.ACTIVE)
                .build();

        planeService.addPlane(plane1);
        planeService.addPlane(plane2);
        planeService.addPlane(plane3);

        // Add Flights
        Flight flight1 = Flight.builder()
                .departureCity("New York")
                .arrivalCity("London")
                .departureTime(Time.valueOf("10:00:00"))
                .arrivalTime(Time.valueOf("20:00:00"))
                .departureDate(Date.valueOf(LocalDate.now()))
                .arrivalDate(Date.valueOf(LocalDate.now().plusDays(1))) // 1 day from now
                .departureAirport("JFK")
                .arrivalAirport("LHR")
                .departureTerminal("T4")
                .arrivalTerminal("T5")
                .departureCountry("USA")
                .arrivalCountry("UK")
                .plane(plane1)
                .build();

        Flight flight2 = Flight.builder()
                .departureCity("Los Angeles")
                .arrivalCity("Tokyo")
                .departureTime(Time.valueOf("14:00:00"))
                .arrivalTime(Time.valueOf("04:00:00"))
                .departureDate(Date.valueOf(LocalDate.now()))
                .arrivalDate(Date.valueOf(LocalDate.now().plusDays(1))) // 1 day from now
                .departureAirport("LAX")
                .arrivalAirport("HND")
                .departureTerminal("T2")
                .arrivalTerminal("T3")
                .departureCountry("USA")
                .arrivalCountry("Japan")
                .plane(plane2)
                .build();

        flightService.addFlight(flight1);
        flightService.addFlight(f

light2);
    }
}

在不显着改变应用程序结构的情况下解决此循环依赖问题的最佳方法是什么?

谢谢您的帮助!

spring spring-boot hibernate dependency-injection lazy-initialization
1个回答
0
投票

在编写服务甚至服务方法时,您应该考虑用例而不是实体。您的

addFlight
方法应该创建集合等,而不是将其委托给另一个服务(
SeatService
)。只需将该方法移动到
FlightService
使其成为
private
并从该方法中调用它即可。我怀疑您是否需要独立的
createSeat
方法。

当您移动该方法时,您可以放弃

SeatService
并简单地将
SeatRepository
注入到
FlightService
中,甚至更好,假设
Flight
是一个正确的 JPA 实体并且与
Seat
有关系,您甚至不知道需要它,只需将
Seat
添加到
Flight
即可一次性保存所有内容。

@Service
@RequiredArgsConstructor
public class FlightServiceImp implements FlightService {

    private final FlightRepository flightRepository;
    private final PlaneService planeService;

    @Override
    public Map<String, Integer> getSeatNumbers(long flightId) {
        long planeId = flightRepository.findById(flightId).orElseThrow().getPlane().getId();
        return planeService.getSeatNumbers(planeId);
    }

    @Override
    public Flight getFlightById(long id) {
        return flightRepository.findById(id).orElseThrow();
    }

    @Override
    public void addFlight(Flight flight) {
        Plane existingPlane = planeService.findPlaneById(flight.getPlane().getId());
        if (existingPlane == null) {
            throw new IllegalArgumentException("Plane not found");
        }
        if (existingPlane == null || !existingPlane.getCurrentAirport().equals(flight.getDepartureAirport())) {
            throw new IllegalArgumentException("Plane not found");
        }
        createSeats(flight);
        flightRepository.save(flight);
    }

    private void createSeats(Flight flight) {
      var planeId = flight.getPlane().getId();
      planeService.getSeatNumbers(planeId).forEach((key, value) -> {
        int rowNumber = 1;
        char seatLetter = 'A';
        for (int i = 1; i <= value; i++) {
          String seatNumber = seatLetter + String.valueOf(rowNumber);
          Seat seat = Seat.builder()
                      .seatNumber(seatNumber)
                      .seatClass(SeatClass.valueOf(key))
                      .flight(flight
                      .seatStatus(SeatStatus.AVAILABLE)
                      .build();
           flight.addSeat(seat);
           seatLetter++;
           if (seatLetter > 'F') {
             seatLetter = 'A';
             rowNumber++;
           }
         }
       });
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.