Você está na página 1de 104

Direito Autoral

Matera Systems
Direito Autoral
Matera Systems

Informações
IMPORTANTES
✔ Dê a sua opinião e contribua com a melhoria do projeto Matera BootCamp, e
garantimos que sua resposta será anônima!
Acesse agora: https://forms.gle/2SX4jFdfkmaV5yF47
✔ O certificado de participação será enviado aos alunos que tiverem 55% ou mais de
presença, na semana dos dias 13/03 à 17/03/23 via email.
✔ Faremos um sorteio de uma caneca exclusiva para os participantes que completarem

😉
no mínimo 80% de presença. Fique de olho em seu email, o resultado sai no dia
17/03/2023!
✔ Convidamos a todos a participar da nossa comunidade do Matera BootCamp. É o
ambiente perfeito para você participar e garantir que receberá todos os comunicados.
Entre no grupo:

Recursos Necessários
JAVA: https://www.java.com/pt-BR/download/ie_manual.jsp?locale=pt_BR
GIT: https://git-scm.com/downloads
Maven: https://maven.apache.org/download.cgi
JUnit: https://github.com/junit-team/junit4/wiki/Download-and-Install
Lombok: https://mvnrepository.com/artifact/org.projectlombok/lombok
Direito Autoral
Matera Systems

Revisão Geral da Linguagem


JAVA 8

Conceitos Básicos

Encapsulamento

Encapsulamento é um conceito da programação orientada a objetos que,

basicamente, significa separar o software em partes que sejam o mais isoladas


possível. Serve para controlar o acesso aos atributos e métodos de cada classe,
garantindo sua proteção de dados e de comportamento.
Private é o nível de acesso mais restritivo, sendo que atributos e métodos
declarados com este nível são visíveis apenas dentro do escopo em que foram
declarados. No geral, recomenda-se que sempre seja utilizado private, a não ser
que o programador tenha um bom motivo para não fazê-lo.
Outros níveis de acesso são public, protected e package-private (padrão).
Para manter o encapsulamento de cada classe, deve-se prestar atenção quando
métodos get e set realmente devem ser implementados.

Herança
Herança é um princípio da programação orientada a objetos que permite
que classes sejam criadas a partir de outras. Desse modo, temos superclasses
(classes que dão origem às demais) e subclasses. Com isso, criamos um
esquema de hierarquia entre elas.

Composição
Composição apresenta um comportamento parecido com a da herança,
mas na composição podemos mudar o comportamento dos métodos
personalizando-os para cada subclasse em tempo de execução.
Na herança, criamos métodos que serão comuns às classes filhas e as
Direito Autoral
Matera Systems

especificações criamos diretamente nessas classes derivadas. Na composição


criamos um método da classe base que poderá ser customizado em cada classe
derivada.
No geral, é mais recomendado escolher utilizar composição quando
comparado à herança, pois esta não gera tanto acoplamento entre as classes
base e derivadas, deixando o software mais dinâmico e customizável.

Polimorfismo
Basicamente, o polimorfismo é o que permite que classes abstratas
tenham seus comportamentos alterados pelas classes concretas que as
estendem.

Classes Abstratas
Classes abstratas são classes bases que servem de modelo para classes
derivadas. Os métodos definidos na classe abstrata devem ser sobrescritos nas
classes derivadas. Classes abstratas não são instanciáveis, apenas suas
derivadas.

Figura 1: Criação da classe abstrata.


Direito Autoral
Matera Systems

Figura 2: Utilização da classe abstrata.

Exemplo 3: Utilização da classe abstrata.

Figura 4: Teste classe abstrata.


Direito Autoral
Matera Systems

Figura 5: Resultado teste classe abstrata.

Interfaces
Interfaces são padrões definidos (como especificações ou “contratos”) para
as classes que as implementam. Elas definem o formato, e quais métodos
devem ser, obrigatoriamente, implementados.
Uma interface deve ter todos os métodos abstratos, sendo que nenhuma
implementação é permitida.

Figura 6: Criação interface.


Direito Autoral
Matera Systems

Figura 7: Exemplo utilização interface.


Direito Autoral
Matera Systems

Figura 8: Exemplo de utilização de interface.

Figura 9: Método que utiliza interface como parâmetro.


Direito Autoral
Matera Systems

Figura 10: Classe para teste da interface.

Figura 11: Resultado do teste de interface.

Sobreescrita e Sobrecarga

Sobreescrever um método quer dizer que a classe base já possui uma

definição para ele, um código pré-definido, mas que este teve seu código
substituído pelo código definido na classe derivada que a estende.
Direito Autoral
Matera Systems

Figura 12: Criação da classe que terá o método sobreescrito.

Figura 13: Criação da classe que sobreescreve o método.


Direito Autoral
Matera Systems

Figura 14: Classe de teste de sobreescrita de método.

Figura 15: Resultado do teste de sobreescrita.

Entendemos como sobrecarga situações em que métodos distintos


possuem o mesmo nome, mas parâmetros diferentes. Desse modo, os tipos e
quantidades de parâmetros são o que definem para o JAVA qual dos métodos
com aquela mesma nomenclatura existentes na classe deverá ser acionado.
Direito Autoral
Matera Systems

Figura 16: Exemplo de sobrecarga de método.

Figura 17: Testes de sobrecarga de método.


Direito Autoral
Matera Systems

Figura 18: Resultados de testes de sobrecarga.

Equals vs ==

Equals atuará sobre o objeto passado por parâmetro, comparando os

valores das propriedades uma por uma. Retornará true se todas as propriedades
tiverem o mesmo valor.
Por outro lado, o == irá retornar true apenas se os dois objetos
referenciarem o mesmo endereço em memória, isto é, sejam a mesma
instância.

Figura 19: Teste de equals e ==.


Direito Autoral
Matera Systems

Figura 20: Resultado teste de equals e ==.

Modificadores de Acesso
Modificadores de acesso procuram limitar o acesso à cada parte da classe
para serem mais adequadas para cada propósito. São definidos sempre em
letras maiúsculas como FINAL, NATIVE, STATIC SYNCHRONIZED e VOLATILE, e
podem ser utilizados em conjunto.
Quando o atributo recebe os modificadores STATIC FINAL quer dizer que
sua inicialização poderá ser realizada apenas na declaração do atributo ou em
algum bloco de inicialização estático.
Os modificadores Final e Static, os mais utilizados, serão apresentados
com mais detalhes a seguir.

Final

Confere segurança ao código e maior velocidade de execução. Torna o

atributo uma constante que só pode ser inicializada uma única vez, diretamente
na declaração da classe ou no construtor.
Final é utilizado em criação de properties ou variáveis que já terão seus
valores definidos desde o começo. Com tipos primitivos, como integer e string,
esse valor será imutável.
Direito Autoral
Matera Systems

Figura 21: Exemplo de propriedades final.

Contudo, podemos criar uma final de um objeto, por exemplo, e esse


objeto será mutável.

Figura 22: Classe utilizada como exemplo para o teste de modificador final.
Direito Autoral
Matera Systems

Figura 23: Teste de modificador final.

Figura 24: Resultado do teste de modificador final.

No caso acima, quer dizer que a propriedade final irá manter a referência
a este objeto, mas que seus atributos poderão ser modificados posteriormente.
Quando aplicado a métodos, o modificador FINAL garante que este não
será sobreescrito.
Por último, o modificador FINAL pode ser aplicado em parâmetros, sendo
que estes não poderão ser modificados no corpo do método.

Static
Economiza memória e a possibilita a troca de informações entre objetos
da classe. Torna o atributo possível de ser inicializado em qualquer parte da
classe, mas uma vez que recebe valor, todos os objetos da classe podem o
acessar.
Atributos STATIC são “atributos de classe''. Isso significa que todos os
objetos instanciados dessa classe compartilham esse atributo. É parecido com
Direito Autoral
Matera Systems

variáveis globais em linguagens de programação estruturadas.


A maior vantagem é que eles podem ser acionados sem a instância da
classe a qual pertencem, quando são declarados públicos.

Figura 25: Exemplo criação método static.

Figura 26: Classe de teste de static.

Figura 27: Resultado teste static.

Estruturas e Funcionalidades

Classe Anônima
Classes anônimas são classes que não são declaradas explicitamente no
código, mas são internas à outra classe
Direito Autoral
Matera Systems

Normalmente são utilizadas como herança de uma classe em algum


ponto único do código. Isso quer dizer que ela redefine o que a classe pai realiza
apenas naquele trecho do código onde é usada.

Figura 28: Classe Conta utilizada para exemplo de classe anônima.

Figura 29: Exemplo utilização de classe anônima.

Não é possível criar um método numa classe anônima que não existe na
classe pai.
Ainda podemos utilizar classes anônimas com Interfaces.
Direito Autoral
Matera Systems

Figura 30: Interface utilizada para exemplo de classe anônima com interface.

Figura 31: Exemplo de utilização de classe anônima com interface.

No exemplo acima, temos a falsa impressão de que instanciamos a


interface, mas, na verdade, o java simula uma criação de classe que estende
essa interface. Nesse cenário, é sempre obrigatória a utilização de todos os
métodos da interface.
Direito Autoral
Matera Systems

