【问题标题】:How bad is a service circular reference?服务循环引用有多糟糕?
【发布时间】:2023-02-16 22:52:04
【问题描述】:

我有一个问题,它与我遇到的错误有关。在我的服务中使用循环引用到底有多糟糕?我很清楚错误是由于什么以及如何解决的,只是在我工作的公司中,一位资深人士建议我,对于交易问题,有必要进行这样的循环引用,实际上这是一个非常经常发生的问题在那里练习,但由于我是从头开始一个个人项目,这是我第一次得到错误,它再次引发了疑问。非常感谢您!

这是服务的代码

public class MedicalRecordServiceImpl implements MedicalRecordService {

    private final MedicalRecordRepository medicalRecordRepository;
    private final MedicalRecordService medicalRecordService;
    private final PatientService patientService;
    private final TutorService tutorService;
    private final MedicalHistoryAnswerService medicalHistoryAnswerService;
    private final DentalHistoryAnswerService dentalHistoryAnswerService;

    public MedicalRecordServiceImpl(MedicalRecordRepository medicalRecordRepository, MedicalRecordService medicalRecordService, PatientService patientService, TutorService tutorService, MedicalHistoryAnswerService medicalHistoryAnswerService, DentalHistoryAnswerService dentalHistoryAnswerService) {
        this.medicalRecordRepository = medicalRecordRepository;
        this.medicalRecordService = medicalRecordService;

        this.patientService = patientService;
        this.tutorService = tutorService;
        this.medicalHistoryAnswerService = medicalHistoryAnswerService;
        this.dentalHistoryAnswerService = dentalHistoryAnswerService;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void saveMedicalRecord(MedicalRecordEntity medicalRecord) {
        medicalRecordRepository.save(medicalRecord);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public ResponseEntity<?> createNewMedicalRecord(MedicalRecordDTO medicalRecordDTO) {

        PatientEntity patient = this.storeMedicalRecordIntoPatient(medicalRecordDTO);
        TutorEntity tutor = this.storeMedicalRecordIntoTutor(medicalRecordDTO);
        List<MedicalHistoryAnswerEntity> medicalHistoryAnswers = this.storeMedicalRecordIntoMedicalHisAns(medicalRecordDTO);
        List<DentalHistoryAnswerEntity> dentalHistoryAnswers = this.storeMedicalRecordIntoDentalHisAns(medicalRecordDTO);

        patientService.savePatient(patient);
        tutor.setPatient(patient);
        tutorService.saveTutor(tutor);

        MedicalRecordEntity medicalRecord = this.createMedicalRecord(patient, tutor);

        medicalRecordService.saveMedicalRecord(medicalRecord);

        medicalHistoryAnswers.forEach(medicalHistoryAnswer -> {
            medicalHistoryAnswer.setMedicalRecord(medicalRecord);
            medicalHistoryAnswerService.saveMedicalHistoryAnswer(medicalHistoryAnswer);
        });

        dentalHistoryAnswers.forEach(dentalHistoryAnswer -> {
            dentalHistoryAnswer.setMedicalRecord(medicalRecord);
            dentalHistoryAnswerService.saveDentalHistoryAnswer(dentalHistoryAnswer);
        });

        return ResponseEntity.status(HttpStatus.OK).body("");
    }
}

【问题讨论】:

  • 除非您有多个 MedicalRecordService 的实现,否则使用将引用同一服务实例的引用调用方法没有任何意义(因为 bean 默认为 Singleton
  • 在这种情况下,自我引用没有意义。如果 saveMedicalRecord 将在新事务中运行,那将是有意义的。但在这里它没有意义。该服务的另一个问题是它通过返回属于控制器的 ResponseEntity 绑定到 Web 层不是你的服务。
  • @M.Deinum 那你建议我直接从控制器返回响应实体?事情是有人教我控制器不应该有任何逻辑,它们应该只限于调用服务然后我从控制器所做的就是调用服务并且已经,然后服务负责返回 200, 404、500...
  • 那是错的。您的服务不应该绑定到网络,控制器的责任是将服务答案转换为适合网络的内容,并将来自网络的输入转换为服务可以使用的内容。您的控制器中应该只有转换逻辑(或多或少)。所有业务逻辑都应该驻留在服务中。现在你有一个服务,当从消息队列、soap web 服务等使用时,它是无用的,而你的想法是你应该能够重用它。

标签: java spring spring-boot spring-mvc


【解决方案1】:

您可能需要循环依赖的唯一原因是当您想要访问“this”作为 bean 以触发带注释的方法逻辑时。

例如,如果您有两个方法“foo”(用@Transactional 注释)和“bar”(在其中调用“foo”)。如果调用 bar>foo(selfBean.foo() 而不是 this.foo()),您将不得不使用自我注入来触发事务。

您也可以使用 @Lasy 进行自我注入以避免循环依赖错误。

但这是一个非常丑陋的解决方案,如果可能的话,您应该避免使用它。这取决于情况,可能将逻辑拆分到不同的服务或使用 TransactionTemplate。

【讨论】:

  • 正如您刚才所说,例如,如果我将“@Transactional”删除到 createNewMedicalRecord 方法,我会在 saveMedicalRecord 方法中收到以下错误“方法不应调用具有不兼容的‘@Transactional’值的同类方法”,这是唯一的方法消失是在循环引用或将@Transactional 放到父方法中,我真的不明白为什么。以及为什么以前面提到的这两种方式解决它。
  • 当您在没有代理对象(bean 对象)的情况下访问对象方法时,预计会忽略 @Transactional。当您在对象中调用对象方法时,就会发生这种情况。
【解决方案2】:

正如您所说,我假设您知道错误是什么以及如何解决它。由于以下原因,循环引用是不好的:

Spring 在您启动项目时加载 beans,这意味着它以正确的顺序加载每个 bean,因此它可以加载所有 beans 并成功引用它们。如果您有一个循环引用,Spring 将不知道首先启动哪个 bean,因此会发生错误。这是关于 Spring 是如何工作的。

我现在的项目也有这个错误,你不局限于不做循环引用,你只需要指示Spring,让它知道在这些情况下如何处理每个bean。

【讨论】:

    【解决方案3】:

    好吧,想象一下: 你得到了一部新手机,大家都很兴奋。你想解锁它,但它受密码保护。密码在锁定手机的笔记中可用。

    所以,你想使用你的手机,你需要通行证。你想要通行证,为此你需要解锁手机。你想使用你的电话,你需要你的通行证......等等。

    当你有循环引用时也会发生同样的情况,对于 A 你需要 B,对于 B 你需要 A,所以你不能创建 A 也不能创建 B 也不能继续。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-30
      • 1970-01-01
      • 2011-10-14
      • 1970-01-01
      • 2021-07-12
      • 2010-11-24
      相关资源
      最近更新 更多