@ModelAttribute and abstract class

2020-03-31 03:38发布

I know that there have been similar questions. The examples given in them are too fragmentary and unclear.

I need to edit the entities through a form on the page that sends the POST. The standard method is a method in the controller uses the parameter with @ModelAttribute and validator. If one form serves some subclass of an abstract class, there are no problems with the generation of the necessary fields, but there is a problem in the controller.

As I understand it, @ModelAttribute works this way: it initializes the desired object class, and then collects his fields of the parameters of the request. Of course, if the object is an abstract class, it can not be initialized. Therefore, the form has a field that will indicate what subclass to initialize. Next, we need peace of code, that will read this attribute and initialize the correct subclass. What should it be? I saw fragmentary examples about the Converter, PrepertyEditor, WebDataBinder, but difficult to put everything together.

So. There are the following hierarchy:

public abstract class Person {role, name, email, password ...}
public class Student extends Person {}
public class Lecturer extends Person {}

There is a controller and methods in it:

@RequestMapping (Path = "/ persons / uid {personId} / edit",
                method = RequestMethod.GET)
public String editPerson (@PathVariable Integer personId, Model model) {
    Person find = personDAO.read (personId);
    model.addAttribute ( "person", find);
    return "editPerson";
}

@RequestMapping (Path = "/ persons / uid {personId} / edit",
                method = RequestMethod.POST)
public String editPersonPost (@PathVariable Integer personId,
        @Valid @ModelAttribute ( "Person") Person person,
        BindingResult result) {
    if (result.hasErrors ()) return "editPerson error = true?";
    personDAO.update (person);
    return "redirect: / persons / uid" + personId + "saved = true?";
}

And there is a JSP with a form:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%>

<h1>${person.name}</h1>

<form:form action="edit" method="post" commandName="person">
    <input type="hidden" value="${person.role}" name="person_type" />
    <table>
        <tr>
            <td>Password</td>
            <td><form:input path="httpAuth.password" type="password"/></td>
            <td><form:errors path="httpAuth.password" cssClass="error"></form:errors></td>
        </tr>
        <tr>
            <td>Email</td>
            <td><form:input path="email" /></td>
            <td><form:errors path="email" cssClass="error"></form:errors></td>
        </tr>
        <tr>
            <td></td>
            <td><input type="submit" value="Save"></td>
            <td></td>
        </tr>
    </table>
</form:form>

Also, the converter has been written, but I doubt whether it is necessary, or else do it (inheriting another class ...)

public class PersonConverter implements Converter <String, Person> {

public Person convert (String personType) {
    Person person = null;
    switch (personType) {
        case "Student":
            person = new Student ();
            break;
        case "Lecturer":
            person = new Lecturer ();
            break;
        default:
            throw new IllegalArgumentException (
                    "Unknown person type:" + personType);
    }
    return person;
}}

Which is registered with ConversionService

<bean class="org.springframework.context.support.ConversionServiceFactoryBean"
    id="theConversionService">
    <property name="converters">
        <list>
            <bean class="schedule.service.PersonConverter"></bean>
        </list>
    </property>
</bean>
<mvc:annotation-driven conversion-service="theConversionService" validator="validator"/>

Nevertheless, something is missing, that is a method that will take person_type from request parameters and give it to converter, and it will return a result of the method the controller via the automatic binding mechanisms.

Help me please.

1条回答
我欲成王,谁敢阻挡
2楼-- · 2020-03-31 04:29

You just need to ensure that the element below

<input type="hidden" value="${person.role}" name="person_type" />

has its named attribute changed to person

<input type="hidden" value="${person.role}" name="person" />

so that it matches the model attribute in your controller

public String editPersonPost (@PathVariable Integer personId,
        @Valid @ModelAttribute ( "person") Person person,
        BindingResult result)

This is how it works.

When a request is received and Spring needs to create the model attribute it checks if the attribute already exists. If it doesn`t exist and there is no request parameter of matching name it creates a new object using the default constructor of the parameter class

If it exists and matches the argument type it proceeds to bind the request parameters. If it is not compatible or a request parameter of same name is available it tries to find a converter capable of converting the current value to the required type

If conversion is successful it binds the request parameters to the result otherwise it throws an Exception

In your case the person attribute is sent as a String. Spring will attempt to convert it to a Person. It picks your PersonConverter to do the conversion to an appropriate subclass before binding

查看更多
登录 后发表回答