Lambdas
As funções lambda tem como objetivo adicionar características de
linguagens funcionais ao JAVA. Foi introduzida no JAVA 8. Uma das grandes
vantagens é a diminuição de código necessário para a utilização e escrita de
funções.
Assim como as Classes Anônimas, as funções lambda nada mais são que
funções sem definição. Não é necessário declarar uma função lambda, por isso
podem ser declaradas já no mesmo ponto do código em que serão utilizadas.
A sintaxe das funções lambdas em JAVA é (argumento) -> (corpo), sendo
que uma função lambda por ter 0 ou N parâmetros e seus parâmetros podem
ou não ter declaração de tipos. Nos casos onde os tipos dos parâmetros não são
informados, o JAVA é capaz de inferi-los.

Figura 32: Exemplos de funções lambda.

Acima temos vários exemplos de funções lambdas e todas são válidas. As


chaves só são obrigatórias caso exista mais de uma instrução na função. Se o
retorno não for definido, a função retornará void.
Funções lambdas podem ser utilizadas em várias situações para
simplificação de código em casos onde ela será utilizada em um ponto
específico.
Um dos usos mais comuns da função lambda é para simplificar a
execução de threads.
Direito Autoral
Matera Systems

Figura 33: Uso de thread comum.

Figura 34: Exemplo uso de thread com lambda.

No exemplo acima, a função lambda não possui nenhum


parâmetro, pois ela está sendo passada para a função run, que também não
possui nenhum parâmetro. O retorno de run é void, portanto não precisamos
declarar um retorno para a função lambda também.
Existe ainda um meio de simplificar ainda mais o código de threads
informando a função lambda diretamente no construtor da classe Thread.
Desse modo, não precisamos instanciar a classe Runnable, passamos apenas a
função lambda no lugar:
Direito Autoral
Matera Systems

Figura 35: Thread com lambda simplificado.

Funções lambdas também são amplamente utilizadas para realizar ações


sobre coleções, como imprimir, filtrar e buscar. Quando combinadas com a
função forEach das listas, podemos simplificar muito código.

Figura 36: Exemplo de manipulação de lista comum.

Figura 37: Resultado do teste de manipulação de lista comum.


Direito Autoral
Matera Systems

Figura 38: Exemplo de uso de manipulação de lista com lambda.

Figura 39: Resultado exemplo de uso de manipulação de lista com lambda.

Ainda existe outro modo de usar as funções lambda que é passá-las como
parâmetro. Essa possibilidade abre oportunidade para que o método seja mais
flexível e customizável.
Direito Autoral
Matera Systems

Figura 40: Exemplo de uso de lambda como parâmetro.

No exemplo acima, podemos manipular a lista conforme a função lambda


informada. O método recebe uma lista e uma função lambda (baseada numa
interface) e executa essa função na lista recebida.

Streams
Stream é um meio de trabalhar com coleções de modo que a
manipulação dessas fique mais fácil e seja realizada com menos código.
Também é uma das vantagens da adição de paradigmas funcionais ao JAVA 8.
Com streams é possível realizar buscas, mapeamentos e operações sobre
coleções.
Direito Autoral
Matera Systems

Figura 41: Exemplo de teste com stream.

No exemplo acima, tenha em mente que podemos utilizar o método


stream() também para a biblioteca java.util.map.

Figura 42: Outros modos de criação de stream.

A iteração sobre streams é feita com forEach. Operações podem ser


realizadas no loop utilizando as funções lambda
Direito Autoral
Matera Systems

Figura 43: Teste com stream e lista.

Figura 44: Resultado de teste com stream e lista.

A maior parte das funções da stream facilita o loop realizado nos itens
desta. Por exemplo, a função filter() irá iterar na stream utilizando como critério
a função lambda informada.
Direito Autoral
Matera Systems

Figura 45: Stream com filter.

Com isso, podemos realizar uma cadeia de operações em uma stream


apenas acionando seus métodos, como no exemplo abaixo:

Figura 46: Objeto de teste stream com operações em cadeia.


Direito Autoral
Matera Systems

Figura 47: Teste de operações em cadeia.

Filter
A função Filter já foi mencionada anteriormente quando estávamos
falando de Streams. É uma função bastante útil que serve para filtrar itens de
uma coleção baseado num predicado. Esse predicado, usualmente, é uma
função lambda.
Já vimos exemplos de como aplicá-lo em uma Stream, mas podemos
utilizá-lo também em Maps.
Direito Autoral
Matera Systems

Figura 48: Filter aplicado em value do HashMap.

Figura 49: Filter aplicado em key do Hashmap.

Interface Funcional
Interfaces funcionais são interfaces que possuem um método abstrato. O
diferencial dessas interfaces é que elas permitem que sejam utilizadas com
expressões lambda.
São definidas pela anotação @FunctionalInterface.
Direito Autoral
Matera Systems

Figura 50: Exemplo de interface funcional.

Isso deixa a implementação mais dinâmica e adaptável a novas


implementações.

Figura 51: Exemplo de uso de interface funcional.

Figura 52: Resultado do teste de interface funcional.

LocalDateTime
É a nova classe de manipulação de datas do JAVA 8. Abaixo algumas das
funcionalidades oferecidas pela biblioteca.
Direito Autoral
Matera Systems

Figura 53: Exemplo de utilização LocalDateTime.

Figura 54: Resultado teste de utilização LocalDateTime.

Podemos obter dados das datas de modo muito mais simples e fácil do
que foi apresentado até o JAVA 8.
Outra classe que é importante mencionar é a Instant. É utilizada para
calcular durações, em sua maioria.
Direito Autoral
Matera Systems

Figura 55: Teste de utlização com Instant e Duration.

Figura 56: Resultado de utilização de teste Instant e Duration.

Os métodos isAfter, isBefore e isEqual facilitam a comparação entre datas,


assim como vários outros métodos como definição de timezone com ZoneId ou
ZoneDateTime e manipulação de datas com plusDays.
Para formatar um LocalDate utilizamos uma classe formatadora
DateTimeFormatter.
Direito Autoral
Matera Systems

Figura 57: Exemplo de utilização de formatação de data.

Figura 58: Resultado do exemplo de utilização de formatação de data.

Optional
A intenção do Optional é encapsular retorno de métodos <T> prevenir o
erro NullPointerException. Isso porque com Optional saberemos se o valor está
ou não presente. Para criar um Optional podemos usar o método estático of.
Caso exista a possibilidade de criação de Optional de objetos nulos, usa-se
o ofNullable.

Figura 59: Exemplo optional.

O código acima recupera a conta e, caso ela não seja nula, a imprime.
Direito Autoral
Matera Systems

Podemos ainda, criar uma cadeia de métodos que irão manipular e filtrar
um optional, sem corrermos o risco de criarmos código que não sabem lidar
com itens nulos.

Collections
É um framework de um conjunto de interfaces que representam grupos
de dados. Coleções podem ser manipuladas através de interfaces,
implementações e algortimos já fornecidos pela Collections Framework.

Figura 60: Representação das interfaces do Collection Framework.

Ainda temos uma segunda hierarquia relacionada à mapas que não são
derivadas da Collection, mas podem ser manipuladas de forma muito
semelhante.

Figura 61: Representação de interfaces Map.


Direito Autoral
Matera Systems

Não existem implementações diretas da Collection, mas é ela quem


define as operações básicas para as demais.
As interfaces Collections também apresentam uma série de
implementações que são usadas para armazenamento de coleções.

Figura 62: Listagem das implementações disponíveis da Collections.

Set

Um Set é uma interface que define uma coleção que não permite

elementos duplicados. Ainda existe uma extensão chamada SortedSet que


possibilita a classificação dos elementos de forma ordenada.

Figura 63: Exemplo utilização Set.


Direito Autoral
Matera Systems

Como um Set não permite elementos duplicados, usamos o código hash


do objeto para garantir isso. Com isso, classes cujas instâncias são elementos de
hashSet devem implementar o método hashCode e equals.
A criação do código para equals e hash deve seguir o contrato definido
pela API JAVA que devem ser seguidos. Algumas IDEs como Eclipse e Netbeans
oferecem opções para gerar esses métodos automaticamente.

Figura 64: Exemplo objeto com compareTo, equals e hashCode.

List
É uma coleção ordenada, mas que pode conter elementos duplicados.
Fornece mais controle ao programador do que um Set e é possível acessar cada
elemento através de índices.
Existem duas implementações da interface List que são ArrayList e
LinkedList.
ArrayList oferece acesso aleatório rápido através de índice, ao contrário da
Direito Autoral
Matera Systems

LinkedList que possui acesso aleatório mais lento e necessita de um objeto “nó”
para cada elemento. Este nó possui o conteúdo e a referência para um próximo
elemento, sendo assim, ela ocupa mais memória.
LinkedLists são úteis para quando precisamos inserir ou remover algum
elemento do meio da lista, sem perder a ordenação. Ainda assim, o uso de
ArrayLists é mais comum, devido à sua adaptatividade.
A classe Collections fornece o método sort() que classifica conforme a
ordem natural (números crescentes ou ordem alfabética) ou ainda baseado na
implementação da interface Comparator.

Figura 65: Uso do sort para lista.

Figura 66: Resultado sort.


Direito Autoral
Matera Systems

Figura 67: Implementação da classe comparadora.

Figura 68: Teste de ordenação de lista com comparador.


Direito Autoral
Matera Systems

Figura 69: Resultado teste comparador.

É possível utilizar mais operações fornecidas pela Collections como


compareTo, add, remove e outras.

