Afinal, o que é Spring MVC?
Atualmente no desenvolvimento de software, o grande “bam bam bam” dos modelos de desenvolvimento é o MVC (“Model-View-Controller”), que foi descrito pela primeira vez por volta de 1970 por Trygve Reenskaug que trabalhavam com o Smalltalk (Linguagem de programação orientada a objeto). Foi criado para fugir da antiga bagunça, onde o código se misturava entre a página e a persistência. Esse modelo facilita a manutenabilidade do código, pois a mudança na view (página) não afeta o “Controller” ou o “model”, e vice versa.
O Spring, como não poderia ficar de fora, foi criado baseado no Spring MVC, onde temos o @Controller (que abordaremos mais a fundo nesse artigo), @Service (facilitador para acessar os Models a partir de um framework de persistência de sua escolha ou JDBC puro) e as Views. As nossas views são JSP, pois não é apenas isso que o framework possui.
Agora, vamos nos aprofundar nos Controllers do Spring e entender o que é possível criar com eles.
Relacionado: O Que é o Spring Framework?
Requisitos
Um Dinamic Web Project configurado com as bibliotecas do Spring e rodando corretamente.
Nota: Ver referência no fim do artigo para acessar artigo com exemplos de configuração - usaremos o mesmo projeto contido no artigo.
Um Controller é responsável tanto por receber requisições como por enviar a resposta ao usuário, algo bem parecido com o Servlet do JSP. Porém, é feito de forma mais elegante e fácil. O Controller se responsabiliza por informar a View, os atributos que serão visíveis para a mesma e também por receber parâmetros vindos da View. E, por último, responder ao usuário o que foi requisitado. Veja uma exemplo na Listagem 1.
@Controller //Define que minha classe será um controller public class HomeController { }
Veja como é simples criar um Controller, mas veja que este não possui nenhum “mapping” atrelado a ele. Então criemos uma view (.jsp) chamada “home.jsp” dentro da pasta “/WEB-INF/views” e criaremos o mapping “/home” para exibir a view criada. Veja a Listagem 2.
@Controller //Define que minha classe será um controller public class HomeController { @RequestMapping("/home") //Define a url que quando for requisitada chamara o metodo public ModelAndView home(){ //Retorna a view que deve ser chamada, no caso home (home.jsp) aqui o .jsp é omitido return new ModelAndView("home"); } }
Agora sabemos como fazer uma requisição e retornar uma pagina, mas vamos supor que minha sessão home possuam várias páginas dentro dela, por exemplo /home/principal e /home/secundaria. Para isso, vamos fazer uma pequena alteração no nosso “HomeController”, conforme a Listagem 3.
@Controller //Define que minha classe será um controller @RequestMapping("/home") //Define que qualquer ação desse controler deve preceder /home public class HomeController { //Define a url que quando for requisitada chamara o metodo no caso /home/principal ou /home/ ou /home //Note que não é obrigado apenas uma url, pode-se mapear varias para o mesmo metodo @RequestMapping( value = { "/" , "" ,"/principal" }) public ModelAndView homePrincipal(){ //Retorna a view que deve ser chamada, no caso principal (principal.jsp) que esta dentro da pasta /home return new ModelAndView("home/principal"); } //Define a url que quando for requisitada chamara o metodo @RequestMapping( "/secundaria" ) public ModelAndView homeSecundaria(){ //Retorna a view que deve ser chamada, no caso secundaria (secundaria.jsp) que esta dentro da pasta /home return new ModelAndView("home/secundaria"); } }
Nota: Não necessariamente as views deverão estar contidas na pasta “/home”, é apenas uma forma de organizá-las melhor.
Passando parâmetros
Agora já entendemos melhor como funcionam as requisições e as respostas do “Controller”, agora veremos como receber parâmetros e passar atributos para a “View”. Para isso, criaremos um novo “Controller” chamado “ParamController” e seguiremos o mesmo caminho do “HomeController”, mas agora acessando o a url “/param”. Observe a Listagem 4.
@Controller @RequestMapping("/param") public class ParamController { @RequestMapping(value = { "" , "/" } ) public ModelAndView recebeParam(@RequestParam String nome , Model model){ //@RequestParam indica que recebera um parametro "nome" que é uma String //Model é um auxuliar que ajudara a adicionar atributos a nossa view model.addAttribute("nome" , nome); return new ModelAndView("param/index"); } }
Não esqueça de criar dentro de “WEB-INF/views” a pasta “param” e dentro dela a view “index.jsp”, como descrito naListagem 5.
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Controllers Spring</title> </head> <body> <h1>Param - Index</h1> <p>Parametro nome = "<%= request.getAttribute("nome") %>"</p> <p>Pagina chamada a partir do mapping em ParamController</p> </body> </html>
Explicando o que foi feito: ao mapear o @RequestParam eu estou dizendo ao método que ele deve esperar como parâmetro de nome “nome” uma String, ou seja, ao acessar “/param?nome=SeuNomeAqui” o parâmetro nome será atribuído a variável “nome”, e o “Model” se encarregará de enviá-lo como atributo a tela.
Muito fácil não? Mas espere: e se o parâmetro não for passado, o que acontecerá? Será retornado um erro: “Required String parameter 'nome' is not presente”. Para a segurança e garantia do funcionamento da aplicação isso é ótimo, mas caso não queria que o parâmetro seja obrigatório, basta mapeá-lo como “@RequestParam(required = false)”, e veja como o nome recebe null, e nenhum erro será retornado. O uso dependerá de sua necessidade.
Limitando os tipos de métodos recebidos
Agora veremos como podemos limitar um mapping para receber apenas as requisições via “POST” ou “GET”.
Nota: Não será abordado RESTful nesse artigo.
Para limitarmos um Mapping para receber apenas requisições “GET” ou “POST” adicionaremos ao nosso “ParamController” dois novos métodos (Mappings). Veja na Listagem 6.
@Controller @RequestMapping("/param") public class ParamController { @RequestMapping(value = { "" , "/" } ) public ModelAndView recebeParam(@RequestParam(required = false) String nome , Model model){ //@RequestParam indica que recebera um parametro "nome" que é uma String //Model é um auxuliar que ajudara a adicionar atributos a nossa view model.addAttribute("nome" , nome); return new ModelAndView("param/index"); } @RequestMapping(value="/postonly" , method = RequestMethod.POST) public ModelAndView postOnly(){ // o method define que tipo de requisição esse mapping vai aceitar return new ModelAndView("param/postonly"); } @RequestMapping(value="/getonly" , method = RequestMethod.GET) public ModelAndView getOnly(){ return new ModelAndView("param/getonly"); } }
Ao definir o “method” dizemos ao nosso controller que só será executado o método caso seja uma requisição do tipo especificado. Caso nenhum “method” seja definido no @RequestMapping, por default ele receberá tanto GET quanto POST. Caso tente acessar um mapping POST via GET ou vice versa, será retornado um erro:” Request method 'GET' not supported”. O recebimento de parâmetros e envio de atributos para a view continuam iguais sem nenhuma alteração.
Acesse a URL no seu servidor “param/getonly” e veja que ocorre tudo normalmente. Agora tente acessar “param/postonly” e veja o erro.
Recebendo formulários completos
Já vimos como receber parâmetros, como limitar o tipo de requisição aceita. E os formulários? Calma, não será necessária uma lista gigante de @RequestParam, a não ser que goste de escrever!
O Spring fornece uma anotação chamada @ModelAttribute que pode ser um POJO, um Domain etc... Utilizaremos um POJO (Plain Old Java Object - classe contendo apenas um construtor padrão, atributos e getters and setters) como exemplo e usaremos também a tag de formulário do Spring para nos auxiliar no envio. Criemos então um controller chamado “FormController”, como na Listagem 7.
@Controller @RequestMapping("/form") public class FormController { @RequestMapping(value = { "/" , "" } , method = RequestMethod.GET) public ModelAndView carregaForm(Model model){ model.addAttribute("form", new Form()); return new ModelAndView("form/form"); } @RequestMapping(value = { "/" , "" } , method = RequestMethod.POST) public ModelAndView recebeForm(@ModelAttribute("form") Form form, Model model){ model.addAttribute("msg", "Você enviou: " + form.getNome() + " " + form.getSobrenome() ); return new ModelAndView("form/form"); } }
Como devem ter percebido, temos dois @RequestMapping apontando para o mesmo caminho. O Spring fará a diferença de que método será chamado pelo tipo de requisição que está sendo feita. Veja também que está sendo adicionado a nossa view um objeto ”Form”. Criaremos agora o nosso objeto Form, como na Listagem 8.
public class Form { private String nome; private String sobrenome; public String getNome() { return nome; } public void setNome(String nome) { this.nome = nome; } public String getSobrenome() { return sobrenome; } public void setSobrenome(String sobrenome) { this.sobrenome = sobrenome; } }
Agora criaremos a nossa view contendo o formulário. Para isso, adicionaremos uma jsp em “/WEB-INF/views/form/” com o nome “form.jsp”. Veja a Listagem 9.
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Controllers Spring</title> </head> <body> <jsp:include page="/WEB-INF/views/_menu.jsp" /> <h1>Form</h1> <p>Pagina chamada a partir do mapping em FormController</p> <form:form id="frmForm" action="" method="post" modelAttribute="form"> <div> <form:label path="nome" cssStyle="width:80px; text-align:right;display:inline-block">Nome:</form:label> <form:input path="nome"/> </div> <div> <form:label path="sobrenome" cssStyle="width:80px; text-align:right;display:inline-block">Sobrenome:</form:label> <form:input path="sobrenome"/> </div> <form:button>Enviar</form:button> </form:form> <%= (request.getAttribute("msg") != null) ? request.getAttribute("msg") : "" %> </body> </html>
Veja que importamos a taglib “form” do Spring. Ela irá nos auxiliar a ler um objeto que tenha sido passado pelo “Controller”. Vamos dissecar o nosso formulário:
<form:form id="frmForm" action="" method="post" modelAttribute="form">
Perceba o atributo “modelAttribute=’form’”, com ele dizemos ao nosso formulário com que objeto estamos trabalhando, no casso o objeto “form” que foi adicionado ao chamar a view.
<form:label path="nome" cssStyle="width:80px; text-align:right;display:inline-block">Nome:</form:label>
A tag form:label criará um label para o atributo “nome”, como foi especificado através de “path”. “cssStyle” é o mesmo que o “style” do HTML e foi adicionado apenas para deixar um pouco mais apresentável o formulário.
<form:input path="nome"/>
A tag “form:input” criará um campo para o atributo “nome” do nosso objeto “form”.
<form:button>Enviar</form:button>
A tag “form:button” criará um botão de envio do formulário.
Agora com tudo pronto rode sua aplicação, acesse a página do formulário e veja o resultado na Figura 1.
E a Figura 2 mostra após o envio do formulário.
Veja que o formulário já vem preenchido, o Spring faz isso por você. Note também que tudo que foi enviado ao formulário será inserido no @ModelAttribute do nosso método “recebeForm()”e no nosso Controller.
Concluindo
O Spring nos fornece várias ferramentas de fácil uso e de rápido aprendizado. Veja que é um framework bastante rápido e sem muito peso para o nosso servidor.
Até a próxima.
Links Úteis
Saiba mais sobre Spring ;)
-
Introdução ao Spring Framework:
Veja nesse artigo uma introdução ao Spring Framework, um dos mais utilizados em projetos na linguagem Java. Conheça um pouco sobre o framework e implemente um exemplo de Hello World. -
Spring MVC: Construa aplicações responsivas com
Bootstrap:
Desenvolva aplicações responsivas integrando o framework web da Spring com o Bootstrap, uma das bibliotecas de front-end mais conhecidas. -
Persistência de dados: JPA ou Spring Data JPA?:
Conheça as diferenças entre a especificação JPA e o módulo Spring Data JPA e facilite o seu dia a dia na implementação de funcionalidades relacionadas à persistência de dados.