O manipulador da função do Lambda é o método no código da função que processa eventos. Quando sua função é invocada, o Lambda executa o método do manipulador. A função é executada até que o manipulador retorne uma resposta, seja encerrado ou atinja o tempo limite.
Esta página descreve como trabalhar com manipuladores de função do Lambda em Java, incluindo opções para a configuração de projeto, as convenções de nomenclatura e as práticas recomendadas. Além disso, esta página apresenta um exemplo de uma função do Lambda em Java que aceita informações sobre um pedido, produz um recibo em formato de texto e armazena esse arquivo em um bucket do Amazon Simple Storage Service (Amazon S3). Para obter mais informações sobre como implantar a função após gravá-la, consulte Implantar funções do Lambda em Java com arquivos .zip ou JAR ou Implantar funções do Lambda em Java com imagens de contêiner.
Configuração do projeto de manipulador em Java
Ao trabalhar com funções do Lambda em Java, o processo envolve escrever seu código, compilá-lo e implantar os artefatos compilados no Lambda. Você pode inicializar um projeto Java Lambda de várias maneiras. Por exemplo, você pode usar ferramentas como o Maven Archetype para funções Lambda
Um projeto típico de função do Lambda em Java segue esta estrutura geral:
/project-root
└ src
└ main
└ java
└ example
└ OrderHandler.java (contains main handler)
└ <other_supporting_classes>
└ build.gradle OR pom.xml
Você pode usar o Maven ou o Gradle para criar seu projeto e gerenciar dependências.
A lógica principal do manipulador da sua função reside em um arquivo Java no diretório src/main/java/example
. No exemplo desta página, chamamos esse arquivo de OrderHandler.java
. Além desse arquivo, você pode incluir mais classes Java conforme necessário. Ao implantar sua função no Lambda, certifique-se de especificar a classe Java que contém o método manipulador principal que o Lambda deve invocar durante uma invocação.
Exemplo de código da função do Lambda em Java
O exemplo de código apresentado a seguir para uma função do Lambda em Java 21 aceita informações sobre um pedido, produz um recibo em formato de texto e armazena esse arquivo em um bucket do Amazon S3.
exemplo Função do Lambda em OrderHandler.java
package example;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.model.S3Exception;
import java.nio.charset.StandardCharsets;
/**
* Lambda handler for processing orders and storing receipts in S3.
*/
public class OrderHandler implements RequestHandler<OrderHandler.Order, String> {
private static final S3Client S3_CLIENT = S3Client.builder().build();
/**
* Record to model the input event.
*/
public record Order(String orderId, double amount, String item) {}
@Override
public String handleRequest(Order event, Context context) {
try {
// Access environment variables
String bucketName = System.getenv("RECEIPT_BUCKET");
if (bucketName == null || bucketName.isEmpty()) {
throw new IllegalArgumentException("RECEIPT_BUCKET environment variable is not set");
}
// Create the receipt content and key destination
String receiptContent = String.format("OrderID: %s\nAmount: $%.2f\nItem: %s",
event.orderId(), event.amount(), event.item());
String key = "receipts/" + event.orderId() + ".txt";
// Upload the receipt to S3
uploadReceiptToS3(bucketName, key, receiptContent);
context.getLogger().log("Successfully processed order " + event.orderId() +
" and stored receipt in S3 bucket " + bucketName);
return "Success";
} catch (Exception e) {
context.getLogger().log("Failed to process order: " + e.getMessage());
throw new RuntimeException(e);
}
}
private void uploadReceiptToS3(String bucketName, String key, String receiptContent) {
try {
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
.bucket(bucketName)
.key(key)
.build();
// Convert the receipt content to bytes and upload to S3
S3_CLIENT.putObject(putObjectRequest, RequestBody.fromBytes(receiptContent.getBytes(StandardCharsets.UTF_8)));
} catch (S3Exception e) {
throw new RuntimeException("Failed to upload receipt to S3: " + e.awsErrorDetails().errorMessage(), e);
}
}
}
Este arquivo OrderHandler.java
contém as seguintes seções de código:
-
package example
: em Java, isso pode ser qualquer coisa, mas deve corresponder à estrutura de diretórios do seu projeto. Aqui, usamospackage example
porque a estrutura de diretórios ésrc/main/java/example
. -
Declarações de
import
: use-as para importar as classes Java que sua função do Lambda requer. -
public class OrderHandler ...
: isso define sua classe Java e deve ser uma definição de classe válida. -
private static final S3Client S3_CLIENT ...
: isso inicializa um cliente do S3 fora de qualquer um dos métodos da classe. Isso faz com que o Lambda execute esse código durante a fase de inicialização. -
public record Order ...
: define o formato do evento de entrada esperado neste registroJava personalizado. -
public String handleRequest(Order event, Context context)
: este é o método principal do manipulador, que contém a lógica principal da sua aplicação. -
private void uploadReceiptToS3(...) {}
: este é um método auxiliar que é referenciado pelo método principal do manipuladorhandleRequest
.
O arquivo build.gradle
ou pom.xml
a seguir acompanha essa função.
Para que esta função funcione corretamente, seu perfil de execução deve permitir a ação s3:PutObject
. Além disso, certifique-se de definir a variável de ambiente RECEIPT_BUCKET
. Após uma invocação com êxito, o bucket do Amazon S3 deve conter um arquivo de recibo.
nota
Essa função pode exigir configurações adicionais para ser executada com sucesso sem atingir o tempo limite. Recomendamos configurar 256 MB de memória e um tempo limite de 10 segundos. A primeira invocação pode levar mais tempo devido a uma inicialização a frio. As invocações subsequentes devem ser executadas muito mais rapidamente devido à reutilização do ambiente de execução.
Definições de classe válidas para manipuladores Java
Para definir sua classe, a biblioteca aws-lambda-java-core
A interface RequestHandler
é um tipo genérico que usa dois parâmetros: o tipo de entrada e o tipo de saída. Ambos os tipos devem ser objetos. Neste exemplo, nossa classe OrderHandler
implementa RequestHandler<OrderHandler.Order, String>
. O tipo de entrada é o registro Order
que definimos dentro da classe, e o tipo de saída é String
.
public class OrderHandler implements RequestHandler<OrderHandler.Order, String> {
...
}
Quando você usa essa interface, o runtime do Java desserializa o evento no objeto com o tipo de entrada e serializa a saída em texto. Use essa interface quando a serialização interna funcionar com seus tipos de entrada e saída.
Para usar sua própria serialização, você pode implemente a interface RequestStreamHandler
. Com essa interface, o Lambda transmite ao seu manipulador uma transmissão de entrada e uma de saída. O manipulador lê bytes do fluxo de entrada, grava no fluxo de saída e retorna um void. Para ver um exemplo disso usando o runtime do Java 21, consulte HandlerStream.java
Se você estiver trabalhando somente com tipos básicos e genéricos (ou seja, String
, Integer
, =List
ou Map
) em sua função Java, não precisará implementar uma interface. Por exemplo, se sua função recebe uma entrada Map<String, String>
e retorna um String
, sua definição de classe e assinatura do manipulador podem ter a seguinte aparência:
public class ExampleHandler {
public String handleRequest(Map<String, String> input, Context context) {
...
}
}
Além disso, quando você não implementa uma interface, o objeto de contexto é opcional. Por exemplo, sua definição de classe e assinatura de manipulador podem ter a seguinte aparência:
public class NoContextHandler {
public String handleRequest(Map<String, String> input) {
...
}
}
Convenções de nomenclatura para manipuladores
Para funções do Lambda em Java, se você estiver implementando a interface RequestHandler
ou RequestStreamHandler
, seu método de manipulador principal deverá ser nomeado handleRequest
. Além disso, inclua a tag @Override
acima do seu método handleRequest
. Ao implantar sua função no Lambda, especifique o manipulador principal na configuração da sua função no seguinte formato:
-
<package>
.<Class>
: por exemplo,example.OrderHandler
.
Para funções do Lambda em Java que não implementam a interface RequestHandler
ou RequestStreamHandler
, você pode usar qualquer nome para o manipulador. Ao implantar sua função no Lambda, especifique o manipulador principal na configuração da sua função no seguinte formato:
-
<package>
.<Class>
::<handler_method_name>
: por exemplo,example.Handler::mainHandler
.
Definição e acesso ao objeto do evento de entrada
O formato de entrada JSON é o mais comum e padrão para funções do Lambda. Neste exemplo, a função espera uma entrada semelhante à seguinte:
{
"orderId": "12345",
"amount": 199.99,
"item": "Wireless Headphones"
}
Ao trabalhar com funções do Lambda em Java 17, é possível definir o formato do evento de entrada esperado como um registro em Java. Neste exemplo, definimos um registro dentro da classe OrderHandler
para representar um objeto Order
:
public record Order(String orderId, double amount, String item) {}
Esse registro corresponde ao formato de entrada esperado. Após definir o registro, você pode escrever uma assinatura de manipulador que aceita uma entrada JSON que está em conformidade com a definição do registro. O runtime do Java automaticamente desserializa esse JSON em um objeto Java. Em seguida, você pode acessar os campos do objeto. Por exemplo, event.orderId
recupera o valor de orderId
da entrada original.
nota
Os registros Java são um atributo somente dos runtimes do Java 17 e versões mais recentes. Em todos os runtimes do Java, é possível usar uma classe para representar os dados do evento. Nesses casos, você pode usar uma biblioteca como jackson
Outros tipos de eventos de entrada
Há muitos eventos de entrada possíveis para funções do Lambda em Java:
-
Integer
,Long
,Double
etc. – o evento é um número sem formatação adicional, por exemplo,3.5
. O runtime do Java converte o valor em um objeto do tipo especificado. -
String
: o evento é uma string JSON, incluindo aspas, por exemplo,“My string”
. O runtime converte o valor em um objetoString
sem aspas. -
List<Integer>
,List<String>
,List<Object>
etc. – o evento é uma matriz JSON. O runtime o desserializa em um objeto da interface ou tipo especificado. -
InputStream
: o evento é qualquer tipo JSON. O runtime transmite um fluxo de bytes do documento ao manipulador sem modificação. Desserialize a entrada e grave a saída em um fluxo de saída. -
Tipo de biblioteca: para eventos enviados por outros serviços da AWS, use os tipos na biblioteca aws-lambda-java-events
. Por exemplo, se sua função do Lambda for invocada pelo Amazon Simple Queue Service (SQS), use o objeto SQSEvent
como entrada.
Acesso e uso do objeto de contexto do Lambda
O objeto de contexto do Lambda contém informações sobre a invocação, a função e o ambiente de execução. Neste exemplo, o objeto de contexto é do tipo com.amazonaws.services.lambda.runtime.Context
e é o segundo argumento da função principal do manipulador.
public String handleRequest(Order event, Context context) {
...
}
Se sua classe implementa a interface RequestHandler
Se você realizar chamadas a outros serviços usando o AWS SDK, o objeto de contexto será necessário em algumas áreas importantes. Por exemplo, para produzir logs de funções para o Amazon CloudWatch, você pode usar o método context.getLogger()
para obter um objeto LambdaLogger
para registro em log. Neste exemplo, podemos usar o logger para registrar uma mensagem de erro se o processamento falhar por qualquer motivo:
context.getLogger().log("Failed to process order: " + e.getMessage());
Fora do registro em log, é possível usar o objeto de contexto para realizar o monitoramento da função. Para obter mais informações sobre o objeto de contexto, consulte Usar o objeto de contexto do Lambda para recuperar informações das funções em Java.
Uso do SDK da AWS para Java v2 em seu manipulador
Frequentemente, você usará as funções do Lambda para interagir com ou fazer atualizações em outros recursos da AWS. A maneira mais simples de interagir com esses recursos é usar o SDK da AWS para Java v2.
nota
O SDK da AWS para Java (v1) está em modo de manutenção e terá o suporte encerrado em 31 de dezembro de 2025. Recomendamos que você use somente o SDK da AWS para Java v2 daqui em diante.
Para adicionar dependências do SDK à sua função, adicione-as em seu arquivo build.gradle
para Gradle ou pom.xml
para Maven. Recomendamos adicionar somente as bibliotecas necessárias para sua função. No código de exemplo anterior, usamos a biblioteca software.amazon.awssdk.services.s3
. No Gradle, você pode adicionar essa dependência adicionando a seguinte linha na seção de dependências do seu build.gradle
:
implementation 'software.amazon.awssdk:s3:2.28.29'
No Maven, adicione as seguintes linhas na seção <dependencies>
do seu pom.xml
:
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>s3</artifactId>
<version>2.28.29</version>
</dependency>
nota
Esta pode não ser a versão mais recente do SDK. Escolha a versão apropriada do SDK para a sua aplicação.
Em seguida, importe as dependências diretamente na sua classe Java:
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.model.S3Exception;
O código de exemplo então inicializa um cliente do Amazon S3 da seguinte maneira:
private static final S3Client S3_CLIENT = S3Client.builder().build();
Neste exemplo, inicializamos nosso cliente do Amazon S3 fora da função do manipulador principal para evitar a necessidade que inicializá-lo a cada vez que nossa função é invocada. Após inicializar o cliente do SDK, você pode usá-lo para interagir com outros serviços da AWS. O código de exemplo chama a API PutObject
do Amazon S3 da seguinte forma:
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
.bucket(bucketName)
.key(key)
.build();
// Convert the receipt content to bytes and upload to S3
S3_CLIENT.putObject(putObjectRequest, RequestBody.fromBytes(receiptContent.getBytes(StandardCharsets.UTF_8)));
Acesso a variáveis de ambiente
No código do manipulador, você pode fazer referência a qualquer variável de ambiente ao usar o método System.getenv()
. Neste exemplo, referenciamos a variável de ambiente RECEIPT_BUCKET
definida usando a seguinte linha de código:
String bucketName = System.getenv("RECEIPT_BUCKET");
if (bucketName == null || bucketName.isEmpty()) {
throw new IllegalArgumentException("RECEIPT_BUCKET environment variable is not set");
}
Usar o estado global
O Lambda executa seu código estático e o construtor de classe durante a fase de inicialização antes de invocar a função pela primeira vez. Os recursos criados durante a inicialização permanecem na memória entre as invocações, para que você possa evitar ter que criá-los toda vez que invocar sua função.
No código de exemplo, o código de inicialização do cliente do S3 está fora do método do manipulador principal. O runtime inicializa o cliente antes que a função manipule seu primeiro evento e o cliente permanece disponível para reutilização em todas as invocações.
Práticas recomendadas de código para as funções do Lambda em Java
Adote as diretrizes da lista a seguir para usar as práticas recomendadas de codificação ao compilar suas funções do Lambda:
-
Separe o manipulador do Lambda da lógica central. Isso permite que você crie uma função mais fácil para teste de unidade.
-
Controle as dependências no pacote de implantação da função. O ambiente de execução do AWS Lambda contém várias bibliotecas. Para habilitar o conjunto de recursos e atualizações de segurança mais recente, o Lambda atualizará periodicamente essas bibliotecas. Essas atualizações podem introduzir alterações sutis ao comportamento de sua função do Lambda. Para ter controle total das dependências usadas por sua função, empacote todas as dependências em seu pacote de implantação.
-
Minimize a complexidade de suas dependências. Prefira frameworks mais simples que sejam carregados rapidamente no startup do ambiente de execução. Por exemplo, prefira frameworks de injeção de dependência Java (IoC) mais simples, como Dagger
ou Guice , em vez de frameworks mais complexos, como o Spring Framework . -
Minimize o tamanho do pacote de implantação às necessidades do runtime. Isso reduzirá a quantidade de tempo necessária para que seu pacote de implantação seja obtido por download e desempacotado antes da invocação. Para funções criadas em .Java, evite carregar a biblioteca do AWS SDK inteira como parte do pacote de implantação. Em vez disso, dependa seletivamente dos módulos que coletam os componentes do SDK necessários (por exemplo, DynamoDB, módulos do SDK do Amazon S3 e bibliotecas principais do Lambda
).
-
Aproveite a reutilização do ambiente de execução para melhorar a performance da função. Inicialize clientes SDK e conexões de banco de dados fora do manipulador de funções e armazene em cache os ativos estáticos localmente no diretório
/tmp
. As invocações subsequentes processadas pela mesma instância da função podem reutilizar esses recursos. Isso economiza custos reduzindo o runtime da função.Para evitar possíveis vazamentos de dados entre invocações, não use o ambiente de execução para armazenar dados do usuário, eventos ou outras informações com implicações de segurança. Se sua função depende de um estado mutável que não pode ser armazenado na memória dentro do manipulador, considere criar uma função separada ou versões separadas de uma função para cada usuário.
-
Use uma diretiva de keep-alive para manter conexões persistentes. O Lambda limpa conexões ociosas ao longo do tempo. A tentativa de reutilizar uma conexão ociosa ao invocar uma função resultará em um erro de conexão. Para manter sua conexão persistente, use a diretiva keep-alive associada ao runtime. Para obter um exemplo, consulte Reutilizar conexões com keep-alive em Node.js.
-
Use variáveis de ambiente para passar parâmetros operacionais para sua função. Por exemplo, se estiver gravando em um bucket do Amazon S3, em vez fixar no código o nome do bucket em que você está gravando, configure o nome do bucket como uma variável de ambiente.
-
Evite usar invocações recursivas em sua função do Lambda, em que a função invoca a si mesma ou inicia um processo que pode invocar a função novamente. Isso pode levar a um volume não intencional de invocações da função e a custos elevados. Se você observar um volume não intencional de invocações, defina a simultaneidade reservada da função como
0
imediatamente para limitar todas as invocações da função enquanto atualiza o código. -
Não use APIs não documentadas e não públicas no código da função Lambda. Para os tempos de execução gerenciados pelo AWS Lambda, o Lambda aplica periodicamente atualizações funcionais e de segurança às APIs internas do Lambda. Essas atualizações internas da API podem ser incompatíveis com versões anteriores, gerando consequências não intencionais, como falhas de invocação, caso sua função tenha dependência nessas APIs não públicas. Consulte a referência da API para obter uma lista de APIs disponíveis publicamente.
-
Escreva um código idempotente. Escrever um código idempotente para suas funções garante que eventos duplicados sejam tratados da mesma maneira. Seu código deve validar eventos adequadamente e lidar corretamente com eventos duplicados. Para obter mais informações, consulte Como torno minha função do Lambda idempotente?
.
-
Evite usar o cache do DNS Java. As funções do Lambda já armazenam as respostas de DNS em cache. Se usar outro cache do DNS, você poderá experimentar tempos limite de conexão.
A classe
java.util.logging.Logger
pode habilitar indiretamente o cache do DNS da JVM. Para substituir as configurações padrão, defina networkaddress.cache.ttlcomo 0 antes de inicializar o logger
. Exemplo:public class MyHandler { // first set TTL property static{ java.security.Security.setProperty("networkaddress.cache.ttl" , "0"); } // then instantiate logger var logger = org.apache.logging.log4j.LogManager.getLogger(MyHandler.class); }
-
Reduza o tempo necessário para o Lambda desempacotar pacotes de implantação criados em Java colocando seus arquivos
.jar
de dependências em um diretório lib/separado. Isso é mais rápido do que colocar todo o código de sua função em um único jar com um grande número de arquivos.class
. Para obter instruções, consulte Implantar funções do Lambda em Java com arquivos .zip ou JAR.