Map e HashCode
Map são coleções que utilizam hashcode. Dispõe de um mecanismo de
criação de índices para armazenar uma grande quantidade de dados e facilitar a
busca.
A interface Map do JAVA mapeia valores para as chaves que serão
utilizadas como índices. Chaves essas que não podem ser repetidas na mesma
coleção. Essa interface faz parte do java.util e não possui os métodos da
interface Collection.
Direito Autoral
Matera Systems

Figura 70: Exemplo utilização Map.

Figura 71: Resultado teste de utilização Map.

Basicamente, o Map faz a associação das chaves com os valores. A classe


HashMap é a implementação da classe Map e pode ser utilizada como coleção
de dados, vide exemplo acima.
O HashMap apresenta rápida busca e inserção de dados e permite inserir
valores e chaves nulas.
Os métodos utilizados para busca e inserção de dados são os fornecidos
pela interface Map, como values(), keySet(), entrySet() e put(). Os métodos set
retornam collections que podem ser percorridas e manipuladas.
A ordenação de HashMaps pode ser realizada por uma classe que
implemente a interface Comparator.
Direito Autoral
Matera Systems

Figura 72: Implementação do comparador de índices para o Map.

Figura 73: Utilização de ordenação de Map com comparador.


Direito Autoral
Matera Systems

Figura 74: Resultado da ordenação de Map com comparador.

Por último, ainda vale mencionar que existe a classe HashTable, que
funciona de modo bastante similar à HashMap, mas possui funções extras de
controle de concorrência e não aceita valores nulos.

Figura 75: Objeto que será utilizado para teste com MapTable.
Direito Autoral
Matera Systems

Figura 76: Teste de MapTable.

Figura 77: Resultado teste de MapTable.

Iterator
Possibilita percorrer uma coleção e remover seus elementos. As interfaces
da Collections herdam o método iterator, que retorna uma interface Iterator.
Direito Autoral
Matera Systems

Figura 78: Exemplo uso de iterator.

Figura 79: Resultado exemplo de uso de iterator.

Pode ser usada também para percorrer um Set. Oferece os operadores


hasNext e hasPrevious que retorna true caso exista um elemento posterior ou
anterior, respectivamente. Além desses, possui os métodos next e previous que
retornam o objeto correspondente, e ainda os métodos de adicionar e remover
elementos.
Direito Autoral
Matera Systems

Testes Unitários

TDD
É o conceito que define que antes de criarmos um código novo, devemos
escrever testes para ele. Isso garante que o código que está sendo escrito já terá
testes automáticos que o cobrem. Estes testes serão utilizados como métrica
durante toda a vida do projeto.

O que são?
Testes unitários se concentram em testar a menos unidade de um projeto
de software. É realizado em cima de uma unidade lógica, com dados o
suficiente para testar apenas essa unidade. Em JAVA, normalmente uma
unidade lógica é um método.

Por que usar?


● Previne futuros erros;
● Aumenta a confiabilidade do código;
● Permite alterações nos códigos testados com o mínimo de impacto;
● Testa cenários de modo automático;
● Em algumas situações serve como documentação de código.

O que testar?
Cada método deve ser uma unidade de negócio mínima, portanto, cada
método deve ser testado. É importante imaginar diversos cenários para cada
teste e escrevê-los. Quanto mais cenários, melhor.
Métodos get e set, normalmente, não são testados.
Direito Autoral
Matera Systems

JUnit
JUnit é um framework que facilita o desenvolvimento e execução de
testes unitários em JAVA. Fornece uma API para construir testes e aplicações
gráficas e em modo console para executar os testes criados.
Facilita a criação, execução automática e apresentação de resultados.
Fazer download do junit.jar em
https://github.com/junit-team/junit4/wiki/Download-and-Install e adicionar o jar
ao classpath do projeto.

Exemplo
Vamos criar uma classe Conta que possui os métodos: sacar e depositar.
Direito Autoral
Matera Systems

Figura 80: Classe de estudo para testes automáticos.


Direito Autoral
Matera Systems

Figura 81: Classe de teste com métodos de teste.

Figura 82: Resultado da execução dos testes automáticos.


Direito Autoral
Matera Systems

Figura 83: Mudança no código que irá quebrar o teste.

Figura 84: Teste de depósito quebrado.


Direito Autoral
Matera Systems

GIT
É um gerenciador de versões bastante utilizado em projetos. A maior
parte das operações é feita localmente primeiro, como commits e criação de
branches.

Fluxo de Trabalho
O GIT trabalha com uma área temporária de arquivos entre as alterações
locais e as que estão no servidor. Portanto, temos o Diretório de Trabalho
(Working Directory) que mantém todas as alterações vigentes realizadas
localmente, o Index que é a área temporária onde as alterações ainda não estão
no servidor. Por último, a HEAD que aponta para o último commit realizado.

Adicionar e Confirmar
Quando mudanças são realizadas, primeiro as adicionamos ao Index
usando git add <arquivo> ou git add .
Para realmente confirmar essas mudanças, é necessário fazer commit
com o comando git commit -m “Comentario do commit”. Isso quer dizer que
suas mudanças estarão no HEAD, mas ainda não estarão acessíveis no
repositório remoto.

Enviar alterações para o repositório remoto


Para enviar suas alterações para o repositório remoto, execute git push
origin master (master pode ser qualquer branch desejado).
Caso você não tenha clonado um repositório, mas sim criado um do zero,
para enviá-lo ao servidor remoto execute git remote add origin <servidor>.
Direito Autoral
Matera Systems

Criando branches
Branches são utilizados para desenvolver atividades isoladamente, de
modo que os commits e envios dessa atividade não afete o branch master. Eles
sempre são criados a partir de um branch “oficial” e, ao final do
desenvolvimento, são reintegrados ao branch principal.
Para criar um novo branch execute git checkout -b <nome_branch>.
Desse modo, seu espaço de trabalho estará nesse branch. Caso queira retornar
para a master execute git checkout master.
O checkout faz com que todos os arquivos locais que se tenha na
máquina fiquem de acordo com o branch de qual foi feito checkout. Qualquer
alteração que não tenha sido commitada poderá ser perdida.
O branch criado ficará disponível apenas localmente até que você o envie
para o servidor com o comando git push origin <nome_banch>. Para apagar o
branch criado execute git branch -d <nome_branch>.

Atualizações de branch e Merges


Para atualizar seu repositório local execute git pull. Isso irá fazer o
download para sua pasta local de todas as alterações realizadas no branch que
foram enviadas ao repositório remoto.
Para fazer um merge de um outro branch no seu diretório execute git
merge <branch>. As duas operações irão realizar merges automáticos das suas
alterações com as alterações que estão chegando do repositório remoto. Pode
ser que esses merges resultam em conflitos, que não são resolvidos
automaticamente e que precisam de atenção do desenvolvedor para serem
resolvidos manualmente.
Altere os arquivos utilizando uma ferramenta de merge, por exemplo,
WinMerge ou o próprio VsCode, e, após a solução de todos os conflitos, marque
esses arquivos com git add <arquivo>. Pode-se visualizar as diferenças entre
eles usando git diff <branch origem> <branch destino>.
Após marcá-los faça o commit do merge com os conflitos resolvidos.
Direito Autoral
Matera Systems

Squash Merge
Durante o desenvolvimento, é recomendado realizar commits frequentes.
Isso pode poluir a timeline de um branch. Nesse caso, seria muito melhor
termos apenas um commit com todas as alterações realizadas durante todo o
desenvolvimento. Para isso utilizamos o comando rebase.
O rebase é parecido com o merge e atualiza o branch atual com os
commits do branch de referência, porém, ao invés dele criar um commit de
“merge”, ele usa como base o branch de destino, colocando por cima todos os
commits realizados na branch atual, reescrevendo seu histórico.
Isso significa que o último commit do histórico não será o commit de
merge, e sim o último commit realizado na branch atual.
Para facilitar ainda mais, podemos condensar todos os commits realizados
até então em um único commit, despoluindo o log de commits e facilitando a
integração desse branch em outros.
Primeiro, encontramos o hash do último commit que foi realizado antes
de todos os que queremos condensar. Imagine que temos 3 commits que
iremos agrupar, desse modo, temos que pegar o hash do quarto commit. Com o
hash em mãos, executamos o comando git rebase -i <hash>. Isso irá abrir o
modo interativo do rebase, e todos os commits a partir desse poderão ser
alterados.
Direito Autoral
Matera Systems

Figura 85: Exemplo de rebase -i.

O rebase irá retornar alguns commits que poderão ser agrupados. Para
marcar quais deverão sofrer o squash, troque o pick por squash.

Figura 86: Exemplo de squash de commits com rebase.

Após salvar e fechar, os commits serão aplicados, e então podemos alterar


a mensagem do commit gerado a partir do squash.
Direito Autoral
Matera Systems

Figura 87: Exemplo de alteração de mensagem de commit de squash.

No final do processo, teremos apenas um commit no histórico, e não mais


3 commits como antes.

Maven
É uma ferramenta de automação e gerenciamento de projetos JAVA.
Fornece um meio de automatizar e padronizar a construção e publicação de
software.
Padroniza as versões e quais bibliotecas são utilizadas em cada projeto,
deste modo, o ambiente de construção do ambiente irá sempre utilizar essas
bibliotecas, não dependendo de cada desenvolvedor em separado.
Direito Autoral
Matera Systems

