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.

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.

Listagem 1: Criando um controller


@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.

Listagem 2: Criando um RequestMapping para HomeController


@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.

Listagem 3: Modificando o HomeController


@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.

Listagem 4: Criando e Mapeando o ParamController


@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.

Listagem 5: Criando a view param/index.jsp


<%@ 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.

Listagem 6: Adicionando limitações GET e POST


@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.

Listagem 7: FormController


@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.

Listagem 8: POJO Form


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.

Listagem 9: View form.jsp


<%@ 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:

Listagem 10: Tag form:form

 <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.

Listagem 11: Tag form:label

 <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.

Listagem 12: Tag form:input

 <form:input path="nome"/> 

A tag “form:input” criará um campo para o atributo “nome” do nosso objeto “form”.

Listagem 13: Tag form:button

 <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.

Página de formulário antes do envio

Figura 1: Página de formulário antes do envio

E a Figura 2 mostra após o envio do formulário.

Página de formulário após o envio

Figura 2: Página de formulário após o envio

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 ;)