Apostila SpringMVC-Springboot
Apostila SpringMVC-Springboot
Apostila SpringMVC-Springboot
Teorico/Prático
Sumário
O que é Spring Boot?..................................................................................................................... 3
Objetivos básicosdo Spring Boot ........................................................................................... 3
Principais recursos do Spring Boot ........................................................................................ 3
Estrutura de projeto padrão para projetos Spring Boot ............................................................... 7
Não use o “default” package ................................................................................................. 7
Layout Típico ......................................................................................................................... 7
Estrutura do Projeto - Primeira abordagem.......................................................................... 8
Conteúdo Estático (static content)........................................................................................ 9
Conteúdo web dinâmico (templates).................................................................................. 10
Estrutura do Projeto - Segunda abordagem ....................................................................... 11
pom.xml............................................................................................................................... 11
O que é o Spring Boot Starter Parent e como usá-lo? ........................................................ 12
Aplicativos web spring boot - usando JSP como visualizações ........................................... 13
Arquitetura do projeto Spring Boot ............................................................................................ 14
Arquitetura de três camadas (três camadas) ...................................................................... 15
Arquitetura Spring Boot Flow (exemplo) ............................................................................ 16
Java Spring Boot & Eclipse .......................................................................................................... 19
Pré-Requisitos ..................................................................................................................... 19
Instalando o Eclipse ............................................................................................................. 19
Spring Tools ......................................................................................................................... 25
Criando o projeto ................................................................................................................ 25
Criando projeto Spring Boot com Spring Initializer ..................................................................... 33
Web Service Spring Restful ......................................................................................................... 38
Estrutura do Diretório do Projeto ....................................................................................... 40
Springboot Annotations .............................................................................................................. 48
Projeto API de upload / download de arquivo de springboot .................................................... 53
Escrevendo APIs para upload e download de arquivos .......................................... 58
Spring Boot Thymeleaf CRUD ...................................................................................................... 77
Etapas de Desenvolvimento ................................................................................................ 78
Construindo um aplicativo de chat com Spring Boot e WebSocket ........................................... 90
Projeto Spring Boot integrado com Angular 9 .......................................................................... 116
Arquitetura do projeto ...................................................................................................... 117
Projeto Spring Boot com Angular eAPI Rest MongoDB ............................................................ 131
Criando back-end Spring Boot........................................................................................... 132
Apendice.................................................................................................................................... 152
Referencias: ............................................................................................................................... 153
3
Requisitos de sistema
Servlet Containers
• @ComponentScan
• @EntityScan
• @SpringBootApplication
Layout Típico
A equipe springboot geralmente recomenda que possamos localizar a
classe de aplicativo principal em um pacote raiz acima de outras
classes. A annotation @SpringBootApplication é freqüentemente colocada em
sua classe principal e define implicitamente um “pacote de pesquisa” básico para
certos itens. Por exemplo, se você estiver escrevendo um aplicativo JPA, o
pacote da classe anotada @SpringBootApplication é usado para
8
import org.springframework.boot.SpringApplication ;
import
org.springframework.boot.autoconfigure.SpringBootApplication ;
@SpringBootApplication public class Application {
public static void main ( String [] args ) {
SpringApplication . run ( Application . class,
args);
}
}
• /static
• /public
• /resources
• / META-INF / resouces
spring.mvc.static-path-pattern = /resources/**
• FreeMarker - spring-boot-starter-freemarker
• Groovy - spring-boot-starter-groovy
• Thymeleaf - spring-boot-starter-thymeleaf
• Mustache - spring-boot-starter-mustache
pom.xml
Em um projeto spring boot, a maioria dos módulos pode ser habilitada ou
desabilitada apenas adicionando um conjunto de iniciadores. Todos os projetos
Spring Boot normalmente usam spring-boot-starter-parent como o pai
no arqruivo pom.xml .
12
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.3.RELEASE</version>
</parent>
Por exemplo:
Conclusão
Pré-Requisitos
Java JDK 8 ou superior
Instalando o Eclipse
O site oficial do IDE Eclipse é www.eclipse.org, e na página de downloads
podemos baixar a versão mais recente do IDE. Para preparar seu computador
para desenvolviemnto em Java, você precisará do JDK e de um ambiente de
desenvolvimento integrado, como o Eclipse (ou outro, como o NetBeans).
Vejamos os procedimentos necessários para instalar o Eclipse:
1 – Baixar e instalar o JDK (Java Development Kit) para que seja possível
desenvolver aplicações em Java. Se você quiser usar o Eclipse para desenvolver
em outras linguagens que não o Java, basta baixar e instalar o JRE (Java
Runtime Environment) para que seja possível rodar o Eclipse, pois ele é escrito
em Java também. A versão necessária é a 1.7.0 ou mais recente para rodar o
Eclipse.
http://www.eclipse.org/downloads/
Na tela que se abrirá você deve escolher a primeira opção, “Eclipse IDE for
Java Developers“, pois nosso objetivo é usar o IDE para aprender programação
em Java.
21
Será peguntado onde seus projetos deverão ser armazenados. Para isso,
o Eclipse cria uma pasta especial denominada Workspace. Escolha um local no
seu disco, ou mantenha o padrão sugerido (dentro de seu diretório pessoal),
marque a caixa “Use this as the default and do not ask again” para tornar a pasta
padrão em futuros projetos e clique em OK:
Você verá a tela de boas-vindas do Eclipse IDE for Java, onde há links para
tutoriais, exemplos e outras informações sobre o software. Feche essa aba
clicando no “x” ao lado da palavra Welcome, no lado superior esquerdo:
24
Vale lembrar que também é altamente válido instalar o Spring Tool Suite (STS) ao
invés do eclipse puro. O STS é basicamente o eclipse configurado com o plugin spring
tools, otimizado para desenvolvimento com o spring framework
Configurando o eclipse
Spring Tools
Para ter uma melhor experiência com desenvolvimento Spring, recomendo
instalar o Spring Tools. Vá no menu Help > Eclipse Marketplace… e procure por
spring:
Instale a versão mais atual do Spring Tools (na data de escrita deste guia
é a 4.2.1) e reinicie o eclipse.
Criando o projeto
Com tudo configurado corretamente, hora de criarmos o projeto. Vá no
menu: File > New > Project… e selecione a opção Spring Starter Project que
está localizada abaixo do menu Spring Boot, conforme imagem abaixo:
26
Iniciando o projeto
Utilize o código abaixo para criar o endpoint que irá retornar "Hello World!":
package com.example.demo.controllers;
import org.springframework.http.ResponseEntity;
import
org.springframework.web.bind.annotation.GetMapping;
import
org.springframework.web.bind.annotation.RequestMapping;
import
org.springframework.web.bind.annotation.RestController;
/**
* ExampleController
*
32
*
*/
@RestController
@RequestMapping("/api/example")
public class ExampleController {
@GetMapping("/hello-world")
public ResponseEntity<String> get() {
return ResponseEntity.ok("Hello World!");
}
}
Após clicar no botão de executar, você deverá ver a saída do log de inicialização
Vamos criar um projeto simples Spring boot – seu nome será hello world
- usando a ferramenta Spring Initializr e importar no Eclipse IDE.
• Project: Maven
• Language: Java
• Spring Boot: 2.3.3
• Java: 8
• Group: com.example
• Artfact: demo
• Name: demo
• Description: Demo Project for Spring Boot
• Package name: com.example.demo
• package: jar (este é o valor padrão)
• Dependencies: Web, JPA, MySQL
Depois de clicar em Finish, o Maven levará algum tempo para baixar todas as
dependências e inicializar o projeto.
$ java -version
java version "1.8.0_102"
Java(TM) SE Runtime Environment (build 1.8.0_102-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.102-b14,
mixed mode)
39
$ mvn -v
Apache Maven 3.3.9
(bb52d8502b132ec0a5a3f4c09453c07478323dc5; 2015-11-
10T16:41:47+00:00)
Maven home: /usr/local/Cellar/maven/3.3.9/libexec
Java version: 1.8.0_102, vendor: Oracle Corporation
http://localhost:8080/greeting
{"id":1,"content":"Hello, World!"}
http://localhost:8080/greeting?name=User
{"id":1,"content":"Hello, User!"}
• Project: Maven
• Language: Java
• Spring Boot: 2.3.4
• Java: 8
• Group: com.example.
• Artfact: helloworld
• Name: helloworld
• Description: Demo Project for Spring Boot
• Package name: com.example.helloworld
• package: jar (este é o valor padrão)
• Dependencies: Web, JPA, MySQL
O arquivo pom.xml
<groupId> com.example.helloworld</groupId>
<artifactId> helloworld</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>helloworld<name>
<description>Demo project for Spring
Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-
parent</artifactId>
<version> 2.3.4.RELEASE</version>
<relativePath/> <!-- lookup parent from
repository -->
</parent>
42
<properties>
<project.build.sourceEncoding>UTF-
8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-
8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-
web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-
test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-
plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
spring-boot-starter-parent
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-
parent</artifactId>
<version> 2.3.4.RELEASE</version>
</parent>
Parent Poms permitem que você gerencie as seguintes coisas para vários
projetos e módulos filhos:
{
" id " : 1 ,
" content " : " Hello, World! "
}
import java.util.concurrent.atomic.AtomicLong;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class GreetingController {
@RequestMapping("/greeting")
public Greeting greeting(@RequestParam(value = "name",
defaultValue = "World") String name) {
return new Greeting(counter.incrementAndGet(),
String.format(template, name));
}
}
import org.springframework.boot.SpringApplication;
import
org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class HelloworldApplication {
SpringApplication.run(SpringbootHelloworldApplication.class, args);
}
}
Executando o aplicativo
$ mvn spring-boot:run
Teste o serviço
Agora que o serviço está ativo, visite http: // localhost: 8080 / greeting ,
onde você verá:
48
Resumo
Springboot Annotations
1. @SpringBootApplication
import org.springframework.boot.SpringApplication ;
import
org.springframework.boot.autoconfigure.SpringBootApplication ;
2. @EnableAutoConfiguration
import org.springframework.boot.SpringApplication ;
import
org.springframework.boot.autoconfigure.EnableAutoConfiguration ;
@EnableAutoConfiguration
public class Application {
public static void main ( String [] args ) {
SpringApplication . run ( Application . class, args);
}
}
3. @ConditionalOnClass e @ConditionalOnMissingClass
@Configuration
@ConditionalOnClass ( DataSource . Class)
class OracleAutoconfiguration {
// ...
}
4. @ConditionalOnBean e @ConditionalOnMissingBean
Os
annotations @ConditionalOnBean e @ConditionalOnMissingBean deixam de
incluir um bean com base na presença ou ausência de bean específicos.
@Bean
@ConditionalOnBean ( name = " dataSource " )
LocalContainerEntityManagerFactoryBean entityManagerFactory ()
{
// ...
}
@Configuration
public class MyAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public MyService myService () { ... }
}
5. @ConditionalOnProperty
52
@Bean
@ConditionalOnProperty (
name = " usemysql " ,
havingValue = " local "
)
DataSource dataSource () {
// ...
}
6. @ConditionalOnResource
7. @ConditionalOnWebApplication e @ConditionalOnNotWebApplication
Os
annotations @ConditionalOnWebApplication e @ConditionalOnNotWebAppl
ication deixar de configuração ser incluídos dependendo se o aplicativo é uma
“aplicação web”. Um aplicativo da web é um aplicativo que usa um
Spring WebApplicationContext , define um escopo de sessão ou tem
um StandardServletEnvironment .
Código de exemplo de
annotation @ConditionalOnWebApplication : com essas annotations,
53
@ConditionalOnWebApplication
HealthCheckController healthCheckController () {
// ...
}
8. @ConditionalExpression
@Bean
@ConditionalOnExpression ( " $ {usemysql} && $ {mysqlserver ==
'local'} " )
DataSource dataSource () {
// ...
}
9. @Conditional
Criando o aplicativo
• Abra http://start.spring.io
• Insira o arquivo-demo no campo “Artefato”.
• Adicione Web na seção de dependências.
• Clique em Gerar para gerar e baixar o projeto.
## MULTIPART (MultipartProperties)
spring.servlet.multipart.enabled=true
spring.servlet.multipart.file-size-threshold=2KB
spring.servlet.multipart.max-file-size=200MB
spring.servlet.multipart.max-request-size=215MB
file.upload-dir=/Users/callicoder/uploads
package com.example.filedemo.property;
import
org.springframework.boot.context.properties.ConfigurationPr
operties;
@ConfigurationProperties(prefix = "file")
return uploadDir;
this.uploadDir = uploadDir;
}
57
Abra a classe
principal src/main/java/com/example/filedemo/FileDemoApplicat
package com.example.filedemo;
import com.example.filedemo.property.FileStorageProperties;
import org.springframework.boot.SpringApplication;
import
org.springframework.boot.autoconfigure.SpringBootApplication;
import
org.springframework.boot.context.properties.EnableConfigurationPropert
ies;
@SpringBootApplication
@EnableConfigurationProperties({
FileStorageProperties.class
})
SpringApplication.run(FileDemoApplication.class, args);
com.example.filedemo.controller
package com.example.filedemo.controller;
import com.example.filedemo.payload.UploadFileResponse;
import com.example.filedemo.service.FileStorageService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import
org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
59
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@RestController
@Autowired
@PostMapping("/uploadFile")
String fileDownloadUri =
ServletUriComponentsBuilder.fromCurrentContextPath()
.path("/downloadFile/")
.path(fileName)
.toUriString();
file.getContentType(), file.getSize());
@PostMapping("/uploadMultipleFiles")
public List<UploadFileResponse>
uploadMultipleFiles(@RequestParam("files") MultipartFile[] files) {
return Arrays.asList(files)
.stream()
60
.collect(Collectors.toList());
@GetMapping("/downloadFile/{fileName:.+}")
Resource resource =
fileStorageService.loadFileAsResource(fileName);
try {
contentType =
request.getServletContext().getMimeType(resource.getFile().getAbsolute
Path());
if(contentType == null) {
contentType = "application/octet-stream";
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType(contentType))
.header(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + resource.getFilename() + "\"")
.body(resource);
}
61
UploadFileResponse
package com.example.filedemo.payload;
this.fileName = fileName;
this.fileDownloadUri = fileDownloadUri;
this.fileType = fileType;
this.size = size;
}
62
package com.example.filedemo.service;
import com.example.filedemo.exception.FileStorageException;
import com.example.filedemo.exception.MyFileNotFoundException;
import com.example.filedemo.property.FileStorageProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
@Service
@Autowired
public FileStorageService(FileStorageProperties
fileStorageProperties) {
this.fileStorageLocation =
Paths.get(fileStorageProperties.getUploadDir())
.toAbsolutePath().normalize();
try {
Files.createDirectories(this.fileStorageLocation);
String fileName =
StringUtils.cleanPath(file.getOriginalFilename());
try {
if(fileName.contains("..")) {
Path targetLocation =
this.fileStorageLocation.resolve(fileName);
Files.copy(file.getInputStream(), targetLocation,
StandardCopyOption.REPLACE_EXISTING);
64
return fileName;
try {
Path filePath =
this.fileStorageLocation.resolve(fileName).normalize();
if(resource.exists()) {
return resource;
} else {
1. FileStorageException
65
package com.example.filedemo.exception;
super(message);
super(message, cause);
2. MyFileNotFoundException
package com.example.filedemo.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(HttpStatus.NOT_FOUND)
super(message);
}
66
super(message, cause);
o Spring boot responda com um status 404 Not Found quando essa exceção
for lançada.
Desenvolvendo o Front-End
static
└── css
└── main.css
└── js
└── main.js
└── index.html
O template HTML
<!DOCTYPE html>
<html>
<head>
67
</head>
<body>
<noscript>
</noscript>
<div class="upload-container">
<div class="upload-header">
</div>
<div class="upload-content">
<div class="single-upload">
<form id="singleUploadForm"
name="singleUploadForm">
<input id="singleFileUploadInput"
type="file" name="file" class="file-input" required />
</form>
<div class="upload-response">
<div id="singleFileUploadError"></div>
<div id="singleFileUploadSuccess"></div>
</div>
</div>
<div class="multiple-upload">
<form id="multipleUploadForm"
name="multipleUploadForm">
68
<input id="multipleFileUploadInput"
type="file" name="files" class="file-input" multiple required />
</form>
<div class="upload-response">
<div id="multipleFileUploadError"></div>
<div
id="multipleFileUploadSuccess"></div>
</div>
</div>
</div>
</div>
</body>
</html>
O arquivo Javascript
'use strict';
var singleUploadForm =
document.querySelector('#singleUploadForm');
var singleFileUploadInput =
document.querySelector('#singleFileUploadInput');
var singleFileUploadError =
document.querySelector('#singleFileUploadError');
var singleFileUploadSuccess =
document.querySelector('#singleFileUploadSuccess');
var multipleUploadForm =
document.querySelector('#multipleUploadForm');
var multipleFileUploadInput =
document.querySelector('#multipleFileUploadInput');
var multipleFileUploadError =
document.querySelector('#multipleFileUploadError');
69
var multipleFileUploadSuccess =
document.querySelector('#multipleFileUploadSuccess');
function uploadSingleFile(file) {
formData.append("file", file);
xhr.open("POST", "/uploadFile");
xhr.onload = function() {
console.log(xhr.responseText);
if(xhr.status == 200) {
singleFileUploadError.style.display = "none";
singleFileUploadSuccess.innerHTML = "<p>File
Uploaded Successfully.</p><p>DownloadUrl : <a href='" +
response.fileDownloadUri + "' target='_blank'>" +
response.fileDownloadUri + "</a></p>";
singleFileUploadSuccess.style.display = "block";
} else {
singleFileUploadSuccess.style.display = "none";
xhr.send(formData);
function uploadMultipleFiles(files) {
formData.append("files", files[index]);
70
xhr.open("POST", "/uploadMultipleFiles");
xhr.onload = function() {
console.log(xhr.responseText);
if(xhr.status == 200) {
multipleFileUploadError.style.display = "none";
multipleFileUploadSuccess.innerHTML = content;
multipleFileUploadSuccess.style.display = "block";
} else {
multipleFileUploadSuccess.style.display = "none";
xhr.send(formData);
singleUploadForm.addEventListener('submit', function(event){
if(files.length === 0) {
71
singleFileUploadError.style.display = "block";
uploadSingleFile(files[0]);
event.preventDefault();
}, true);
multipleUploadForm.addEventListener('submit', function(event){
if(files.length === 0) {
multipleFileUploadError.style.display = "block";
uploadMultipleFiles(files);
event.preventDefault();
}, true);
O arquivo CSS
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
body {
margin: 0;
72
padding: 0;
font-weight: 400;
font-size: 1rem;
line-height: 1.58;
color: #333;
background-color: #f4f4f4;
body:before {
height: 50%;
width: 100%;
position: absolute;
top: 0;
left: 0;
background: #128ff2;
content: "";
z-index: 0;
.clearfix:after {
display: block;
content: "";
clear: both;
margin-top: 20px;
margin-bottom: 20px;
}
73
h1 {
font-size: 1.7em;
a {
color: #128ff2;
button {
box-shadow: none;
font-size: 14px;
outline: none;
line-height: 100%;
white-space: nowrap;
vertical-align: middle;
border-radius: 2px;
cursor: pointer;
min-height: 38px;
button.primary {
background-color: #128ff2;
color: #fff;
input {
74
font-size: 1rem;
input[type="file"] {
padding: 6px;
max-width: 100%;
.file-input {
width: 100%;
.submit-btn {
display: block;
margin-top: 15px;
min-width: 100px;
.file-input {
.submit-btn {
display: inline-block;
margin-top: 0;
margin-left: 10px;
}
75
.upload-container {
max-width: 700px;
margin-left: auto;
margin-right: auto;
background-color: #fff;
margin-top: 60px;
min-height: 400px;
position: relative;
padding: 20px;
.upload-header {
.upload-header h2 {
font-weight: 500;
.single-upload {
padding-bottom: 20px;
margin-bottom: 20px;
.upload-response {
overflow-x: hidden;
word-break: break-all;
}
76
Opção JQuery
$('#singleUploadForm').submit(function(event) {
// You can directly create form data from the form element
// (Or you could get the files from input element and append
them to FormData as we did in vanilla javascript)
$.ajax({
type: "POST",
enctype: 'multipart/form-data',
url: "/uploadFile",
data: formData,
processData: false,
contentType: false,
console.log(response);
// process response
},
console.log(error);
// process error
});
event.preventDefault();
});
77
Conclusão
Premissa
Etapas de Desenvolvimento
1. Criando um aplicativo Spring Boot
2. Estrutura do Projeto
3. Dependências Maven - Pom.xml
4. Camada de Domínio
5. A Camada de Repositório
6. A camada de controlador
7. A Camada de Visualização
8. Executando o aplicativo
9. Demo
2. Estrutura do Projeto
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-
thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>8.0.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-
plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Observe que estamos indo com os valores padrão, não adicionamos nada
no arquivo de configuração application.properties .
4. Camada de Domínio
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.validation.constraints.NotBlank;
@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@Column(name = "phone_no")
private long phoneNo;
public Student() {}
return phoneNo;
}
5. A Camada do Repositório
import java.util.List;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import net.javaguides.springboot.projeto.entity.Student;
@Repository
public interface StudentRepository extends CrudRepository
<Student, Long> {
List<Student> findByName(String name);
}
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import net.javaguides.springboot.projeto.entity.Student;
import
net.javaguides.springboot.projeto.repository.StudentRepository;
@Controller
@RequestMapping("/students/")
public class StudentController {
@Autowired
public StudentController(StudentRepository studentRepository)
{
this.studentRepository = studentRepository;
}
@GetMapping("signup")
public String showSignUpForm(Student student) {
return "add-student";
}
@GetMapping("list")
public String showUpdateForm(Model model) {
model.addAttribute("students",
studentRepository.findAll());
return "index";
}
@PostMapping("add")
public String addStudent(@Valid Student student, BindingResult
result, Model model) {
if (result.hasErrors()) {
return "add-student";
}
studentRepository.save(student);
return "redirect:list";
}
84
@GetMapping("edit/{id}")
public String showUpdateForm(@PathVariable("id") long id,
Model model) {
Student student = studentRepository.findById(id)
.orElseThrow(() - > new
IllegalArgumentException("Invalid student Id:" + id));
model.addAttribute("student", student);
return "update-student";
}
@PostMapping("update/{id}")
public String updateStudent(@PathVariable("id") long id,
@Valid Student student, BindingResult result,
Model model) {
if (result.hasErrors()) {
student.setId(id);
return "update-student";
}
studentRepository.save(student);
model.addAttribute("students",
studentRepository.findAll());
return "index";
}
@GetMapping("delete/{id}")
public String deleteStudent(@PathVariable("id") long id, Model
model) {
Student student = studentRepository.findById(id)
.orElseThrow(() - > new
IllegalArgumentException("Invalid student Id:" + id));
studentRepository.delete(student);
model.addAttribute("students",
studentRepository.findAll());
return "index";
}
}
7. A Camada de Visualização
add-student.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>Add User</title>
<meta name="viewport" content="width=device-width, initial-
scale=1">
<link rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap
.min.css" integrity="sha384-
MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO"
crossorigin="anonymous">
<link rel="stylesheet"
href="https://use.fontawesome.com/releases/v5.4.1/css/all.css"
integrity="sha384-
5sAR7xN1Nv6T6+dT2mhtzEpVJvfS3NScPQTrOxhwjIuvcA67KV2R5Jz6kr4abQsz"
crossorigin="anonymous">
<!-- <link rel="stylesheet" href="../css/shards.min.css"> --
>
</head>
<body>
<div class="container my-5">
<h3> Add Student</h3>
<div class="card">
<div class="card-body">
<div class="col-md-10">
<form action="#"
th:action="@{/students/add}" th:object="${student}" method="post">
<div class="row">
<div class="form-group col-md-8">
<label for="name" class="col-
form-label">Name</label> <input type="text" th:field="*{name}"
class="form-control" id="name" placeholder="Name"> <span
th:if="${#fields.hasErrors('name')}" th:errors="*{name}" class="text-
danger"></span>
</div>
<div class="form-group col-md-8">
<label for="email" class="col-
form-label">Email</label> <input type="text" th:field="*{email}"
class="form-control" id="email" placeholder="Email"> <span
th:if="${#fields.hasErrors('email')}" th:errors="*{email}"
class="text-danger"></span>
</div>
<div class="col-md-6">
86
</div>
</form>
</div>
</div>
</div>
</div>
</body>
</html>
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>Users</title>
<meta name="viewport" content="width=device-width, initial-
scale=1">
<link rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap
.min.css" integrity="sha384-
MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO"
crossorigin="anonymous">
<link rel="stylesheet"
href="https://use.fontawesome.com/releases/v5.4.1/css/all.css"
integrity="sha384-
5sAR7xN1Nv6T6+dT2mhtzEpVJvfS3NScPQTrOxhwjIuvcA67KV2R5Jz6kr4abQsz"
crossorigin="anonymous">
<!-- <link rel="stylesheet" href="../css/shards.min.css"> --
>
</head>
<body>
<div class="container my-2">
<div class="card">
<div class="card-body">
<div th:switch="${students}" class="container
my-5">
<p class="my-5">
<a href="/students/signup" class="btn
btn-primary"><i
class="fas fa-user-plus ml-2"> Add Student</i></a>
</p>
<div class="col-md-10">
<h2 th:case="null">No Students yet!</h2>
<div th:case="*">
87
</div>
</div>
</div>
</div>
</div>
</body>
</html>
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>Update User</title>
<meta name="viewport" content="width=device-width, initial-
scale=1">
<link rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap
.min.css" integrity="sha384-
MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO"
crossorigin="anonymous">
88
<link rel="stylesheet"
href="https://use.fontawesome.com/releases/v5.4.1/css/all.css"
integrity="sha384-
5sAR7xN1Nv6T6+dT2mhtzEpVJvfS3NScPQTrOxhwjIuvcA67KV2R5Jz6kr4abQsz"
crossorigin="anonymous">
</head>
<body>
<div class="container my-5">
<h3> Update Student</h3>
<div class="card">
<div class="card-body">
<div class="col-md-8">
<form action="#"
th:action="@{/students/update/{id}(id=${student.id})}"
th:object="${student}" method="post">
<div class="row">
<div class="form-group col-md-6">
<label for="name" class="col-
form-label">Name</label> <input type="text" th:field="*{name}"
class="form-control" id="name" placeholder="Name"> <span
th:if="${#fields.hasErrors('name')}" th:errors="*{name}" class="text-
danger"></span>
</div>
<div class="form-group col-md-8">
<label for="email" class="col-
form-label">Email</label> <input type="text" th:field="*{email}"
class="form-control" id="email" placeholder="Email"> <span
th:if="${#fields.hasErrors('email')}" th:errors="*{email}"
class="text-danger"></span>
</div>
<div class="form-group col-md-8">
<label for="phoneNo" class="col-
form-label">Phone No</label> <input type="text" th:field="*{phoneNo}"
class="form-control" id="phoneNo" placeholder="phoneNo"> <span
th:if="${#fields.hasErrors('phoneNo')}" th:errors="phoneNo"
class="text-danger"></span>
</div>
</html>
89
8. Executando o aplicativo
import org.springframework.boot.SpringApplication;
import
org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
9. Executando a aplicação
Você pode abrir o aplicativo em duas abas, fazer o login com nomes de
usuário diferentes e começar a enviar mensagens.
Criando o aplicativo
• Vá para http://start.spring.io/ .
• Insira o valor do Artifact como websocket-demo .
• Adicione Websocket na seção de dependências.
• Clique em Gerar projeto para baixar o projeto.
• Extraia o arquivo zip baixado.
Configuração WebSocket
package com.example.websocketdemo.config;
import org.springframework.context.annotation.Configuration;
import
org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.*;
@Configuration
@EnableWebSocketMessageBroker
@Override
registry.addEndpoint("/ws").withSockJS();
@Override
registry.setApplicationDestinationPrefixes("/app");
registry.enableSimpleBroker("/topic");
Criação do ChatMessagemodel
package com.example.websocketdemo.model;
95
CHAT,
JOIN,
LEAVE
return type;
this.type = type;
return content;
this.content = content;
return sender;
}
96
this.sender = sender;
package com.example.websocketdemo.controller;
import com.example.websocketdemo.model.ChatMessage;
import
org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.messaging.handler.annotation.SendTo;
import
org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.stereotype.Controller;
@Controller
@MessageMapping("/chat.sendMessage")
@SendTo("/topic/public")
return chatMessage;
@MessageMapping("/chat.addUser")
@SendTo("/topic/public")
SimpMessageHeaderAccessor
headerAccessor) {
headerAccessor.getSessionAttributes().put("username",
chatMessage.getSender());
return chatMessage;
package com.example.websocketdemo.controller;
import com.example.websocketdemo.model.ChatMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
98
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import
org.springframework.messaging.simp.SimpMessageSendingOperations;
import
org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.stereotype.Component;
import
org.springframework.web.socket.messaging.SessionConnectedEvent;
import
org.springframework.web.socket.messaging.SessionDisconnectEvent;
@Component
@Autowired
@EventListener
public void
handleWebSocketConnectListener(SessionConnectedEvent event) {
@EventListener
public void
handleWebSocketDisconnectListener(SessionDisconnectEvent event) {
StompHeaderAccessor headerAccessor =
StompHeaderAccessor.wrap(event.getMessage());
if(username != null) {
chatMessage.setType(ChatMessage.MessageType.LEAVE);
chatMessage.setSender(username);
messagingTemplate.convertAndSend("/topic/public",
chatMessage);
Criação do front-end
static
└── css
└── main.css
└── js
└── main.js
└── index.html
100
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<noscript>
</noscript>
<div id="username-page">
<div class="username-page-container">
<div class="form-group">
</div>
<div class="form-group">
</div>
</form>
</div>
</div>
<div class="chat-container">
<div class="chat-header">
</div>
<div class="connecting">
Connecting...
</div>
<ul id="messageArea">
</ul>
<div class="form-group">
<button type="submit"
class="primary">Send</button>
</div>
</div>
</form>
</div>
</div>
102
<script src="https://cdnjs.cloudflare.com/ajax/libs/sockjs-
client/1.1.4/sockjs.min.js"></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/stomp.js/2.3.3/stomp.min.j
s"></script>
<script src="/js/main.js"></script>
</body>
</html>
2. JavaScript - main.js
'use strict';
var colors = [
];
function connect(event) {
username = document.querySelector('#name').value.trim();
if(username) {
usernamePage.classList.add('hidden');
chatPage.classList.remove('hidden');
stompClient = Stomp.over(socket);
event.preventDefault();
function onConnected() {
stompClient.subscribe('/topic/public', onMessageReceived);
stompClient.send("/app/chat.addUser",
{},
connectingElement.classList.add('hidden');
}
104
function onError(error) {
connectingElement.style.color = 'red';
function sendMessage(event) {
var chatMessage = {
sender: username,
content: messageInput.value,
type: 'CHAT'
};
stompClient.send("/app/chat.sendMessage", {},
JSON.stringify(chatMessage));
messageInput.value = '';
event.preventDefault();
function onMessageReceived(payload) {
messageElement.classList.add('event-message');
messageElement.classList.add('event-message');
} else {
messageElement.classList.add('chat-message');
var avatarText =
document.createTextNode(message.sender[0]);
avatarElement.appendChild(avatarText);
avatarElement.style['background-color'] =
getAvatarColor(message.sender);
messageElement.appendChild(avatarElement);
var usernameText =
document.createTextNode(message.sender);
usernameElement.appendChild(usernameText);
messageElement.appendChild(usernameElement);
textElement.appendChild(messageText);
messageElement.appendChild(textElement);
messageArea.appendChild(messageElement);
messageArea.scrollTop = messageArea.scrollHeight;
}
106
function getAvatarColor(messageSender) {
var hash = 0;
return colors[index];
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
107
html,body {
height: 100%;
overflow: hidden;
body {
margin: 0;
padding: 0;
font-weight: 400;
font-size: 1rem;
line-height: 1.58;
color: #333;
background-color: #f4f4f4;
height: 100%;
body:before {
height: 50%;
width: 100%;
position: absolute;
top: 0;
left: 0;
background: #128ff2;
content: "";
z-index: 0;
.clearfix:after {
display: block;
108
content: "";
clear: both;
.hidden {
display: none;
.form-control {
width: 100%;
min-height: 38px;
font-size: 15px;
.form-group {
margin-bottom: 15px;
input {
padding-left: 10px;
outline: none;
margin-top: 20px;
margin-bottom: 20px;
h1 {
font-size: 1.7em;
109
a {
color: #128ff2;
button {
box-shadow: none;
font-size: 14px;
outline: none;
line-height: 100%;
white-space: nowrap;
vertical-align: middle;
border-radius: 2px;
cursor: pointer;
min-height: 38px;
button.default {
background-color: #e8e8e8;
color: #333;
button.primary {
background-color: #128ff2;
color: #fff;
110
button.accent {
background-color: #ff4743;
color: #fff;
#username-page {
text-align: center;
.username-page-container {
background: #fff;
border-radius: 2px;
width: 100%;
max-width: 500px;
display: inline-block;
margin-top: 42px;
vertical-align: middle;
position: relative;
min-height: 250px;
position: absolute;
top: 50%;
left: 0;
right: 0;
margin: 0 auto;
margin-top: -160px;
}
111
.username-page-container .username-submit {
margin-top: 10px;
#chat-page {
position: relative;
height: 100%;
.chat-container {
max-width: 700px;
margin-left: auto;
margin-right: auto;
background-color: #fff;
margin-top: 30px;
max-height: 600px;
position: relative;
#chat-page ul {
list-style-type: none;
background-color: #FFF;
margin: 0;
overflow: auto;
overflow-y: scroll;
#chat-page #messageForm {
padding: 20px;
#chat-page ul li {
line-height: 1.5rem;
margin: 0;
#chat-page ul li p {
margin: 0;
#chat-page .event-message {
width: 100%;
text-align: center;
clear: both;
#chat-page .event-message p {
color: #777;
font-size: 14px;
word-wrap: break-word;
#chat-page .chat-message {
padding-left: 68px;
113
position: relative;
#chat-page .chat-message i {
position: absolute;
width: 42px;
height: 42px;
overflow: hidden;
left: 10px;
display: inline-block;
vertical-align: middle;
font-size: 18px;
line-height: 42px;
color: #fff;
text-align: center;
border-radius: 50%;
font-style: normal;
text-transform: uppercase;
color: #333;
font-weight: 600;
#chat-page .chat-message p {
color: #43464b;
float: left;
114
float: left;
width: 80px;
height: 38px;
margin-left: 5px;
.chat-header {
text-align: center;
padding: 15px;
.chat-header h2 {
margin: 0;
font-weight: 500;
.connecting {
padding-top: 5px;
text-align: center;
color: #777;
position: absolute;
top: 65px;
width: 100%;
}
115
.chat-container {
margin-left: 10px;
margin-right: 10px;
margin-top: 10px;
.chat-container {
.username-page-container {
width: auto;
margin-left: 15px;
margin-right: 15px;
padding: 25px;
#chat-page ul {
width: 65px;
.chat-header {
padding: 10px;
.connecting {
top: 60px;
.chat-header h2 {
font-size: 1.1em;
Executando o aplicativo
$ mvn spring-boot:run
Arquitetura do projeto
Implementação de recursos
• Criar um employee
• Atualizar um employee
• Lista de employees
• Excluir employee
118
• Ver Employee
Excluir employee
• Project : Maven
• Versão Java : 8
• Spring Boot : 2.3.4
• Grupo : com.example
• Artfact : angularspringboot
• Name : angularspringboot
• Description: Sringboot Angular Project
• PackageName : com.example.amgularspringbooot
• pacote : jar (este é o valor padrão)
121
3. O arquivo pom.xml
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>angularspringboot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>angularspringboot</name>
<description>Sprinboot Angular Project</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-
8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-
8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-
jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-
plugin</artifactId>
</plugin>
</plugins>
123
</build>
</project>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
server.servlet.context-path=/springboot-crud-rest
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
124
import javax.persistence.Table;
@Entity
@Table(name = "employees")
public class Employee {
public Employee() {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
@Override
public String toString() {
return "Employee [id=" + id + ", firstName=" + firstName
+ ", lastName=" + lastName + ", emailId=" + emailId
+ "]";
}
125
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import
net.guides.springboot2.springboot2jpacrudexample.model.Employee;
@Repository
public interface EmployeeRepository extends
JpaRepository<Employee, Long>{
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import
net.guides.springboot2.springboot2jpacrudexample.exception.ResourceNot
FoundException;
import
net.guides.springboot2.springboot2jpacrudexample.model.Employee;
import
net.guides.springboot2.springboot2jpacrudexample.repository.EmployeeRe
pository;
@GetMapping("/employees")
126
@GetMapping("/employees/{id}")
public ResponseEntity<Employee>
getEmployeeById(@PathVariable(value = "id") Long employeeId)
throws ResourceNotFoundException {
Employee employee =
employeeRepository.findById(employeeId)
.orElseThrow(() -> new
ResourceNotFoundException("Employee not found for this id :: " +
employeeId));
return ResponseEntity.ok().body(employee);
}
@PostMapping("/employees")
public Employee createEmployee(@Valid @RequestBody Employee
employee) {
return employeeRepository.save(employee);
}
@PutMapping("/employees/{id}")
public ResponseEntity<Employee>
updateEmployee(@PathVariable(value = "id") Long employeeId,
@Valid @RequestBody Employee employeeDetails) throws
ResourceNotFoundException {
Employee employee =
employeeRepository.findById(employeeId)
.orElseThrow(() -> new
ResourceNotFoundException("Employee not found for this id :: " +
employeeId));
employee.setEmailId(employeeDetails.getEmailId());
employee.setLastName(employeeDetails.getLastName());
employee.setFirstName(employeeDetails.getFirstName());
final Employee updatedEmployee =
employeeRepository.save(employee);
return ResponseEntity.ok(updatedEmployee);
}
@DeleteMapping("/employees/{id}")
public Map<String, Boolean>
deleteEmployee(@PathVariable(value = "id") Long employeeId)
throws ResourceNotFoundException {
Employee employee =
employeeRepository.findById(employeeId)
.orElseThrow(() -> new
ResourceNotFoundException("Employee not found for this id :: " +
employeeId));
employeeRepository.delete(employee);
Map<String, Boolean> response = new HashMap<>();
response.put("deleted", Boolean.TRUE);
return response;
}
}
127
@CrossOrigin(origins = "http://localhost:4200")
@RestController
@RequestMapping("/api/v1")
public class EmployeeController {
// ....
}
Aqui está o que acontece quando você dispara uma solicitação para um
recurso não encontrado: http: // localhost: 8080 / some-dummy-url
{
"timestamp": 1512713804164,
"status": 404,
"error": "Not Found",
"message": "No message available",
"path": "/some-dummy-url"
}
Vamos ver o que Spring Boot faz quando uma exceção é lançada de
um Resource . podemos especificar o Status de Resposta para uma exceção
específica junto com a definição da Exceção
com a annotation '@ResponseStatus' .
128
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends Exception{
import java.util.Date;
return details;
}
}
import java.util.Date;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<?>
resourceNotFoundException(ResourceNotFoundException ex, WebRequest
request) {
ErrorDetails errorDetails = new ErrorDetails(new
Date(), ex.getMessage(), request.getDescription(false));
return new ResponseEntity<>(errorDetails,
HttpStatus.NOT_FOUND);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<?> globleExcpetionHandler(Exception
ex, WebRequest request) {
ErrorDetails errorDetails = new ErrorDetails(new Date(),
ex.getMessage(), request.getDescription(false));
return new ResponseEntity<>(errorDetails,
HttpStatus.INTERNAL_SERVER_ERROR);
}
}
9. Aplicação em execução
import org.springframework.boot.SpringApplication;
import
org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
Use os endpoints abaixo Rest para testar CRUD Rest APIs e no aplicativo
Angular.
Criar employee:
A seguir está uma captura de tela do todo-app que iremos construir neste
projeto.
• Vá para http://start.spring.io/ .
• Insira o valor do artefato como todoapp .
• Adicione Spring Web e Spring Data MongoDB na seção de
dependências.
• Clique em Gerar para gerar e baixar o projeto.
133
# MONGODB (MongoProperties)
spring.data.mongodb.uri=mongodb://localhost:27017/todo
app
Observe que, para que a configuração acima funcione, você precisa ter o
MongoDB instalado em seu sistema.
mongodb://<dbuser>:<dbpassword>@ds161169.mlab.com:6116
9/testdatabase
3. Criação do TodoModel
package com.example.todoapp.models;
import java.util.Date;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;
@Document(collection="todos")
@Id
@NotBlank
@Size(max=100)
@Indexed(unique=true)
public Todo() {
super();
this.title = title;
return id;
this.id = id;
return title;
this.title = title;
return completed;
this.completed = completed;
return createdAt;
this.createdAt = createdAt;
@Override
return String.format(
package com.example.todoapp.repositories;
import com.example.todoapp.models.Todo;
import
org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;
@Repository
Por fim, vamos criar as APIs que serão expostas ao client. Crie um novo
pacote controllers dentro com.example.todoapp e adicione um
arquivo TodoController.java dentro do pacote controllers com o seguinte
código:
package com.example.todoapp.controllers;
import javax.validation.Valid;
import com.example.todoapp.models.Todo;
import com.example.todoapp.repositories.TodoRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api")
@CrossOrigin("*")
public class TodoController {
@Autowired
TodoRepository todoRepository;
@GetMapping("/todos")
public List<Todo> getAllTodos() {
Sort sortByCreatedAtDesc = new Sort(Sort.Direction.DESC,
"createdAt");
return todoRepository.findAll(sortByCreatedAtDesc);
}
@PostMapping("/todos")
public Todo createTodo(@Valid @RequestBody Todo todo) {
todo.setCompleted(false);
return todoRepository.save(todo);
}
@GetMapping(value="/todos/{id}")
public ResponseEntity<Todo> getTodoById(@PathVariable("id")
String id) {
return todoRepository.findById(id)
.map(todo -> ResponseEntity.ok().body(todo))
.orElse(ResponseEntity.notFound().build());
}
@PutMapping(value="/todos/{id}")
public ResponseEntity<Todo> updateTodo(@PathVariable("id")
String id,
139
@Valid @RequestBody
Todo todo) {
return todoRepository.findById(id)
.map(todoData -> {
todoData.setTitle(todo.getTitle());
todoData.setCompleted(todo.getCompleted());
Todo updatedTodo =
todoRepository.save(todoData);
return
ResponseEntity.ok().body(updatedTodo);
}).orElse(ResponseEntity.notFound().build());
}
@DeleteMapping(value="/todos/{id}")
public ResponseEntity<?> deleteTodo(@PathVariable("id")
String id) {
return todoRepository.findById(id)
.map(todo -> {
todoRepository.deleteById(id);
return ResponseEntity.ok().build();
}).orElse(ResponseEntity.notFound().build());
}
}
mvn spring-boot:run
O aplicativo será iniciado na porta 8080. Você pode testar o backend apis
usando o carteiro ou qualquer outro cliente restante de sua escolha.
$ ng new angular-frontend
2. Executando o aplicativo
$ cd angular-frontend
$ ng serve --open
<main>
<todo-list></todo-list>
</main>
id: string;
title: string;
completed: boolean;
createdAt: Date;
@Component({
selector: 'todo-list',
templateUrl: './todo-list.component.html'
142
})
ngOnInit(): void {
this.getTodos();
}
getTodos(): void {
clearEditing(): void {
}
}
@NgModule({
declarations: [
AppComponent,
TodoListComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Observe que,
adicionamos FormsModule e HttpModule dentro app.module.ts porque vamos
precisar deles para lidar com a vinculação de formulários e chamar as APIs
restantes, respectivamente.
<div class="todo-content">
<h1 class="page-title">My Todos</h1>
<div class="todo-create">
<form #todoForm="ngForm" (ngSubmit)="createTodo(todoForm)"
novalidate>
<input type="text" id="title" class="form-control"
placeholder="Type a todo and press enter..."
required
name="title" [(ngModel)]="newTodo.title"
#title="ngModel" >
<div *ngIf="title.errors && title.dirty"
class="alert alert-danger">
144
<div [hidden]="!title.errors.required">
Title is required.
</div>
</div>
</form>
</div>
<ul class="todo-list">
<li *ngFor="let todo of todos"
[class.completed]="todo.completed === true" >
<div class="todo-row" *ngIf="!editing || editingTodo.id
!= todo.id">
<a class="todo-completed"
(click)="toggleCompleted(todo)">
<i class="material-icons toggle-completed-
checkbox"></i>
</a>
<span class="todo-title">
{{todo.title}}
</span>
<span class="todo-actions">
<a (click)="editTodo(todo)">
<i class="material-icons edit">edit</i>
</a>
<a (click)="deleteTodo(todo.id)">
<i class="material-icons delete">clear</i>
</a>
</span>
</div>
<div class="todo-edit" *ngIf="editing && editingTodo.id
=== todo.id">
<input class="form-control" type="text"
[(ngModel)]="editingTodo.title" required>
<span class="edit-actions">
<a (click)="updateTodo(editingTodo)">
<i class="material-icons">done</i>
</a>
<a (click)="clearEditing()">
<i class="material-icons">clear</i>
</a>
</span>
</div>
</li>
</ul>
<div class="no-todos" *ngIf="todos && todos.length == 0">
<p>No Todos Found!</p>
</div>
</div>
/* You can add global styles to this file, and also import other
style files */
body {
font-size: 18px;
line-height: 1.58;
background: #d53369;
background: -webkit-linear-gradient(to left, #cbad6d,
#d53369);
background: linear-gradient(to left, #cbad6d, #d53369);
color: #333;
}
h1 {
font-size: 36px;
}
* {
box-sizing: border-box;
}
i {
vertical-align: middle;
color: #626262;
}
input {
border: 1px solid #E8E8E8;
}
.page-title {
text-align: center;
}
.todo-content {
max-width: 650px;
width: 100%;
margin: 0 auto;
margin-top: 60px;
background-color: #fff;
padding: 15px;
box-shadow: 0 0 4px rgba(0,0,0,.14), 0 4px 8px
rgba(0,0,0,.28);
}
.form-control {
font-size: 16px;
padding-left: 15px;
146
outline: none;
border: 1px solid #E8E8E8;
}
.form-control:focus {
border: 1px solid #626262;
}
.todo-content .form-control {
width: 100%;
height: 50px;
}
.todo-content .todo-create {
padding-bottom: 30px;
border-bottom: 1px solid #e8e8e8;
}
.todo-content .alert-danger {
padding-left: 15px;
font-size: 14px;
color: red;
padding-top: 5px;
}
.todo-content ul {
list-style: none;
margin: 0;
padding: 0;
max-height: 450px;
padding-left: 15px;
padding-right: 15px;
margin-left: -15px;
margin-right: -15px;
overflow-y: scroll;
}
.todo-content ul li {
padding-top: 10px;
padding-bottom: 10px;
border-bottom: 1px solid #E8E8E8;
}
.todo-content ul li span {
display: inline-block;
vertical-align: middle;
}
.todo-content .todo-title {
width: calc(100% - 160px);
margin-left: 10px;
overflow: hidden;
text-overflow: ellipsis;
}
.todo-content .todo-completed {
display: inline-block;
text-align: center;
width:35px;
height:35px;
cursor: pointer;
147
.todo-content .todo-completed i {
font-size: 20px;
}
.no-todos {
text-align: center;
}
.toggle-completed-checkbox:before {
content: 'check_box_outline_blank';
}
li.completed .toggle-completed-checkbox:before {
content: 'check_box';
}
li.completed .todo-title {
text-decoration: line-through;
color: #757575;
}
li.completed i {
color: #757575;
}
<link rel="stylesheet"
href="https://fonts.googleapis.com/icon?family=Material+Icons">
@Injectable()
export class TodoService {
private baseUrl = 'http://localhost:8080';
getTodos(): Promise<Todo[]> {
return this.http.get(this.baseUrl + '/api/todos/')
.toPromise()
.then(response => response.json() as Todo[])
.catch(this.handleError);
}
// Inside app.module.ts
// Inside app.module.ts
providers: [TodoService]
@Component({
selector: 'todo-list',
templateUrl: './todo-list.component.html'
})
constructor(
private todoService: TodoService,
) {}
ngOnInit(): void {
this.getTodos();
}
getTodos(): void {
this.todoService.getTodos()
.then(todos => this.todos = todos );
}
clearEditing(): void {
this.editingTodo = new Todo();
151
this.editing = false;
}
}
$ cd todoapp
$ mvn spring-boot:run
$ cd angular-frontend
$ ng serve --open
152
Apendice
file.upload-dir=/Users/seuusuario/uploads
153
Referencias:
https://www.javaguides.net/2018/09/getting-started-with-spring-boot.html
http://www.bosontreinamentos.com.br/java/programacao-em-java-instalando-o-ide-eclipse/
https://www.javaguides.net/2018/10/spring-boot-annotations.html
https://www.callicoder.com/spring-boot-file-upload-download-rest-api-example/
https://www.javaguides.net/2019/04/spring-boot-thymeleaf-crud-example-projeto.html
https://www.callicoder.com/spring-boot-websocket-chat-example/
https://www.javaguides.net/2020/01/spring-boot-angular-9-crud-example-projeto.html
https://www.callicoder.com/spring-boot-mongodb-angular-js-rest-api-projeto/