Instalação
Antes de instalar o maven, é necessário ter o JAVA instalado. Para fazer o
download do maven acesse http://maven.apache.org/download.html e escolha o
arquivo compatível com seu ambiente.
Então, descompacte o arquvivo em um diretório com o nome no formato
maven-[versão] (sem espaços).
Criar as variáveis de ambiente JAVA_HOME e M2_HOME. A primeira deve
conter a localização do diretório do JDK que está sendo utilizado e a segunda
deve conter a localização do diretório maven. Também deve-se alterar a variável
Path, adicionando a localização do diretório bin do maven.
Segue link com tutorial de instalação completo:
https://dicasdejava.com.br/como-instalar-o-maven-no-windows/ .

Criação de um projeto Maven


O maven oferece uma série de arquétipos que podem ser utilizados. Eles
já criam um esqueleto de organização do código que poderá ser utilizado como
projeto.
Para isso, abra uma comand window ou um terminal e digite mvn
archetype:generate -Dfilter=org.apache.maven.archetypes:
Esse comando irá trazer todos os arquétipos já disponíveis na biblioteca
oficial do apache.
Direito Autoral
Matera Systems

Figura 88: Exemplo de criação de projeto maven.

O arquétipo mais indicado é o maven-archetype-quickstart. Ele já vem


selecionado por padrão.
Após isso, é só preencher as propriedades de groupId, artifactId, version e
package que um projeto maven em branco será gerado.
Direito Autoral
Matera Systems

Figura 89: Resultado da criação de projeto maven.

Para importar esse projeto no eclipse, execute mvn eclipse:clean


eclipse:eclipse no diretório do projeto.
Direito Autoral
Matera Systems

Figura 90: Projeto importado no eclipse.

Dependências
As dependências são gerenciadas pelo arquivo pom.xml. Aqui são listadas
as dependências que o projeto utiliza, quais suas versões, quais plugins e quais
perfis existem para esse projeto.
Direito Autoral
Matera Systems

Figura 91: Exemplo de arquivo pom.xml.

O maven procura essas dependências em repositórios locais e públicos. O


repositório local fica no próprio computador do usuário, na pasta .m2/repository.
Toda vez que o maven precisa de uma dependência, ele primeiro procura na
pasta local e depois no repositório público
http://repo.maven.apache.org/maven2/.
Caso encontre, ele já adiciona a dependência na pasta local para futuras
consultas. Podemos informar qualquer dependência apenas a adicionando ao
pom.xml.
Todas as dependências são inseridas no .war ou .jar gerado na pasta target
do projeto. Para gerar esse arquivo basta executar mvn clean install.

Figura 92: Resultado da execução de mvn clean install.


Direito Autoral
Matera Systems

Figura 93: Jar gerado a partir da execução do mvn clean install na pasta target.

Lombok
É um framework que permite escrever código menos verboso. Isso
significa que será necessário escrever menos código para conseguir o resultado
desejado.
Basicamente, gera uma compilação dos métodos GET e SET, construtores
e padrão builder.

Instalação
Para utilizar o Lombok é necessário instalar o plugin do Lombok na sua
IDE.
IntelliJ: Para instalar, vá na parte de instalação de pugin do IntelliJ e
instale o Lombok Plugin.
Eclipse: Baixe e instale o .jar do Lombok na versão desejada em
https://mvnrepository.com/artifact/org.projectlombok/lombok.
Após instalar o plugin na IDE, adicionamos o lombok no projeto.
Direito Autoral
Matera Systems

Figura 94: Adicionar lombok no pom.xml

Podemos adicionar o jar ao projeto diretamente, caso não esteja usando o


maven.

Utilização

Figura 95: Exemplo de classe construída sem lombok.


Direito Autoral
Matera Systems

Figura 96: Exemplo de classe construída com lombok.

As notações ainda podem ser utilizadas por atributo, então, apenas o


atributo anotado terá os métodos get e set criados automaticamente.
Existem uma gama de anotações que podem ser utilizadas como
@EqualsAndHashCode, @ToString, @Builder, entre outras.
Para adicionar algumas anotações de uma só vez, existe a anotação
@Data que já adiciona getters, setters, equals, hashCode e o toString.
Como as anotações podem ser adicionadas por atributo, é sempre
importante prestar atenção no encapsulamento da sua classe. Caso existam
atributos que não podem ser modificados por outras classes, não faz sentido,
por exemplo, adicionar o @Data na classe.
O lombok facilita a leitura de código e retira a poluição de getters e setters
sempre criados nas classes, porém, ainda deve-se tomar cuidado para manter o
encapsulamento adequado para cada classe.

Arquitetura MVC
(Model-View-Controller)
Possibilita a divisão do projeto em camadas. Cada uma delas, Model, View
e Controller possui finalidades específicas e cada uma executa apenas o que lhe
é definido.
Direito Autoral
Matera Systems

A sua utilização promove o isolamento de regras de negócio, as separando


da lógica de apresentação e da interface com o usuário. Desse modo, podemos
ter várias apresentações de uma mesma regra de negócio, possibilitando o
reuso de classes sempre que possível.
A comunicação entre a regra de negócio e a interface é feita por meio de
controladores. Imagine que um botão é acionado na interface, então esse botão
acionará um controlador, que, por sua vez, irá acionar uma regra de negócio
específica.
O intermediador é o que chamamos de Controller. A regra de negócio
ficará na camada Model e a interface na View.

Model
É a camada responsável pelas regras de negócio, persistência no banco de
dados e classes de entidades. Ela recebe as requisições vindas da Controller e
gera respostas para essas requisições.

View
É a camada de visualização. Contém as interfaces com o usuário. Não
contém nenhuma lógica de negócio, apenas tabelas, grids, formulários e outras
formas de interface.
Essa camada aciona a Controller.

Controller
Serve como um intermediário que organiza os eventos da interface e os
direciona para a camada de modelo. Assim, é possível o reaproveitamento da
camada de modelo em outros sistemas, já que não existe dependência entre a
visualização e as regras de negócio.
Direito Autoral
Matera Systems

Vantagens
● Separação clara entre camadas;
● Manutenção facilitada;
● Reaproveitamento de código;
● Isolamento da camada de interface, sendo que qualquer alteração
realizada em telas não afeta o negócio.

Desvantagens
● Em projetos mais simples, acaba criando uma complexidade
desnecessária;
● Requer um tempo maior de modelagem de sistema;
● Exige bastante disciplina dos desenvolvedores envolvidos para manter a
organização do projeto.

Tratamento de Exceção
Exceções acontecem quando algo imprevisto ocorre, podem ser resultado
de erros de lógica ou acesso a recursos que não estejam disponíveis, como
tentar abrir um arquivo inexistente, ou acessar alguma posição em memória
que ainda não criada, ou ainda tentar conectar em algum servidor ou banco de
dados que não esteja disponível.
Por isso, em todos os locais onde identificamos que algum erro, seja de
lógica ou de acesso possam ocorrer, criamos o tratamento de exceções. Para
isso usamos os comando try e catch no JAVA.
Direito Autoral
Matera Systems

Figura 97: Exemplo de método que lança uma exceção.

Quando uma exceção é lançada, o código para o que está executando e


retorna essa exceção para a camada que a trata, por isso, no exemplo citado, não
é necessário adicionar else. Caso a exception seja lançada, nada mais dali para
baixo será executado.
Ainda no exemplo acima, utilizamos o comando throws. Isso significa que
essa exceção será lançada para o método que o chama.
Direito Autoral
Matera Systems

Figura 98: Exemplo de classe que captura a exceção.

Figura 99: Resultado da execução do exemplo.

Imagine um cenário onde, mesmo que uma exceção seja lançada, o


código ainda precise finalizar algo, como uma conexão com bando de dados, ou
fechar um arquivo que foi aberto para leitura/escrita.
Para esses cenários, utilizamos o comando finally. Ele será executado,
mesmo que uma exceção seja capturada.
Direito Autoral
Matera Systems

Figura 100: Exemplo de utilização de finally.

Nesse caso, mesmo que a exceção tenha sido capturada, o finally ainda
será executado como demonstrado no resultado abaixo.

Figura 101: Resultado comando finally.

Agora, imagine um cenário onde mais de um tipo de exceção precisa ser


tratada. O tipo Exception é o mais genérico, mas podemos ainda criar tipos de
exceções próprias que irão marcar cada tipo de exceção que podemos ter no
código.
Direito Autoral
Matera Systems

Figura 102: Exemplo de exceção personalizada.

Figura 103: Exemplo de utilização de exception personalizada.

Nesse caso, não precisamos ajustar o throws, pois a nossa exception


personalizada é uma derivada da classe Exception.
Direito Autoral
Matera Systems

Figura 104: Exemplo de tratamento de exceção personalizada.

Figura 105: Resultado do teste de exceção personalizada.


Direito Autoral
Matera Systems

Sistemas Web
Com a facilidade do acesso à internet, os sistemas web vêm dominando
cada vez mais o mercado. Além da praticidade ao usuário do sistema, de poder
acessá-lo de qualquer dispositivo, também facilita a manutenção. Com um
sistema web, não é necessário dar manutenção, atualizando máquina por
máquina, mas apenas uma única aplicação em um servidor.
Um servidor fica disponível para acesso em um endereço e pode ser
utilizado por pessoas, através de uma interface gráfica, ou até mesmo por outros
sistemas.
Existe mais de uma forma de se desenvolver um sistema web.
Inicialmente, uma única aplicação no servidor era responsável por devolver ao
usuário uma página com os recursos do front-end. Em algumas linguagens,
frameworks foram desenvolvidos para trazer uma maior dinamicidade para
essas páginas. Porém, ficava sob responsabilidade do servidor que montasse a
página com os resultados e entregasse prontas ao cliente. Cabia a mesma
aplicação conhecer o back-end e o front-end. Era o servidor que devolvia pronto
todo o HTML de tabelas com respostas de consultas.

