java - puede - Formulario enviado en Spring MVC 3-explicación
spring mvc ejemplo (1)
Tengo problemas para entender cómo funciona un formulario enviado en Spring 3 MVC.
Lo que quiero hacer es crear un controlador que tome el nombre del usuario y se lo muestre. Y de alguna manera lo he hecho, pero realmente no entiendo cómo funciona. Asi que..
Tengo un formulario que se ve así:
<form:form method="post" modelAttribute="person">
<form:label path="firstName">First name</form:label>
<form:input path="firstName" />
<br />
<form:label path="lastName">Last name</form:label>
<form:input path="lastName" />
<br />
<input type="submit" value="Submit" />
</form:form>
También tengo un controlador que se ve así:
@Controller
public class HomeController {
@RequestMapping(value = "/", method = RequestMethod.GET)
public String showHelloPage(Model model) {
model.addAttribute("person", new Person());
return "home";
}
@RequestMapping(value = "/", method = RequestMethod.POST)
public String sayHello(Person person, Model model) {
model.addAttribute("person", person);
return "home";
}
}
Para mostrar un mensaje de bienvenida a un usuario, utilizo el siguiente código en la página JSP:
<c:if test="${not empty person.firstName and not empty person.lastName}">
Hello ${person.firstName} ${person.lastName}!
</c:if>
Y funciona (omito los archivos de configuración XML porque son irrelevantes para el problema).
Pensé que el atributo "modelAttribute" en una forma apunta a la variable de bean que debe ser poblada con los valores de las entradas (como se establece en sus atributos de "ruta"). Pero mira, funciona de una manera muy diferente. Si elimino la línea
model.addAttribute("person", new Person());
del método "showHelloPage" obtengo una excepción (común) "Ni BindingResult ni ...".
Además, al principio, el método "sayHello" se veía así:
(...)
public String sayHello(@ModelAttribute("person") Person person, Model model) {
(...)
Quiero decir, tenía la anotación "ModelAttribute". Lo agregué, porque en los tutoriales que leí, siempre estuvo presente. Pero después de eliminarlo, todo funcionó bien, como lo hizo antes.
Entonces mi pregunta es : ¿para qué sirve la anonnatación "ModelAttribute"? ¿Es una forma de omitir un atributo "modelAttribute" en un formulario? Y la segunda parte, ¿cuál es el camino (tal vez alguna anotación) para hacer que un formulario vincule automáticamente los valores de las entradas con las propiedades del bean correcto (que se declararía como un parámetro de método)? Sin necesidad de agregar un bean vacío antes de enviar un formulario (ya que tengo que hacerlo ahora).
Gracias por sus respuestas (que no son enlaces a la documentación de Spring, porque ya lo he leído).
La anotación @ModelAttribute
en este caso se usa para identificar un objeto que Spring debería agregar como atributo de modelo. Los atributos del modelo son una abstracción de los atributos HttpServletRequest
. Básicamente, son objetos identificados por alguna clave que encontrará su camino en los atributos HttpServletRequest
. Para ello, agregue manualmente un atributo con Model#addAttribute(String, Object)
, tenga un método anotado @ModelAttribute
anote un parámetro de método con @ModelAttribute
.
Lo que necesita comprender es cómo Spring resuelve los parámetros de su método de manejo e inyecta argumentos. Utiliza la interfaz HandlerMethodArgumentResolver
para hacerlo. Hay varias clases de implementación (vea javadoc) y cada una tiene la responsabilidad de resolveArgument()
devolviendo el argumento que Spring usará para invoke()
su método de manejo a través de la reflexión. Spring solo llamará al método resolveArgument()
si el HandlerMethodArgumentResolver
supportsParameter()
el HandlerMethodArgumentResolver
supportsParameter()
devuelve true
para el parámetro específico.
La implementación de HandlerMethodArgumentResolver
en cuestión aquí es ServletModelAttributeMethodProcessor
que se extiende desde ModelAttributeMethodProcessor
que estados
Resuelve los argumentos del método anotados con @ModelAttribute y maneja los valores de retorno de los métodos anotados con @ModelAttribute.
Spring (3.2) register este HandlerMethodArgumentResolver
y otros
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();
// Annotation-based argument resolution
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
resolvers.add(new RequestParamMapMethodArgumentResolver());
resolvers.add(new PathVariableMethodArgumentResolver());
resolvers.add(new ServletModelAttributeMethodProcessor(false));
resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters()));
resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters()));
resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
resolvers.add(new RequestHeaderMapMethodArgumentResolver());
resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
// Type-based argument resolution
resolvers.add(new ServletRequestMethodArgumentResolver());
resolvers.add(new ServletResponseMethodArgumentResolver());
resolvers.add(new HttpEntityMethodProcessor(getMessageConverters()));
resolvers.add(new RedirectAttributesMethodArgumentResolver());
resolvers.add(new ModelMethodProcessor());
resolvers.add(new MapMethodProcessor());
resolvers.add(new ErrorsMethodArgumentResolver());
resolvers.add(new SessionStatusMethodArgumentResolver());
resolvers.add(new UriComponentsBuilderMethodArgumentResolver());
// Custom arguments
if (getCustomArgumentResolvers() != null) {
resolvers.addAll(getCustomArgumentResolvers());
}
// Catch-all
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
resolvers.add(new ServletModelAttributeMethodProcessor(true));
return resolvers;
}
Cuando Spring necesite invocar el método de su manejador, iterará a través de los tipos de parámetros ya través de la lista anterior y usará el primero que supportsParameter()
.
Observe que se agregan dos instancias de ServletModelAttributeMethodProcessor
(una después de //catch all
comentarios). El ModelAttributeMethodProcessor
tiene un campo annotationNotRequired
que le dice si debe buscar @ModelAttribute
o no. La primera instancia debe buscar @ModelAttribute
, la segunda no. Spring hace esto para que pueda registrar sus propias instancias de HandlerMethodArgumentResolver
, consulte el // Custom arguments
comentario de // Custom arguments
.
Específicamente
@RequestMapping(value = "/", method = RequestMethod.POST)
public String sayHello(Person person, Model model) {
model.addAttribute("person", person);
return "home";
}
En este caso, no importa si su parámetro Person
está anotado o no. Un ModelAttributeMethodProcessor
lo resolverá y vinculará los campos de formulario, es decir. parámetros de solicitud, a los campos de la instancia. Ni siquiera debería necesitar agregarlo al model
ya que la clase ModelAttributeMethodProcessor
lo manejará.
En su método showHelloPage()
model.addAttribute("person", new Person());
es necesario con el <form>
taglib. Así es como resuelve sus campos de input
.
Entonces mi pregunta es: ¿para qué sirve la anonnatación "ModelAttribute"?
Para agregar automáticamente el parámetro especificado (o el valor de retorno del método) al modelo.
¿Es una forma de omitir un atributo "modelAttribute" en un formulario?
No, el enlace de form
busca un objeto en el Model
y vincula sus campos a input
elementos de input
html.
Y la segunda parte, ¿cuál es el camino (tal vez alguna anotación) para hacer que un formulario vincule automáticamente los valores de las entradas con las propiedades del bean correcto (que se declararía como un parámetro de método)? Sin necesidad de agregar un bean vacío antes de enviar un formulario (ya que tengo que hacerlo ahora).
Una etiqueta Spring <form>
acopla a un objeto de atributo modelo y usa sus campos para crear elementos de input
y label
. No importa cómo terminó el objeto en el modelo, siempre y cuando lo hizo. Si no puede encontrar un atributo de modelo con el nombre (clave) que especificó, arroja excepciones, como pudo ver.
<form:form method="post" modelAttribute="person">
La alternativa de proporcionar un bean vacío es crear el html usted mismo. Todo lo que hace Spring <form>
es usar los nombres de campo del bean para crear un elemento de input
. Así que esto
<form:form method="post" modelAttribute="person">
<form:label path="firstName">First name</form:label>
<form:input path="firstName" />
Crea algo como
<form method="post" action="[some action url]">
<label for="firstName">First name<label>
<input type="text" name="firstName" value="[whatever value firstName field had]" />
...
Spring vincula los parámetros de solicitud a los campos de instancia utilizando el atributo de name
.