【问题标题】:Data flow between different MVC layers不同 MVC 层之间的数据流
【发布时间】:2025-12-24 10:55:10
【问题描述】:

下面我展示了从使用表单到持久层的数据流。但是对于哪些对象应该在 MVC 的哪个层可用以及数据应该如何在 MVC 的不同层之间传输存在疑问。我正在使用 Spring,所以下面发布的代码是 Spring 框架的代码。

开始吧,我有一个 DTO(数据传输对象)PatientForm,它保存了用户输入的表单数据。

public class Patient {

private int id;
private String name;
private String medicineOne;
private String medicineTwo;

public int getId() {
    return id;
}
public void setId(int id) {
    this.id = id;
}
public String getName() {
    return name;
}
public void setName(String name) {
    this.name = name;
}
public String getMedicineOne() {
    return medicineOne;
}
public void setMedicineOne(String medicineOne) {
    this.medicineOne = medicineOne;
}
public String getMedicineTwo() {
    return medicineTwo;
}
public void setMedicineTwo(String medicineTwo) {
    this.medicineTwo = medicineTwo;
}
}

PatientForm 被传递给控制器​​PatientController,不传输数据而是将表单传递给服务层PatientService

@PostMapping("/patient/addpatient")
public ModelAndView addPatient(@ModelAttribute("patientform") PatientForm patient){
    patientService.addPatient(patient);
    return new ModelAndView("redirect:/");
}

在服务层 PatientService 中,数据从 DTO 到持久实体 Patient 的实际传输发生。

public void addPatient(com.hp.view.form.PatientForm patientForm){
    String medicineOneName = patientForm.getMedicineOne();
    Medicine medicineOne = medicineService.findByName(medicineOneName);
    if(medicineOne == null){
        medicineService.save(new Medicine(medicineOneName));
        medicineOne = medicineService.findByName(medicineOneName);
    }

    String medicineTwoName = patientForm.getMedicineTwo();
    Medicine medicineTwo = medicineService.findByName(medicineTwoName);
    if(medicineTwo == null){
        medicineService.save(new Medicine(medicineTwoName));
        medicineTwo = medicineService.findByName(medicineTwoName);
    }

    List<Medicine> medicines = new ArrayList<>();
    medicines.add(medicineOne);
    medicines.add(medicineTwo);

    Patient patient = new Patient();
    patient.setName(patientForm.getName());
    patient.setMedicine(medicines);
    patientRepository.save(patient);
}

根据上述流程,这是我的问题:

  1. 是否应该 Controller layerService layer 将数据从 DTO 传输到持久实体?

  2. 如果在控制器中完成数据传输,则模型实体将在控制器层中声明。如果数据传输在服务层完成,则意味着 DTO 将在服务层声明。两者哪个更受欢迎?

  3. 在我的服务层中,我已经实例化了我的实体对象 Patient 的实例。这会产生问题吗?我应该让 Spring contianer 管理我的实体 bean?

  Patient patient = new Patient();

【问题讨论】:

    标签: java spring-mvc model-view-controller persistence data-transfer-objects


    【解决方案1】:

    (1) 控制器层或服务层应该将数据从 DTO 传输到 持久实体?

    FormBeans 是特定于客户端/通道/端点的,因此控制器层应该执行特定于客户端的验证(如最小长度、最大长度等),然后将 FormBean 的数据转换为实体 Bean,然后将其传递给服务层。 在 3 层架构中,Service 层应该是可重用的(解释如下),它不应该知道 FormBeans,因此接收实体对象并应该负责处理业务逻辑(执行业务验证和核心逻辑以及与 DAO/Repository 类交互)。

    (2) 如果在控制器中完成数据传输意味着模型实体将 在控制器层中声明。如果数据传输是在服务中完成的 layer 表示 DTO 将在服务层中声明。两者哪个是 喜欢吗?

    可以重用/公开单个服务以连接多个端点,例如控制器或不同的 Web 服务,并且每个端点可能需要不同的 formbean,因此控制器(端点)层更适合处理端点特定的验证和然后创建/传递正确的实体对象给服务。

    (3) 在我的服务层中,我已经实例化了我的实体实例 对象患者。这会产生问题吗?我应该让 Spring 容器管理我的实体 bean?

    没问题。由于实体对象不是单例,您可以像以前那样在服务中创建它们。但是,如果您允许 Spring 管理它们,则需要确保为每个输入请求创建一个实例。这是因为Spring bean的defaultscopesingleton,需要改成request作用域。

    【讨论】:

      【解决方案2】:

      实际上,我会采用完全不同的方法。 DTO 可能会绑定到特定的 Web 应用程序框架。这会降低服务的可重用性。相反,您可以创建类似“DTO 到实体转换器”的东西。一个简单的界面可能如下所示:

      public interface DtoToEntityConverter<T, R> {
          R getEntity(T t);
      }
      

      然后你可以定义具体的类(甚至在更简单的情况下使用 lambdas):

      @Component
      public class PatientFormConverter implements DtoToEntityConverter<PatientForm, Patient> {
      
          public Patient getEntity(PatientForm form) {
              // Conversion rules and stuff...
          }
      }
      

      然后,只需将该组件注入控制器并在添加患者时调用getEntity

      addPatient(patientFormConverter.getEntity(patientForm));
      

      【讨论】:

        【解决方案3】:

        在 spring 中,您让应用程序上下文来管理您的 bean(即,您不初始化您的类),然后您可以自动装配(将它们包含在其他类中)而无需显式初始化它们。

        1) 服务层用作控制器和模型之间的中介。也就是说,您将服务自动连接到您的休息控制器中。

        上面解释了第二个和第三个答案。

        P.S.:这里 autowire 表示 dependency injection - .http://www.javatpoint.com/dependency-injection-in-spring

        【讨论】: