Depois de passar por alguns tutoriais e leitura inicial do documento a partir da referência docs.spring.org eu entendi que ele é criado no controlador de uma classe POJO criado pelo desenvolvedor. Mas ao ler este me deparei com o parágrafo abaixo:
Um
@ModelAttribute
em um argumento método indica o argumento deve ser recuperada a partir do modelo. Se não estiver presente no modelo, o argumento deve ser instanciado em primeiro lugar e, em seguida, adicionados ao modelo. Uma vez presentes no modelo, os campos do argumento deve ser preenchida a partir de todos os parâmetros de solicitação que têm nomes correspondentes. Isto é conhecido como dados de ligação em Spring MVC, um mecanismo muito útil que poupa de ter de analisar cada campo de formulário individualmente.@RequestMapping(value="/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST) public String processSubmit(@ModelAttribute Pet pet) { }
No parágrafo que é mais preocupante é a linha:
"Se não estiver presente no modelo ..."
Como podem os dados estar lá no modelo? (Porque não criamos um modelo - que será criado por nós.)
Além disso, tenho visto alguns métodos controlador aceitante do Model
tipo como um argumento. O que isso significa? Está ficando a Model
algum lugar criado? Se assim for, que está criando para nós?
Se não estiver presente no modelo, o argumento deve ser instanciado em primeiro lugar e, em seguida, adicionados ao modelo.
O parágrafo descreve o seguinte trecho de código:
if (mavContainer.containsAttribute(name)) {
attribute = mavContainer.getModel().get(name);
} else {
// Create attribute instance
try {
attribute = createAttribute(name, parameter, binderFactory, webRequest);
}
catch (BindException ex) {
...
}
}
...
mavContainer.addAllAttributes(attribute);
(retirado ModelAttributeMethodProcessor#resolveArgument
)
Para cada pedido, Primavera inicializa uma ModelAndViewContainer
instância que grava modelo e vista-relacionado decisões tomadas por HandlerMethodArgumentResolver
s e HandlerMethodReturnValueHandler
s durante o curso de invocação de um método de controlador.
A recém-criado ModelAndViewContainer
objecto é inicialmente preenchido com atributos de flash (se houver):
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
Isso significa que o argumento não será inicializado se ele já existe no modelo.
Para provar isso, vamos passar a um exemplo prático.
A Pet
classe:
public class Pet {
private String petId;
private String ownerId;
private String hiddenField;
public Pet() {
System.out.println("A new Pet instance was created!");
}
// setters and toString
}
A PetController
classe:
@RestController
public class PetController {
@GetMapping(value = "/internal")
public void invokeInternal(@ModelAttribute Pet pet) {
System.out.println(pet);
}
@PostMapping(value = "/owners/{ownerId}/pets/{petId}/edit")
public RedirectView editPet(@ModelAttribute Pet pet, RedirectAttributes attributes) {
System.out.println(pet);
pet.setHiddenField("XXX");
attributes.addFlashAttribute("pet", pet);
return new RedirectView("/internal");
}
}
Vamos fazer um pedido POST para o URI /owners/123/pets/456/edit
e ver os resultados:
A new Pet instance was created!
Pet[456,123,null]
Pet[456,123,XXX]
A new Pet instance was created!
Primavera criou um ModelAndViewContainer
e não encontrou nada para preencher a instância com (é um pedido de um cliente; não havia nenhum redirecionamentos). Desde que o modelo está vazio, Primavera teve que criar um novo Pet
objeto, chamando o construtor padrão que imprimiu a linha.
Pet[456,123,null]
Uma vez presentes no modelo, os campos do argumento deve ser preenchida a partir de todos os parâmetros de solicitação que têm nomes correspondentes.
Nós imprimimos o dado Pet
para se certificar de todos os campos petId
e ownerId
tinha sido obrigado corretamente.
Pet[456,123,XXX]
Nós estabelecemos hiddenField
para verificar a nossa teoria e redirecionado para o método invokeInternal
que também espera um @ModelAttribute
. Como podemos ver, o segundo método recebeu o exemplo (com o próprio valor oculto), que foi criado para o primeiro método.