API - Application Programming Interface


Uma API é um software que intermedia e disponibiliza o acesso às
funcionalidades de algum software, biblioteca, sistema operacional, etc.

Recurso
Um recurso é a representação de algo do mundo real para um elemento
acessível via web por meio de uma URI (Uniform Resource Identifier). Por
exemplo:
http://www.matera.com/bootcamp-for-girls-matera/alunas
Hipoteticamente se essa URI existisse ao acessá-la poderíamos ver, por
exemplo, os nomes das alunas aprovadas.
Direito Autoral
Matera Systems

Rest - Representational State Transfer


Nos anos 2000 foi criado por Roy Fielding o conceito de REST. Rest é um
padrão arquitetural (especificação) baseado em um protocolo (o mais utilizado é
o HTTP) que define algumas regras (constraints) e padroniza a maneira como os
softwares web, que seguem essa especificação, devem se comunicar. Essa
especificação se popularizou cada vez mais à medida que o tempo foi passando.
Com o REST o servidor fica responsável por devolver uma resposta, que
geralmente está em formato JSON ou XML (não mais HTML, CSS ou javascript),
e essa resposta pode ser consumida por um app, outro servidor e/ou uma outra
aplicação front-end. O cliente acessa o front-end. O front-end conhece o
servidor back-end e sabe manipular as requisições para renderizar as respostas
nas tabelas.

Constraints do Rest
Iremos conhecer agora as regras (constraints) definidas pelo Rest. São
elas:
● Cliente-Servidor: Essa regra define que precisamos de uma aplicação
cliente (um app, uma outra aplicação, um frontend e etc.) que consome
(faz requisições) para uma aplicação servidora (que disponibiliza um
serviço ou recurso). Além disso, essas aplicações cliente e servidor podem
evoluir de maneira totalmente independentes.
● Stateless: A aplicação servidora não pode guardar o “estado”
(informações) do cliente que fez a requisição. Ou seja, todas as
informações necessárias para o processamento da requisição devem ser
enviadas no momento da requisição.
● Cache: Uma API pode fazer cache da resposta de uma requisição. Por
exemplo, uma API que retorna os bairros de uma cidade não precisa
processar a todo momento a requisição uma vez que os bairros de uma
cidade não mudam com alta frequência. Isso facilita a escalabilidade das
aplicações, melhora o tempo de resposta e de performance.
Direito Autoral
Matera Systems

● Interface Uniforme: As operações de um sistema devem ser bem


definidas. Uma vez definida a interface de uma API essa definição
(contrato) deve ser seguido "à risca". Em outras palavras, uma API deve ser
criada utilizando os verbos http de maneira adequada, com identificação
dos recursos e representação dos recursos de maneira adequada bem
como com mensagens auto explicativas e Hypermidia (assim como a
partir de uma página web é possível navegar para outras páginas, a partir
de uma API podemos “navegar” para outra(s) API(‘s)).
● Sistema em Camadas: Essa regra trata sobre a possibilidade de entre o
cliente e servidor terem outros servidores (de cache, segurança e etc.).
Entretanto, tais servidores não podem interferir na requisição e resposta
das API’s e o cliente não deve conhecer quais camadas existem.
● Código sob demanda (opcional): Essa constraint é raramente usada em
API’s, mas, trata da possibilidade de códigos serem devolvidos na resposta
de uma API. Por exemplo, ao fazer uma requisição para uma API de
extrato, um código javascript poderia ser retornado para “montar” algum
gráfico pelo cliente que fez a requisição.
(Para saber mais sobre rest e suas constraints acesse:
https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm)

Rest X Restful
É comum termos dúvidas quanto a diferença entre Rest e Restful. Mas, a
diferença entre os dois é “simples”. Rest é o padrão arquitetural (especificação)
com suas constraints e Restful é uma API desenvolvida seguindo essas
constraints definidas pelo Rest.
Existem alguns estudos sobre o nível de maturidade das API’s Rest
mediante as constraints aplicadas por elas. Para saber mais acesse:
https://martinfowler.com/articles/richardsonMaturityModel.html
e https://www.brunobrito.net.br/richardson-maturity-model/
Direito Autoral
Matera Systems

Protocolo HTTP - Hypertext Transfer Protocol


O protocolo http é baseado em request/response (requisição/resposta) e utiliza
os verbos e status code para ter semântica nas requisições e respostas.

Verbos HTTP (métodos http)


Os verbos http são utilizados para sinalizar qual o tipo da “operação”
deseja-se realizar.
POST: Deve ser utilizado quando queremos cadastrar um recurso.
GET: Utilizado quando se deseja obter um recurso. Ao enviar uma
requisição utilizando GET os dados do tal recurso serão devolvidos na resposta
da requisição.
PUT e PATH: Devem ser utilizados quando desejamos atualizar uma ou
mais informações de um recurso. A diferença entre os dois é que o PUT vai
atualizar o recurso inteiro e o PATH permite a atualização parcial.
DELETE: Esse verbo deve ser utilizado quando deseja-se excluir um
recurso.

Status Code
Os Status Code são códigos que representam o status de processamento
de uma requisição. Isso ajuda o cliente que fez a requisição “agir” de maneira
adequada mediante a resposta da mesma. Assim como temos a semântica em
relação aos verbos HTTP, temos também uma semântica para os Status Code,
ou seja, devemos usar as famílias de maneira adequada.
Essas são as famílias de status code:
1XX – Informações Gerais (100 a 199)
2XX – Sucesso (200 a 299)
3XX – Redirecionamento (300 a 399)
4XX – Erro no cliente (400 a 499)
5XX – Erro no servidor (500 a 599)
Para conhecer mais sobre os status code acesse:
https://developer.mozilla.org/pt-BR/docs/Web/HTTP/Status
Direito Autoral
Matera Systems

Request
Uma requisição é composta por:
● Método Http (também conhecido como Verbos Http): GET, POST, DELETE,
PUT, PATH e outros.
● URI: é o que recursos que desejamos dentro do servidor
● Versão do protocolo http: Atualmente estamos na versão 2.0 do protocolo
http, entretanto a versão mais utilizada é a versão 1.1
● Cabeçalhos: Os cabeçalhos são informações do formato chave: valor. Essas
informações ajudam o servidor a processar adequadamente a requisição.
O protocolo http já define alguns cabeçalhos. Entretanto, podemos criar
nossos próprios cabeçalhos caso necessário.
● Corpo: O corpo da requisição não é obrigatório, dependendo do verbo
http utilizado. O mesmo serve para enviar informações para o servidor.
Por exemplo:

POST /api/v1/bootcamp-for-girls-matera/alunas HTTP/1.1


Content-Type: application/json
{
"nome": "Maria Joaquina"
}

Verbo: POST
URI: /api/v1/bootcamp-for-girls-matera/alunas
Versão do protocolo: HTTP/1.1
Cabeçalho: Content-Type: aplication/json (significa que estamos enviando um corpo no
formato json)
Corpo: { “nome”: “Maria Joaquina”}

Response
Uma resposta é composta por:
● Versão
● Status Code
Direito Autoral
Matera Systems

● Cabeçalhos
● Corpo
Por exemplo:

HTTP/1.1 201 Created


Content-Type: application/json
{
"id": 1,
"nome": "Maria Joaquina"
}

Versão do protocolo: HTTP/1.1


Status Code: 201 Created (Significa que a requisição foi processada adequadamente e o
recurso foi criado)
Cabeçalho: Content-Type: aplication/json (significa que estamos recebendo um corpo no
formato json)
Corpo: { “id”: 1, “nome”: “Maria Joaquina”}

Java e Spring
O Java possui diversos frameworks para desenvolvimento web. Nos
modelos antigos, quando o servidor respondia toda a página renderizada com
os dados preenchidos, frameworks como JSF e JSP eram amplamente
utilizados. Porém, com a chegada de ferramentas front-end como o Angular e
React, a maior parte das aplicações Java web que são desenvolvidas (e de
quaisquer outras linguagens de programação), são pensadas apenas como
servidores back-end.
Dos frameworks backend Java o Spring é o que mais se sobressai e
atualmente é o “padrão” de mercado.. O Spring não é apenas um framework, na
realidade ele é todo um ecossistema. São vários módulos com utilidades em
diversas áreas, abstraindo grande parte de configuração que precisaria ser feita
para o uso mais comum de cada um desses módulos. Alguns exemplos entre os
módulos mais comuns são Spring Data JPA, Spring Security e Spring Web.
Direito Autoral
Matera Systems

O Spring trabalha com um conceito de Beans. Beans são objetos que são
gerenciados pelo Spring através de um container de Inversão de Controle (IoC).
Inversão de Controle é o processo responsável por verificar as dependências dos
Beans sem precisar, de fato, criá-los. Após essa análise, os Beans são criados pelo
container de IoC, baseado nas suas dependências. Em outras palavras inversão
de controle é delegar para o Spring a instanciação dos nossos objetos que ele
gerencia e todo esse processo fica encapsulado pelo framework.
A forma mais simples de se trabalhar com Spring é através do uso de
anotações. Para se criar um Bean no Spring, basta anotar a classe criada com
@Component. Algumas outras variações estão disponíveis para uso, como
@Controller, @RestController, @Service e @Repository, porém a única diferença
entre elas é tentar facilitar a leitura do código (semântica).

