Java
Java é uma Linguagem de Programação multi-paradigma.
Core
Membros Estáticos
O elemento estático (static) é único, pertencente a classe em si, não a instância da classe. Ou seja, todos os objetos daquela classe compartilham a mesma estrutura de dado. É preciso acessar via classe, Ex: Classe.metodoStatic(value).
O principal motivo de usar static é design (constantes, utilitários, loggers, recursos compartilhados). (Na prática, o ganho de performance é bem pequeno).
Exceptions
Uma Exception é um objeto que representa uma anomalia (não necessariamente um erro) durante a execução. Se as exceptions não forem tratadas (caught) o programa é interrompido. Existem dois tipos de exceções: unchecked e checked exceptions. Elas se diferenciam com base no que o compilador obriga o programador a fazer.
Checked
O compilador obriga a tratar com um try-catch ou postergue com throws (até a main postergar também e a JVM receber e interromper o programa). Geralmente, se usa esse tipo de exception quando quer forçar o usuário a tratar a exception e quando tem chance realista de recuperar a operação no tratamento (geralmente operações de I/O). Exemplos:
IOException("Se não foi possível se conectar na rede pode tentar conectar de novo")ClassNotFoundSQLException
Unchecked
O compilador NÃO obriga a tratar (embora que, se não tratar, a thread é interrompido de qualquer forma). São as exceptions que herdam de RuntimeException. É bem menos verboso já que não obriga a tratar em toda chamada do método que pode lançar a exception. E, normalmente são erros que não são possível recuperar no tratamento. Por isso (e por se menos verboso) que, o mais comum é criar custom exceptions unchecked que representam alguma violação de regra de negócio ou anomalia (Nem tanto que as frameworks modernas como no Spring utiliza-se mais de unchecked . Exemplo:
NullPointerException(durante a execução tentar acessar um elemento null)UnderageUserException(Custom Exception)

Exceptions e Servidores WEB
Uma observação importante é que as Exceptions elas interrompem as threads (uma linha de execução do programa). Se for um programa "simples" que roda em uma thread apenas, checked e unchecked exceptions interrompem a execução dele. No entanto, em ambientes de execução em paralelo/concorrente como em servidores web Tomcat muda. Uma aplicação web é fundamentalmente diferente. Quando inicia-se, o Spring Boot, por exemplo, o código de não é executado diretamente. Sobe-se um servidor web embutido (geralmente o Tomcat), que fica escutando por requisições HTTP em uma porta (ex: 8080). O trabalho do web server é gerenciar um pool de threads/um conjunto de workers. Exemplo do que acontece:
- Um client faz uma request para a API (ex:
POST /users). - O Tomcat recebe essa requisição e aloca um worker (uma thread) do seu pool e para processar a request.
- A thread então executa todo o código. (Controller ⟶ Service ⟶ Repository, etc)
- Enquanto isso, o Tomcat continua com suas outras threads escutando por novas requests e atendendo outros clientes simultaneamente.
Se uma exception (checked ou unchecked) é lançada durante a execução de uma thread e ela não é tratada, ela vai "subindo" na Stack a partir de onde foi lançada até chegar no Tomcat/Spring. O servidor então captura e "mata" aquela thread. Ou seja, a execução morre mas o servidor (o maestro) continua vivo, rodando. (O spring boot por default imprime no console a stack trace e retorna a response com
HTTP 500 Internal Server Error).
Convenções
Nomes de diretórios/packages
Em Java, os diretórios são chamados de packages e a nomenclatura deles deve ser tudo em minúsculo, sem caracteres especiais e palavras sem separadores. Ex: a entidade PlayPackage fica na pasta /playpackage/*. Outro detalhe é que convenção mais comum é usar plural para packages que contêm múltiplas classes do mesmo tipo:
exceptions(para múltiplas classes de exceção)controllers(para múltiplos controllers)services(para múltiplos services)repositories(para múltiplos repositories) No entanto, se for um package mais específico/conceitual ou "uma exceção da comunidade", às vezes singular faz mais sentido.config(apesar de ter várias classes de configuração)dto(apesar de ter várias classes de DTOs) Mas ser plural ou não é um pouco mais flexível em termos de convenções
Nomes booleans
As variáveis booleanas usam prefixos como is, has, can, etc... para indicar um estado (true or false). Exemplo: uma variável/função que retorna se é a primeira compra ou não de um usuário. Por mais que a frase seja "it's the first purchase? " em Inglês, o foco é no estado da variável ("é ou não é", "is it or isn't"). Dessa forma o "certo" seria isFirstPurchase.
Além disso, é ideal evitar colocar os booleans na negativa, como isNotFirstPurchase porque é menos intuitivo, além de evitar duplas negações.
Arquivos
.jar
Java Archive é literalmente apenas um .zip com uma extensão diferente. (É possível fazer unzip -l arquivo.jar). A finalidade é empacotar vários arquivos em um só para facilitar a distribuição e o deployment. Um .jar tipico possui, por exemplo:
*.class: os bytecodes compilados- Recursos:
.properties,.png,.JSON,.xml, ... que o app precisa. META-INF: são os metadados. Dentro de todo.jarexiste esse diretório e dentro dele o arquivoMANIFEST.MF(arquivo chave-valor) que armazena metadados essenciais sobre o arquivo, como a versão que o criou e o ponto de entrada da aplicação.
JAR Executável
Esse são os .jar's que podem ser rodados com java -jar app.jar. A #JVM abre como se fosse um .zip, lê o arquivo META-INF/MANIFEST.MF e procura pelo atributo Main-Class (o ponto de entrada da aplicação). Se o manifesto contiver a linha Main-Class: com.meuprojeto.MinhaClassePrincipal a JVM entende que é para executar a main da classe especificada. Se não houver esse atributo, é printado na CLI a mensagem clássica de no main manifest attribute, in meuapp.jar.
JAR Lib
É um .jar que não é destinado a ser executado sozinho, seu propósito é ser executado em outra aplicação. Exemplos: spring-core.jar, junit.jar, ou qualquer lib que você baixa.
Compilação
Maven
Em vez de ter que baixar manualmente cada dependency (.jar) que o projeto necessita, é possível declarar um único arquivo central as dependências requeridas (pom.xml) .
JVM
Utilitários
Logging
Fazer logging é registrar informações relevantes sobre o comportamento da aplicação em tempo de execução. Exemplo:
- Mensagens de debug. (ajudam no desenvolvimento)
- Erros e Exceptions.
- Informações de processos de negócio. (ex: "Compra criada com sucesso").
- Métricas e auditoria. (ex: "Usuário X fez login às 10h05").
Em Java já vem embutido na JDK o java.util.logging (JUL), que é uma ferramenta básica de logging que quase ninguém usa por ser limitada e verbosa. Dessa forma, existe outras libs externas que são excelentes, como a SLF4J.
SLF4J
Simple Logging Facade for Java
Spring Ecosystem
Spring não é apenas um framework Java, é um conjunto de frameworks que podem ser combinadas, um ecossistema.
Desenvolvido para a JVM (Java Virtual Machine), o que significa que, embora tenha Java como sua principal linguagem, é oferecido suporte para outras linguagens de programação que também rodam na JVM (como Kotlin e Groovy).
Spring Frameworks
Spring Core
É o módulo que implementa os princípios fundamentais do Spring.
Spring Batch
Spring Security
Spring Data
Spring Boot
[[Distributed System#REST|REST]] API com Spring Boot
DTO
É sempre uma boa prática antes de retornar uma entidade na response mapear-la para uma DTO -Data Transfer Object. Se ao invés de mapear para um DTO retornar a entidade "crua", pode acarretar nos seguintes problemas:
- Alto Acoplamento: A response da API fica altamente dependente do nome do atributo da entidade. Se renomear um campo quebra a API para o frontend. Usar um DTO desacopla a API da sua persistência.
Service
A camada lógica de código onde ficam escritos as regras de negócio.
No contexto de um serviço que precisa localizar uma entidade, existem básicamente duas formas de implementar.
1. Check-Then-Act
Primeiro faz uma query para saber se a entity existe. Se não encontrar, lançar uma custom exception como ResourceNotFoundException. E, se encontrar, agir (delete/update).
Prós
- Boa legibilidade. O passo a passo do serviço se torna mais lógico, o que deve fazer.
- Controle total da error response. Controlar o que retornar de erro é mais fácil e escalável do que ficar tratando com
try-catchou um ponto de erro genérico no@ControllerAdvice.
Contras
- Potencialmente 2 consultas.
- Potencial (chance muito baixa) de race condition. Em sistemas de extrema concorrência, é teoricamente possível que outro processo delete a entidade entre o
findById / SELECTe a operaçãoDELETE/UPDATE. No entanto, as chances são muito baixas e, mesmo assim, pode-se contornar utilizando o@Transactional(tornando o conjunto de operações no SGBD atômicas).
2. Try-Catch
Executa a operação diretamente e trata a exceção que o JPA/Hibernate pode lançar se a entidade não existir.
Prós
- Potencialmente 1 consulta.
- Potencialmente menos código. Se não usar a estrutura
try-catchpara tratar a exception e deixar genérica pode ter menos código escrito do que a abordagem 1.
Contras
- Maior acoplamento. Ficar dependendo de exceptions específicas do SGBD driver ou do ORM (como o
EmptyResultDataAccessExceptiondo Hibernate/JPA) faz o código quebrar se o o driver/ORM mudarem ou atualizarem. - Menor flexibilidade. Se precisar do objeto antes de operar (Ex: registrar um log com os dados que foram apagados) com esta abordagem, você não tem o objeto.
Dessa forma, a melhor (disparadamente) abordagem é a 1 (para REST e para aplicações no geral). O argumento mais forte de se preferir a abordagem Try-Catch é que ela faz 1 consulta apenas e a Check-Then-Act faz 2 (ou mais). No entanto, os ORMs (como o Hibernate) possuem cache sofisticado que muitas vezes inibe a segunda query. Além disso, os Database Systems que a maioria das aplicações comunicam são OLTP (otimizados para um alto volume de transações rápidas e curtas).
Controller
A camada que interage com requests e responses exteriores a api.
Criação de Endpoints
Muitas vezes, quando para implementar uma lógica que "mexe" com várias entidades é difícil saber em qual Entity Controller colocar (ou se ainda precisa criar um novo).
Payment Gateway
Payment Gateway é o "portão de caminho" por onde um sistema/SaaS envie/redireciona/comunica a solicitação de pagamento para ser processado.
"Gateway" algo que controla a passagem de uma lado para outro. (Gateway é traduzido como portão de entrada). Gateway de pagamento controla a passagem de uma compra entre o cliente e a instituição financeira.
Criar um sistema de pagamento próprio, do zero, é uma tarefa difícil e burocrática. Envolve:
- Gerenciamento de fraude
- Implementar suporte a múltiplos métodos de pagamento
- Integrar com múltiplas instituições financeira
E é aqui que entra gateways de pagamento, delegando a responsabilidade, complexidade e burocracia toda para a empresa terceirizada e focando nas regras de negócio interna.
Stripe
Stripe é um dos gateways de pagamento mais famosos/padrão de mercado que existe.
Existem várias formas de integrar a Stripe como gateway de pagamento no sistema. Dentre elas, as abordagens mais comuns são:
Stripe Checkout
1. Usuária clica em comprar
backend cria uma CheckoutSession na Stripe (informando o produto, preço, URLs de sucesso e de falha). e envia a URL da página de checkout da stripe.
2. Redirect para a Stripe Checkout Page
O frontend do Sistema redireciona o usuário para uma página de checkout hospedada pela própria Stripe.
3. Processamento da Compra
O pagamento, validação dos dados, e etc acontece lá.
4. Webhook notification
O backend recebe a confirmação via Webhook se a compra foi ou não bem sucedida. Segue abaixo um exemplo de payload enviado pela stripe por webhook.
{
"id": "evt_...",
"object": "event",
"api_version": "2024-06-20",
"created": 1725660000,
"data": {
"object": {
// Objeto principal, como a Session, PaymentIntent, etc.
"id": "cs_test_...",
"object": "checkout.session",
"amount_total": 2000,
"currency": "usd",
"customer": "cus_...",
"metadata": {
// Dados personalizados que você pode ter enviado
},
"payment_status": "paid",
// ... outras propriedades do objeto
}
},
"livemode": false,
"pending_webhooks": 1,
"request": {
"id": null,
"idempotency_key": null
},
"type": "checkout.session.completed"
}Payment Intent + Stripe Elements/SDK
- backend cria um
PaymentIntent(intenção de pagamento) na Stripe (informando valor, moeda, cliente etc.) - No frontend utiliza-se a lib
Stripe.js + Elementspara montar a página de pagamento “na mão”. Campos de cartão, Pix, boleto etc. são iframes seguros fornecidos pela Stripe (não é lidado com dados sensíveis diretamente). - Stripe processa o pagamento e retorna o resultado direto ao frontend.
- Você ainda usa Webhooks no backend para validar e atualizar o status da compra.
Muitos times começam um projeto implementando a integração via Stripe Checkout e depois deixam mais customizado com Stripe Elements/Payment Intent.
Customer
é a abstração de um cliente do seu sistema na Stripe. Esse perfil armazena, por exemplo:
- Payment Methods. credit card, contas bancárias e etc
- Dados de Contato. email, nome, etc
- Histórico de Pagamentos.
- Subscriptions. assinatura, se aplicável Para cada usuário da aplicação que pretende realizar um pagamento, crie o seu Customer. Pode-se criar durante o cadastro ou quando chegar na checkout page.
Não é preciso criar explicitamente um Customer. Se criar (por ex um CheckoutSession) sem informar um Customer a Stripe gera um implicitamente/ por de baixo dos panos.
Environment Config
O Spring Boot utiliza do src/main/resources/*.{yml,properties} para configurar as variáveis da aplicação. Hoje em dia é preferível utilizar o formato .yml por ser mais legível (hierárquico, sem ficar repetindo os prefixos) e é padrão na industria (como o docker-compose.yml). O Spring Boot automaticamente detecta e carrega arquivos .yml/.yaml, então não precisa configurar nada adicional no código.
Exemplo
- [[Java#Exemplo#
application.yml|application]]. configurações aplicadas em todos os ambientes - [[Java#Exemplo#
application-dev.yml|dev]]. desenvolvimento - [[Java#Exemplo#
application-prod.yml|prod]]. produção
application.yml
server:
port: 8080
servlet:
context-path: /api # prefixo geral. (Use /vi/* nos controllers)
spring:
datasource:
url: jdbc:mysql://localhost/mydb
username: user
password: pass
jpa:
hibernate:
ddl-auto: update
show-sql: true # imprime as queries
properties:
"[hibernate.format_sql]": true # formata as queries
jwt:
public:
key: classpath:public.pem # classpath é src/main/resources/public.pem
private:
key: classpath:private.pem
stripe:
public:
key: ${STRIPE_PUBLIC_KEY:} # Usa env var ou fica vazio
secret:
key: ${STRIPE_SECRET_KEY:}
webhook:
secret: ${STRIPE_WEBHOOK_SECRET:}
enabled: trueapplication-dev.yml
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost/mydb
username: user
password: pass
jpa:
hibernate:
ddl-auto: update # ou create-drop para recriar DB a cada restart
show-sql: trueapplication-prod.yml
server:
port: ${PORT:8080}
spring:
datasource:
url: ${DB_URL}
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
jpa:
hibernate:
ddl-auto: update
show-sql: falseConfiguração das var envs
O Spring Boot (SB) é muito flexível na forma como mapeia as variáveis de ambiente para o programa. Ao inicializar uma aplicação, o SB procuro em certos locais para mapear as envs, onde as que estiverem no topo da lista sobrescrevem as que estão embaixo.
- Envs do Sistema. No terminal, digitar
export STRIPE_PUBLIC_KEY="pk_live_..."para os processos filho ao terminal conseguirem "enxergar" a var. (Ou então, em um container docker passar a flag-epara passar as var envsdocker run -e STRIPE_PUBLIC_KEY="pk_live_...".) - CLI args. Passar como args da CLI usando a
--seguido da proprieties .java -jar myapp.jar --stripe.public.key="sua-chave-publica-aqui" src/main/resources/*.{yml,properties}. (O.propertiestem maior precedência que o.yml). Exemplo: Se você configurar um value "estático" para uma key no.propertiesmas configurar uma var de ambiente (export ...) o SB iria ignorar a estática do properties. (Ou seja, as variáveis do.ymlou do.propertiesservem apenas para melhorar a legibilidade).
VS Code
É possível colocar as variáveis de ambiente no arquivo ./vscode/launch.json. esse é um arquivo que descreve como o VS Code deve rodar/debugar sua aplicação .Nele você define coisas como:
- qual classe ou programa rodar (
mainClass) - quais argumentos passar (
args) - quais variáveis de ambiente setar (
env) - qual diretório usar como
cwdExemplo simples para Java:
{
"version": "0.2.0",
"configurations": [
{
"type": "java",
"name": "Debug App",
"request": "launch",
"mainClass": "com.example.Application",
"projectName": "my-app",
"env": {
"SPRING_PROFILES_ACTIVE": "dev",
"MY_SECRET": "abc123"
}
}
]
}