Fagno Alves Fonseca <[email protected]> Mestre em Modelagem Computacional de Sistemas – UFT.
O Spring é um framework open source para a plataforma Java. É composto de um conjunto de projetos independentes, da configuração à segurança, aplicativos da web a big data, quaisquer que sejam as necessidades de infraestrutura de seu aplicativo, há um Projeto Spring para ajudá-lo a construí-lo leia mais.
Principais projetos:
O Spring Framework: fornece um modelo abrangente de programação e configuração para aplicativos empresariais modernos baseados em Java, permite que o projeto possa rodar na web.
Spring Data: fornece um modelo de programação baseado em Spring familiar e consistente para acesso a dados, enquanto ainda retém as características especiais do armazenamento de dados subjacente.
Spring Security: é uma estrutura de autenticação e controle de acesso poderosa e altamente personalizável. É o padrão de fato para proteger aplicativos baseados em Spring. Concentra em fornecer autenticação e autorização para aplicativos Java.
Spring Boot: facilita a criação de aplicativos baseados em Spring autônomos e de nível de produção que você pode "simplesmente executar". Temos uma visão opinativa da plataforma Spring e das bibliotecas de terceiros para que você possa começar com o mínimo de confusão. A maioria dos aplicativos Spring Boot precisa de configuração mínima do Spring.
O Spring MVC é um framework Java, moderno para desenvolvimento Web que usa recursos atuais da linguagem, com características para simplificar e fornecer maior produtividade ao desenvolvimento de aplicações web, utilizando o padrão arquitetural Model-View-Controller.
É um padrão criado com o objetivo de organizar a arquitetura de aplicações web em camadas com responsabilidades bem definidas.
MODEL: camada que representa as classes do domínio da aplicação, ou seja, a persistência de dados, validação e regras de negócio;
VIEW: camada que representa as views (interface) da nossa aplicação;
CONTROLLER: camada que controla a comunicação (request/response) entre as camadas Model e View.
Temos algumas maneiras de configurar o Spring em nosso projeto web, seja por arquivos de configuração XML, ou via código Java, ou utilizando o Spring Boot.
Em nossas aulas iremos utilizar o Spring Boot para criar nossos projetos web utilizando o Spring.
Para criar nosso projeto com o Spring Boot, vamos utilizar a ferramenta web chamada Spring Initializr. Nesta ferramenta, definimos as dependências do nosso projeto e ela gera um arquivo com toda a estrutura básica de uma aplicação Spring, pronta para ser executada.
No Spring Quickstart Guide você pode ver como criar um exemplo prático de um projeto web com Spring.
Ao criar um projeto com Sring Boot as dependências inseridas no projeto não possuem versão definida, ou seja, todas as demais dependências irão seguir a mesma referência definida na dependencia starter-parent conforme a seguir e disponível no seu arquivo pom.xml.
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
<relativePath/>
</parent>
Dependências terminadas com starter informa que essa dependência agrupa outras, tornando essa dependência única, pois a mesma, representa todas as demais dependências necessária ao uso do recurso. Como beneficio, torna o pom.xml menor.
A seguir temos a dependência necessária para que possamos criar um projeto Spring Web (Build web, including RESTful, applications using Spring MVC). Esta dependência deve ser adicionada ao criar o projeto no Spring Boot.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Important
|
O Spring adiciona ao projeto o Apache Tomcat como contêiner padrão. |
Controller são classes contendo a lógica de negócio do seu sistema. É responsável por receber todas as requisições do usuário. Uma camada intermediária entre a camada de apresentação e a lógica.
Antes de criar nosso controller, vamos definir uma entidade para representar uma pessoa.
public class Pessoa{
private Long id;
private String nome;
//getters e setters
}
Após definir a classe Pessoa, vamos criar uma nova classe para representar o Controller e tratar as requisições web. Essa classe vai ser o Controller de pessoas.
A nova classe deve ser definida com o nome PessoasController. Para indicar ao Spring MVC que uma classe é um Controller, adicione sobre ela a anotação @Controller
conforme o exemplo a seguir.
import org.springframework.stereotype.Controller;
@Controller
public class PessoasController{
...
}
Anotando seu controller com @Controller
, todos os seus métodos públicos serão acessíveis pela web. No exemplo a seguir, uma requisição à URI "http://localhost:8080/pessoas/exemplo" redirecionará para o método exemplo().
@Controller
@RequestMapping("pessoas")
public class PessoasController {
@ResponseBody
@GetMapping("/exemplo")
public String exemplo(){
return "Controller de Pessoas!";
}
}
Com a anotação @RequestMapping("pessoas")
, caso uma requisição seja enviada para localhost:8080/pessoas, essa requisição será tratada por um dos métodos a serem implementados neste Controller.
A anotação @ResponseBody
diz ao Spring MVC para jogar o retorno do método na resposta.
A anotação @GetMapping
indica ao Spring MVC que requisições HTTP do tipo GET enviadas para localhost:8080/pessoas/exemplo devem ser atendidas pelo método exemplo().
No exemplo a seguir, utilizamos um DAO para operações de CRUD aos dados das pessoas no banco. Iremos utilizar uma conexão JDBC, pois nosso objetivo nete capítulo é conhecer as funcionalidades do Spring.
public class PessoaDAO {
//criar um objeto Connection para receber a conexão
Connection con;
public PessoaDAO(){
con = MinhaConexao.conexao();
}
public List<Pessoa> buscarPessoas() {
try {
//comando sql
String sql = "select * from tb_pessoa";
PreparedStatement ps = con.prepareStatement(sql);
//ResultSet, representa o resultado do comando SQL
ResultSet rs = ps.executeQuery();
//cria uma lista de pessoas para retornar
List<Pessoa> pessoas = new ArrayList();
//laço para buscar todas as pessoas do banco
while (rs.next()) {
Pessoa p = new Pessoa();
p.setId(rs.getInt("id"));
p.setNome(rs.getString("nome"));
//add pessoa na lista
pessoas.add(p);
}
//retorna a lista de pessoas
return pessoas;
} catch (SQLException ex) {
Logger.getLogger(PessoaDAO.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
public boolean remove(Long id) {
try {
//comando sql
String sql = "delete from tb_pessoa where id = ?";
PreparedStatement ps = con.prepareStatement(sql);
//referênciar o parâmetro do método para a ?
ps.setLong(1, id);
if(ps.executeUpdate()==1)
return true;
} catch (SQLException ex) {
Logger.getLogger(PessoaDAO.class.getName()).log(Level.SEVERE, null, ex);
}
return false;
}
public boolean save(Pessoa pessoa) {
try {
//comando sql
String sql = "insert into tb_pessoa (nome) values (?)";
PreparedStatement ps = con.prepareStatement(sql);
//referênciar o parâmetro do método para a ?
ps.setString(1, pessoa.getNome());
if(ps.executeUpdate()==1)
return true;
} catch (SQLException ex) {
Logger.getLogger(PessoaDAO.class.getName()).log(Level.SEVERE, null, ex);
}
return false;
}
public boolean update(Pessoa pessoa) {
try {
//comando sql
String sql = "update tb_pessoa set nome=? where id=?";
PreparedStatement ps = con.prepareStatement(sql);
//referênciar o parâmetro do método para a ?
ps.setString(1, pessoa.getNome());
ps.setLong(2, pessoa.getId());
if (ps.executeUpdate()==1)
return true;
} catch (SQLException ex) {
Logger.getLogger(PessoaDAO.class.getName()).log(Level.SEVERE, null, ex);
}
return false;
}
public Pessoa buscarPessoa(Long id) {
try {
//comando sql
String sql = "select * from tb_pessoa where id = ?";
PreparedStatement ps = con.prepareStatement(sql);
//referênciar o parâmetro do método para a ?
ps.setLong(1, id);
//ResultSet, representa o resultado do comando SQL
ResultSet rs = ps.executeQuery();
if (rs.next()) {
Pessoa p = new Pessoa();
p.setId(rs.getInt("id"));
p.setNome(rs.getString("nome"));
return p;
}
} catch (SQLException ex) {
Logger.getLogger(PessoaDAO.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
}
Para efetuar a conexão com o banco de dados, vamos utilizar a interface e classes definidas a seguir.
...
import java.sql.Connection;
public interface ConexaoJDBC{
public Connection criarConexao();
}
A seguir temos as classes para conexão com banco Mysql e Postgres. Utilize a classe do banco de dados de sua escolha.
...
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;
public class ConexaoMysql implements ConexaoJDBC{
public static void main(String[] args) {
//testar conexão
System.out.println(new ConexaoMysql().criarConexao());
}
/**
* método que vai retornar uma conexão
* @return
*/
@Override
public Connection criarConexao(){
try {
//carregar o driver de conexão
Class.forName("com.mysql.cj.jdbc.Driver");
//parâmetros
String url = "jdbc:mysql://localhost:3306/teste";
String usuario = "root";
String senha = "senha";
//retorna a conexão com o banco de dados
return DriverManager.getConnection(url, usuario, senha);
} catch (ClassNotFoundException | SQLException ex) {
Logger.getLogger(ConexaoMysql.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
}
...
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;
public class ConexaoPostgre implements ConexaoJDBC{
public static void main(String[] args) {
//testar conexão
System.out.println(new ConexaoPostgre().criarConexao());
}
/**
* método que vai retornar uma conexão
* @return
*/
@Override
public Connection criarConexao(){
try {
//carregar o driver de conexão
Class.forName("org.postgresql.Driver");
//parâmetros
String url = "jdbc:postgresql://localhost:5432/teste";
String usuario = "postgres";
String senha = "senha";
//retorna a conexão com o banco de dados
return DriverManager.getConnection(url, usuario, senha);
} catch (ClassNotFoundException | SQLException ex) {
Logger.getLogger(ConexaoPostgre.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
}
...
import java.sql.Connection;
public class MinhaConexao{
public static Connection conexao(){
ConexaoJDBC conexao = new ConexaoMysql();
return conexao.criarConexao();
}
}
O H2 é um sistema de gerenciamento de banco de dados relacional escrito em Java.
O H2 possui suporte a bancos de dados persistentes e na memória, não tendo limite para o número de bancos de dados abertos simultaneamente ou para o número de conexões abertas.
Para criar um banco de dados em mémoria, adicione as propriedades a seguir no seu arquivo application.properties
.
##H2database
spring.datasource.url=jdbc:h2:mem:dbname
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=user
spring.datasource.password=password
Por fim, incluir no diretório resources
do seu projeto o arquivo data.sql
. Neste arquivo, você deve incluir comandos para criar tabelas e fazer inserções de dados.
Quando rodarmos o projeto com este arquivo no classpath, o Spring irá usá-lo para preencher o banco de dados.
Você pode habilitar o console GUI (interface gráfica do utilizador) embutido para navegar pelo conteúdo do banco de dados e executar consultas SQL.
Para acessar o console utilize o endereço http://localhost:8080/h2-console.
#h2 console
spring.h2.console.enabled=true
Necessário incluir a depência do JPA a seguir.
<!-- Persist data in SQL stores with Java Persistence API using Spring Data and Hibernate.-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
Com nossa classe PessoaDAO pronta podemos definir as operações necessárias no controlador com todas as operações de CRUD para Pessoa.
@Controller
@RequestMapping("pessoas")
public class PessoasController {
PessoaDAO dao;
public PessoasController(){
dao = new PessoaDAO();
}
/**
* @param pessoa necessário devido utilizar no form.html o th:object que faz referência ao objeto esperado no controller.
* @return
*/
@GetMapping("/form")
public String form(Pessoa pessoa){
return "/pessoas/form";
}
@GetMapping("/list")
public ModelAndView listar(ModelMap model) {
model.addAttribute("pessoas", dao.buscarPessoas());
return new ModelAndView("/pessoas/list", model);
}
@PostMapping("/save")
public ModelAndView save(Pessoa pessoa){
dao.save(pessoa);
return new ModelAndView("redirect:/pessoas/list");
}
/**
* @param id
* @return
* @PathVariable é utilizado quando o valor da variável é passada diretamente na URL
*/
@GetMapping("/remove/{id}")
public ModelAndView remove(@PathVariable("id") Long id){
dao.remove(id);
return new ModelAndView("redirect:/pessoas/list");
}
/**
* @param id
* @return
* @PathVariable é utilizado quando o valor da variável é passada diretamente na URL
*/
@GetMapping("/edit/{id}")
public ModelAndView edit(@PathVariable("id") Long id, ModelMap model) {
model.addAttribute("pessoa", dao.buscarPessoa(id));
return new ModelAndView("/pessoas/form", model);
}
@PostMapping("/update")
public ModelAndView update(Pessoa pessoa) {
dao.update(pessoa);
return new ModelAndView("redirect:/pessoas/list");
}
}
Com o método form(), atendemos requisições do tipo GET enviadas para /pessoas/form, que tambem carrega a página form.html através do returno do método. Declaramos um parâmetro do tipo Pessoa, necessário devido utilizar no form.html o th:object que faz referência ao objeto esperado no controller.
Para buscar as pessoas do banco de dados, temos uma intância de PessoaDAO. Através desta instância temos acesso ao método buscarPessoas(), que retorna a lista de pessoas do banco.
O @param model
representa a implementação de um map feita pelo Spring. O Spring nos fornecerá um objeto desse tipo já instanciado.
Através deste objeto, temos acesso ao método addAttribute(), que usamos para enviar dados para a view, adicionando no map o atributo que desejamos enviar.
No return de alguns métodos, retornamos um objeto do tipo ModelAndView. Na instanciação desse objeto passamos por parâmetros o model no qual acabamos de adicionar e uma string, que indica a página a ser exibida. Com o retorno do ModelAndView, o Controller enviar as informações para a view, que carrega a página list.html com os dados enviados pelo model, atendendo requisições do tipo GET enviadas para /pessoas/list.
Observe, que nossa view foi definida em src/main/resources dentro do pacote templates
. Foi definido um novo pacote pessoas
para representar as view de Pessoas.
Para visualizar a lista de pessoas através da requisição à URI "http://localhost:8080/pessoas/list" você deve definiar seu arquivo lista.html conforme a seguir.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>TODO supply a title</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<table class="table" border="1">
<thead>
<tr>
<th>Id</th>
<th>Descrição</th>
</tr>
</thead>
<tr th:each="p : ${pessoas}">
<td th:text="${p.id}"> </td>
<td th:text="${p.nome}"> </td>
<td> <a th:href="@{/pessoas/edit/{id}(id=${p.id})}">Editar</a> </td>
<td> <a onclick="return window.confirm('Tem certeza que deseja excluir este registro?')" th:href="@{/pessoas/remove/{id}(id=${p.id})}">Excluir</a> </td>
</tr>
</table>
</body>
</html>
Na tag raiz do html adicionamos o namespace do Thymeleaf xmlns:th="http://www.thymeleaf.org"
, para que possamos ter acesso aos componentes do Thymeleaf.
Para utilizar o Thymeleaf no seu projeto é necessário adicionar a dependência do mesmo, então basta incluir no seu arquivo pom.xml a depência a seguir, caso ainda não tenha feito.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
A instruçẽo th:each
define um foreach a partir da lista de pessoas enviada pelo nosso controller. Já a th:text
exibe os dados obtidos por cada item da lista.
Para atualizar os dados de uma pessoa, utilizamos o mesmo form.html, através do método edit() carregamos os dados do objeto a ser editado na view. E utilizando o método update() para efetuar as alterações no banco. Observe no código a seguir como definir no form.html.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>TODO supply a title</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<h1>FORM</h1>
<form th:action="${pessoa.id == null} ? @{/pessoas/save} : @{/pessoas/update}" th:object="${pessoa}" method="post">
<input type="hidden" th:field="*{id}">
<input type="text" th:field="*{nome}" placeholder="Nome">
<input type="submit" value="Enviar">
</form>
</body>
</html>
Observe que temos uma condicional que verifica se o id
do objeto Pessoa é null. Desta forma, identificamos qual ação o th:action
deve executar no submit.