Figura 106: Demonstração de como se criar um Bean.


Direito Autoral
Matera Systems

Como dito anteriormente, uma vez que uma classe foi anotada com
alguma anotação do Spring (@Component, @Service e etc.) o container de IoC
faz o gerenciamento da criação desses Beans, portanto, para utilizá-los, não é
necessária a instanciação. Para isso será utilizado um outro conceito
fundamental do Spring que é a injeção de dependências. Isso indicará ao Spring
que uma classe A depende da classe B. Nesse caso o container de IoC criará
primeiro a classe B e utilizará esse objeto criado para criar a classe A. Esse
conceito é amplamente utilizado em sistemas web. Dessa forma é possível fazer
um melhor gerenciamento de memória e concorrência entre as requisições.
Para indicar ao Spring que uma classe depende de outra, é possível fazer de três
maneiras diferentes. Usando a anotação @Autowired diretamente no atributo,
com @Autowired usando o método setter para o atributo ou através do
construtor do objeto (caso a classe tenha mais de um construtor que receba
parâmetros o @Autowired deve sinalizar qual construtor o Spring deve utilizar
na inicialização).
Direito Autoral
Matera Systems

Figura 107: Diferentes maneiras de se injetar uma dependência no Spring.

Os três métodos da figura 107 possuem vantagens e desvantagens. O


primeiro, utilizando @Autowired, é mais rápido de se escrever e ler. Já o
segundo exemplo, usando injeção por construtor, torna o código mais fácil de se
testar e perceber a complexidade, caso a classe tenha se tornado grande
demais, além da possibilidade de definir as variáveis como “final”, o que resulta
num melhor gerenciamento de memória. Já a abordagem usando o método
setter é menos conhecida e utilizada dada sua maior “verbosidade”. Também é
Direito Autoral
Matera Systems

possível utilizar o Lombok para diminuir a verbosidade de código do Spring na


injeção de dependências. Sendo a figura 108 equivalente ao terceiro exemplo
demonstrado na figura 107. A anotação @RequiredArgsConstructor pertence ao
Lombok, e é responsável por gerar um construtor utilizando todas as variáveis
classificadas como “final”.

Figura 108: Utilizando lombok para injetar dependências através do construtor.

Dessa forma, se utilizada a configuração padrão do Spring, é garantido


que, caso os Beans sejam utilizados em mais de uma classe, apenas uma única
instância desse objeto será compartilhada entre todas as que dependerem
desse Bean.

Spring na prática
Nesta seção nós veremos um pouco mais do spring na prática além
conhecermos o Spring boot, Spring Data JPA e um pouco sobre transações.

Spring Boot
O Spring Boot é um dos projetos dentro do ecossistema Spring. Ele tem
como principal missão facilitar o uso dos demais módulos do Spring, abstrair e
Direito Autoral
Matera Systems

simplificar as configurações dos mesmos (e de outras bibliotecas) além de já


disponibilizar um servidor de aplicação embarcado (por default ele utiliza o
tomcat). O Spring Boot trabalha de maneira opinativa, ou seja, ela já abstrai
várias configurações seguindo o que foi convencionado “ser o correto”. Isso é um
ponto positivo pois as convenções guiam o desenvolvedor em relação às
configurações e padronizam o uso do framework pela comunidade de
desenvolvedores de maneira geral. Mas, mesmo sendo opinativo e abstraindo
várias configurações é possível sobrescrevê-las.

Como iniciar um projeto


É possível criar todo o projeto Spring manualmente, mas uma ferramenta
chamada Spring Initializr foi criada com o objetivo de facilitar esse processo
inicial.
Para criar um projeto, basta acessar o site https://start.spring.io/, escolher
as dependências que irá utilizar, nome, versão do Spring, versão do Java, e as
demais configurações de pacote, como demonstrado na figura 109. Feito isso,
basta clicar em generate que um novo projeto Spring Boot será criado de
acordo com as configurações selecionadas. Para utilizar o projeto base criado,
basta extrair o zip baixado e abrir com a IDE Java de sua preferência.
Direito Autoral
Matera Systems

Figura 109: Exemplo de configuração inicial no Spring Boot Initializr.

Criado o projeto, tendo o Java e o Maven(ou Gradle) instalados, basta


compilar e executar a aplicação. Acompanhando os logs, você verá que um
servidor Tomcat será iniciado automaticamente e sua aplicação estará “de pé”.

Perfis de execução
Você verá que sua aplicação foi gerada com uma pasta “resources”, com
alguns arquivos dentro. Neste momento, apenas o arquivo
“application.properties” importa, os demais podem ser excluídos. O Spring é
capaz de entender tanto arquivos “properties” como arquivos “yaml”. Por
motivos de verbosidade, utilizaremos no projeto um arquivo do tipo “yaml”. Para
isso, exclua o arquivo “application.properties” e crie um arquivo com o nome
“application.yaml”. Nesse arquivo algumas configurações fixas podem ser
pré-definidas para a execução da aplicação. Através dele podemos definir
diferentes perfis de execução do Spring. Vários perfis podem estar ativos
simultaneamente. Para criar um novo perfil, basta criar um arquivo
“application-nome-do-perfil.yaml”. Um bom exemplo de uso é quando a
aplicação desenvolvida suporta diferentes tipos de banco de dados. Dessa
Direito Autoral
Matera Systems

forma podem ser definidas as configurações comuns em um arquivo


“application.yaml”, as configurações específicas de um banco de dados MySQL,
por exemplo, em um arquivo “application-mysql.yaml”, e as configurações de
outro banco de dados Oracle, por exemplo, em um arquivo
“application-oracle.yaml”. Tendo feita essa configuração, para alterar o banco de
dados onde sua aplicação é feita, basta trocar o perfil que será utilizado ao
inicializar, não precisando alterar nenhuma configuração. Outro exemplo
comum é criar perfis de teste, utilizando a mesma estrutura de uma pasta
“resources” no pacote exclusivo para testes.

Figura 110: Demonstração da criação de diferentes perfis de execução do Spring.

Configurações em código
O Spring dá também a possibilidade de fazer configurações via linhas de
código. Isso pode ser útil quando alguma informação precisa ser obtida, ou um
objeto instanciado, para que a configuração seja feita. Para criar uma classe de
configuração no Spring basta anotá-la com @Configuration, como no exemplo
abaixo. Classes de configuração geralmente são instanciadas antes dos demais
componentes.
Direito Autoral
Matera Systems

Figura 111: Exemplo de criação de uma classe de configuração.

Como demonstrado, é possível fazer essa configuração de pelo menos três


maneiras. Através da anotação @PostConstruct que não é exclusiva de
@Configuration, podendo ser utilizada em qualquer tipo de classe. Métodos
anotados com @PostConstruct serão executados logo após a criação da classe,
depois do construtor, quando o contexto da aplicação já estiver preparado. É
muito útil para disparar rotinas que precisam ser iniciadas com a aplicação. As
duas outras maneiras, conforme o exemplo da figura 111, utilizam sobre o
método uma anotação @Bean. Essa anotação geralmente é utilizada quando o
objetivo do método é fazer a configuração de um objeto específico na aplicação.
No primeiro caso, onde é retornado um objeto “ModelMapper”, o Spring não
tem esse objeto mapeado em lugar nenhum, e então nesse método
Direito Autoral
Matera Systems

“modelMapper()” ele é criado e configurado. Caso algum componente da


aplicação faça a injeção de dependência de um objeto do tipo “ModelMapper”,
o objeto criado nesse método será devolvido. No segundo caso, o Spring já criou
um objeto do tipo “ObjectMapper”, e o método pede que isso seja informado,
para poder configurá-lo. O método não retorna nenhum objeto, apenas pega a
informação que o Spring já possuía e faz a configuração de fuso horário sobre
ele.
Nos três casos, os métodos são executados apenas uma vez, durante a
subida do contexto da aplicação.

Tratamento Personalizado para Exceções


Caso seja necessário, o Spring possui a opção de configurar tratamentos
de erros customizados para exceções específicas. Isso pode ser utilizado para
padronizar mensagens de erro baseadas na exceção. Para isso deve ser criada
uma classe definida como @ControllerAdvice. Essa anotação se trata de um tipo
especial de Bean, em que podemos criar uma ordem de prioridade, . Após isso
deve ser criado um método anotado com @ExceptionHandler. Essa anotação
deve receber como parâmetro o tipo da exceção que será tratado pelo método
anotado. Isso mesmo, a anotação deve receber um parâmetro. Caso seja
necessário, o método criado poderá receber Beans, e também a própria exceção
ocorrida como parâmetro. Mais de um ExceptionHandler pode ser definido por
classe. Todo esse procedimento é demonstrado pela figura 16.
Direito Autoral
Matera Systems

Figura 112: Criando tratamento personalizado para retornos de exceções.

Na figura 16, caso um usuário não seja encontrado em uma busca pelo
código recebido em “UsuarioCrud.java”, uma exceção “RuntimeException” será
lançada com uma mensagem personalizada. A classe
“ExceptionHandlerConfig.java” configura que para qualquer exceção lançada no
sistema (pois todas derivam de Exception), o método
“defaultExceptionHandler(Exception excecao)” será chamado. Esse método cria
um objeto do tipo “ErroViewModel”, preenche com os dados da exceção
ocorrida, e recebida por parâmetro, e devolve com um status 404 (valor da
constante HttpStatus.NOT_FOUND) uma mensagem JSON com título e detalhe
preenchidos, de acordo com o exemplo.
Direito Autoral
Matera Systems

Manipulando Banco de Dados


A maioria das aplicações grandes necessitam de banco de dados. Para
facilitar a manipulação desses dados, o Java possui um framework chamado
Java Persistence API, ou apenas JPA. O JPA possui algumas anotações que
permitem que através de classes de entidade, uma estrutura equivalente ao
banco de dados seja montada. A partir disso, frameworks como o Hibernate
encapsulam as operações no banco de dados através de código Java.
Na figura 17, um mapeamento de uma tabela usuário é feito utilizando as
anotações do JPA. A anotação @Entity diz para o framework que a classe
Usuario é uma entidade de banco de dados. Caso o nome da tabela seja
diferente, uma anotação @Table com o nome da tabela pode ser adicionada.
Toda entidade precisa ter um @Id. Nesse caso, o campo “id” da entidade possui
duas anotações. A @Id simboliza que esse é o campo que deve ser levado em
consideração nas buscas por Id, na adição e alteração de novos usuários. A
anotação @GeneratedValue diz ao framework que esse valor deverá ser
auto-gerado. A estratégia “identity”, passada como parâmetro dessa anotação,
representa que esse campo é do tipo “auto increment” no banco de dados.
Alguns bancos de dados, como o Oracle, não suportam essa opção. Os demais
campos são todos anotados com @Column, que possui diferentes opções de
configuração. Uma coluna pode ter um limite de caracteres, no caso, em
agência, “length = 4” simboliza que no banco de dados esse campo só deve ter
até, no máximo, 4 caracteres. Por padrão @Column são nullables, para remover
essa opção e tornar todas obrigatórias, a propriedade nullable deve ser marcada
como true. Caso o nome da coluna seja diferente do nome da propriedade da
classe, o campo name pode ser preenchido com o nome da coluna no banco de
dados. Outra propriedade comumente usada é a “unique”, que simboliza que
aquele campo não pode se repetir na tabela.
Direito Autoral
Matera Systems

Figura 113: Criando tratamento personalizado para retornos de exceções.

O JPA também dá a opção de mapearmos relacionamentos entre tabelas.


Para isso, são utilizadas as anotações @OneToOne, @OneToMany, @ManyToOne
e @ManyToMany. Frequentemente, em conjunto com elas, vemos as anotações
@JoinColumn, @JoinColumns ou também @JoinTable.
Três propriedades das anotações de relacionamento que também
merecem atenção são “cascade”, “fetch” e “mappedBy”.
Direito Autoral
Matera Systems

A propriedade Cascade serve para explicar ao framework qual o


comportamento que será utilizado ao inserir ou excluir novos registros. Se um
objeto referente a uma tabela A, possui informações de um objeto de uma
tabela B, mas os dois não existem, os dois devem ser criados? O desenvolvedor
irá criar um de cada vez, ou o framework poderá criar tudo que for necessário
automaticamente? A mesma coisa vale para exclusão e atualização.
O campo fetch, por padrão Eager nos relacionamentos “ToOne” e Lazy nos
relacionamentos “ToMany”, tem relação com as consultas do objeto. Quando
um objeto da tabela A for consultado, o objeto B que pertence a A, deve ser
carregado também? Ou será carregado apenas quando realmente for utilizado?
Essa questão é muito importante e tem um grande impacto nos sistemas.
Carregando tudo inicialmente poderá gerar consultas pesadas de itens que
sequer serão utilizados. Carregar apenas no momento que utilizar pode gerar
diversas consultas em banco de dados, o que também é um problema. Outro
ponto importante é que consultas do tipo Lazy geralmente são aplicadas a
listas, mas não apenas, e só funcionam se forem inicializadas dentro de
transações, que será falado mais abaixo. Apenas a primeira chamada do objeto
Lazy gerará consulta e necessita estar em uma transação. A partir da primeira
chamada, o objeto será utilizado apenas em memória.
Por fim, a propriedade mappedBy deve ser preenchida quando o campo
estiver na entidade fraca do relacionamento (ou seja, a entidade que não é a
dona da FK). O valor de mappedBy deve ser o nome da propriedade Java na
outra classe do relacionamento, que se refere a classe atual. Em contrapartida, a
entidade forte do relacionamento deverá ter um @JoinColumn, simbolizando
que aquela propriedade é a coluna que representa o relacionamento entre as
tabelas.
Vale ressaltar que todo campo com mappedBy é sempre eager load,
independente de como esteja configurado.
Na figura 17, a entidade Lançamento possui o código do usuário pagador
e recebedor. Portanto, a entidade Usuário deve ter o campo mappedBy
preenchido com “pagador” para os débitos e “recebedor” para os créditos, pois é
o nome da variável equivalente ao relacionamento na entidade Lançamento.
Direito Autoral
Matera Systems

Figura 114: Mapeamento com relacionamento entre tabelas.

A figura 17 ainda possui um campo do tipo enum, TipoOperacao. Os


campos enum podem ser anotados como @Enumerated, mudando a
configuração padrão de utilizar o valor numérico do enum para salvar no
bancos, para passar a salvar o nome por extenso.
Direito Autoral
Matera Systems

Também é possível criar conversores customizados entre valores Java e os


dados no banco de dados. Um bom exemplo é utilizar em enums, para utilizar
valores que se encaixem melhor no contexto da aplicação, sem ocupar muito
espaço em banco de dados. Mas podem ser aplicados a qualquer tipo de objeto.

Figura 115: Criando um conversor customizado entre objetos java e valores no BD.

Para configurar um conversor, basta criar uma classe, anotá-la com


“@Converter(autoApply = true)” para que a conversão seja aplicada por padrão a
todos os objetos do tipo entre as entidades, e implementar a interface
AttributeConverter, codificando a conversão entre os objetos, como no exemplo
da figura 18. Nela “DEBITO” é salvo no banco de dados apenas como “D” e
“CRÉDITO” apenas como “C”. Da mesma maneira, quando resgatados do banco
de dados, os valores D e C são convertidos de volta para o valor equivalente no
enum “TipoOperacao”.
Direito Autoral
Matera Systems

Spring Data JPA


Como parte da integração do Spring com o JPA, existem algumas
facilitações que fazem toda a diferença durante o desenvolvimento de sistemas.
Sistemas inteiros em Java podem ser escritos sem nenhuma linha ou forma de
SQL, apenas usando as ferramentas do Spring.

A interface JpaRepository
O Spring possui uma interface que apenas de utilizá-la, todos os métodos
principais de um CRUD (criação, atualização, deleção e busca) são gerados
automaticamente, sem qualquer implementação necessária.
Conforme demonstrado na figura 19, para utilizar a interface
JpaRepository, basta criar uma interface de repositório para a classe de entidade
que deseja, no exemplo a classe @Entity utilizada é a “Usuario”, e fazer com que
a interface criada extenda a interface JpaRepository. Dois parâmetros serão
necessários para isso funcionar. O primeiro será o tipo da entidade desse
repositório, no caso “Usuario”, e o segundo deve ser o tipo do Id dessa entidade,
que nesse caso é Long. Nenhuma outra implementação será necessária. Para
ter acesso a todos os métodos disponíveis na interface JpaRepository, basta a
interface que acabou de criar no Bean onde deseja utilizá-la.
Os principais métodos utilizados serão findById, findAll, deleteById, save e
saveAndFlush. A diferença entre save e saveAndFlush é que para o método save
o framework decidirá qual o melhor momento de executar o script no banco de
dados, enquanto saveAndFlush fará com que o método seja imediatamente
executado.
Direito Autoral
Matera Systems

Figura 116: Demonstração de uso da interface JpaRepository.

Sempre que possível, o método save deve ser a melhor opção, porém há
casos em que imediatamente precisamos do valor alterado no banco de dados
devido a concorrência, para esses casos deve ser usado saveAndFlush. Ambos os
métodos fazem tanto a inserção de novos registros, quanto a atualização dos
que já existem, se diferenciando apenas pelo id ter sido informado ou não.

Queries complexas com JPQL


Caso seja necessário executar alguma query mais complexa, também não
há problemas. Para isso, basta declarar um método na sua interface de
Direito Autoral
Matera Systems

repositório e anotá-lo com @Query, passando como parâmetro dessa anotação


um script no formato String conhecido como JPQL. Esse script é uma espécie
de interface entre o Java e o SQL.

Figura 117: Utilizando JPQL para criar queries customizadas..

A figura 117 dá um exemplo de um select sobre a tabela definida pela


entidade usuário buscando por dados onde a propriedade “bloqueada” seja
igual ao parâmetro “contaBloqueada“ recebido pelo método.
Perceba que “Usuario” é o nome da classe Java, não necessariamente da
tabela no banco de dados. O mesmo vale para a propriedade bloqueada, que no
banco de dados foi mapeada como “conta_bloqueada” conforme a figura 16.
Também é utilizado “:” (dois pontos) na frente do nome do parâmetro
“contaBloqueada”, simbolizando que esse valor virá como parâmetro da função.
Na figura 117 também existe um segundo parâmetro “pageable”. Esse
parâmetro pertence ao Spring, e pode ser facilmente integrado com as
requisições REST recebidas pelo servidor. Ele é usado para definir tamanho,
número e ordenação da página de resultados que será retornada pela consulta.
Sim, o Spring também encapsula todo o tratamento de paginação para o
desenvolvedor.

A magia por trás das Derived Queries


Algumas queries menos complexas não há nem a necessidade de criar
um comando SQL ou JPQL para executá-las. As interfaces criadas com
JpaRepository podem utilizar de um conceito chamado Derived Queries.
Direito Autoral
Matera Systems

Derived Queries são métodos em que o nome da declaração do método


na interface define como aquele método irá se comportar. O framework fará
uma análise por palavras-chave no nome do método e, sem que qualquer
implementação precise ser feita pelo desenvolvedor, a query será gerada.

Figura 118: Exemplo de consulta utilizando Derived Query.

Na figura 118, a query gerada pelo método


“findByAgenciaContainingIgnoreCase” irá buscar uma página de usuários em
que a agência contenha em alguma parte do valor salvo no banco de dados, o
valor recebido por parâmetro. Isso irá gerar um SQL equivalente a “select * from
Usuario u where UPPER(agencia) like UPPER(‘%{0}%’)”, onde “{0}” é o valor
recebido por parâmetro. Nenhuma implementação ou código a mais é
necessário para que isso aconteça.
No link abaixo você encontra todas as palavras disponíveis para utilizar na
criação de Derived Queries.
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repository-q
uery-keywords
Direito Autoral
Matera Systems

Transações
As integrações do Spring com o JPA também permitem que transações
sejam criadas usando anotações. Caso em um processo seja necessário que
uma transação seja criada, basta anotá-lo com @Transactional. Ao serem
executados uma nova transação será imediatamente aberta. O commit
também será feito automaticamente após a conclusão do método. Se uma
exceção não tratada for lançada durante a execução do método, o rollback da
transação será executado imediatamente e toda a transação será desfeita. Mas
valem alguns pontos de atenção:
1. Consultas que sejam do tipo lazy-load, como objetos com listas de outros
objetos no banco de dados, devem obrigatoriamente estar dentro de uma
transação caso os campos com lazy-load precisem ser executados. Caso
os campos que são configurados como lazy-load não sejam utilizados, não
é necessário criar uma transação.
2. Transações só funcionam através de proxies do Spring. Isso significa que
chamadas do tipo self-invocations (quando um objeto chama ele mesmo,
ou algum método herdado), mesmo que anotados como @Transactional,
não terão transações iniciadas. Isso não gera nenhum tipo de alerta ou
erro, e cabe ao desenvolvedor se atentar a isso.
3. Por consequência do item 2, métodos protected e private nunca iniciam
transações.
4. Alterações em objetos consultados dentro de transações serão aplicadas
mesmo que não seja chamado o método save.
Direito Autoral
Matera Systems

Figura 119: Exemplo de um método transacional.

API’s Rest
O Spring abstrai e facilita bastante o desenvolvimento de API’s Rest. Por
conta disso precisamos escrever pouco código para criar um cadastro (CRUD)
completo. A figura 120 mostra o código necessário para criar 5 API’s para
cadastrar, atualizar, apagar e buscar um ou todos os usuários.
Direito Autoral
Matera Systems

Figura 120: Exemplo de API’s para fazer um CRUD simples de usuario.

Vamos entender agora o que foi feito, o que é enviado em cada requisição,
etc.
Na linha 13 temos a anotação @RequestMapping. Essa anotação é
utilizada para definir qual é o recurso que está sendo criado. No caso, é o
“/api/v1/usuarios”.
Direito Autoral
Matera Systems

GET - Buscar todos os usuários

Figura 121: API para listar todos os usuários

Na figura 121 estamos fazendo um findAll() para consultar todos os


usuários cadastrados no banco de dados. Vale destacar que estamos retornando
diretamente os objetos do banco de dados sem aplicar nenhuma conversão.
Isso ocorre porque o próprio Spring já faz essa conversão “automaticamente” de
Json para Objeto Java e vice-versa. Estamos também usando a anotação
@GetMapping que indica para o Spring que estamos criando uma API do tipo
GET que será executada quando o cliente fizer a requisição abaixo:

GET /api/v1/usuarios HTTP/1.1

Figura 122: Request, response e status code da requisição GET para /api/v1/usuarios
Direito Autoral
Matera Systems

Na figura 122 podemos ver o Json de retorno com dois usuários e Status
Code 200 da requisição GET.

GET - Buscar um usuário

Figura 123: API para buscar um usuário por id

Na figura 123 estamos usando a anotação @GetMapping(“/{id}”). Essa


anotação indica para o Spring que estamos criando uma API do tipo GET que
recebe uma variável na url. Para obter o valor da variável estamos usando a
anotação @PathVariable(“id”). Uma vez feito isso, o Spring se encarrega do resto.
Ele vai obter o valor, fazer a conversão (se necessário) e atribuir na variável “Long
id” que criamos. Esse método faz um findById com o id recebido no path da
requisição (é importante frisar que o nome da variável no @GetMapping e
@PathVariable precisam ser iguais, mas, da sua variável java, não. Por exemplo,
poderíamos ter: @GetMapping(“/{id”} e @PathVariable(“id”) e criar uma variável
Long identificador). Caso exista um objeto com o id recebido ele retorna o
objeto no corpo da requisição com o Status Code 200 OK, caso contrário, a
requisição é devolvida sem corpo com o status 404 NOT FOUND. Essa API será
executada quando o cliente fizer, por exemplo, a requisição abaixo:

GET /api/v1/usuarios/5 HTTP/1.1


Direito Autoral
Matera Systems

Figura 124: Request, response e status code da requisição GET para /api/v1/usuarios/5

Na figura 124 podemos ver o Json de retorno com o usuário encontrado e


Status Code 200 da requisição GET.

POST - Cadastrar usuário

Figura 125: API para criar um usuário

Na figura 125 estamos usando a anotação @PostMapping. Essa anotação


indica para o Spring que estamos criando uma API do tipo POST. Para obter o
corpo da requisição que o cliente enviou, estamos usando a anotação
@RequestBody. A partir dessa anotação o Spring vai obter o conteúdo do corpo
da requisição e fazer a conversão para a variável do tipo Usuario que criamos.
Em caso de sucesso, estamos retornando uma instância de ResponseEntity
passando dois parâmetros (objeto do tipo usuário que acabou de ser persistido
e o status code 201 CREATED). Essa API será executada quando o cliente fizer,
por exemplo, a requisição abaixo:

POST /api/v1/usuarios HTTP/1.1


Content-Type: application/json
Direito Autoral
Matera Systems

{
"agencia": "5678",
"conta": "928563"
}

Figura 126: Request, response e status code da requisição POST para /api/v1/usuarios

Na figura 126 podemos ver no response da requisição o usuário criado


com o id 6 e o status code 201.

PUT - Atualizar usuário

Figura 127: API para atualizar um usuário


Direito Autoral
Matera Systems

Na figura 127 estamos usando a anotação @PutMapping. Essa anotação


indica para o Spring que estamos criando uma API do tipo PUT. Para obter o
corpo da requisição que o cliente enviou, estamos usando a anotação
@RequestBody. Para obter o id passado no path da request estamos usando a
anotação @PathVariable. Estamos buscando o usuário pelo ID e atualizando os
valores recebidos na request. Em caso de sucesso e o usuário for encontrado,
estamos retornando uma instância de ResponseEntity passando dois
parâmetros (objeto do tipo usuário que acabou de ser atualizado e o status code
200 OK). Essa API será executada quando o cliente fizer, por exemplo, a
requisição abaixo:

PUT /api/v1/usuarios/5 HTTP/1.1


Content-Type: application/json
{
"id": 5,
"conta": "928555",
"agencia": "5678"
}
Direito Autoral
Matera Systems

Figura 128: Request, response e status code da requisição PUT para /api/v1/usuarios/5

Na figura 128 podemos ver no response da requisição o usuário atualizado


e o status code 200.

DELETE - Apagar usuário

Figura 129: API para deletar um usuário

Na figura 129 estamos usando a anotação @DeleteMapping. Essa


anotação indica para o Spring que estamos criando uma API do tipo DELETE.
Direito Autoral
Matera Systems

Para obter o id passado no path da request estamos usando a anotação


@PathVariable. Estamos buscando o usuário pelo ID e deletando. Em caso de
sucesso e o usuário for encontrado, estamos retornando o status code 200 OK e
nenhum corpo no response. Essa API será executada quando o cliente fizer, por
exemplo, a requisição abaixo:

DELETE /api/v1/usuarios/5 HTTP/1.1

Figura 130: Request, response e status code da requisição DELETE para /api/v1/usuarios/5

Na figura 130 podemos ver o response da requisição vazio e o status code


200.
Vale ressaltar que optamos por seguir a abordagem mais simples possível
de modo a facilitar o entendimento dos principais conceitos e anotações do
Spring. No “mundo real” poderiam e deveriam ser aplicadas validações, outros
tratamentos, regras de negócio e etc..

Teste Integrado
Já vimos de maneira bastante detalhada o que são testes unitários, TDD e
a importância dos mesmos. Entretanto, existem cenários onde é de suma
importância escrever um teste que cubra um fluxo como um todo e não apenas
um comportamento em específico. Nesses casos se faz necessário escrevermos
Testes Integrados. Você pode saber mais sobre o assunto clicando no link: TDD na
prática com Java usando @MockBean

Você também pode gostar