如何在Jersey资源中获取AWS Lambda调用ARN以实现环境配置切换?
getInvokedFunctionArn in Jersey Resources for AWS Serverless Java Container Great question! When using AWS Serverless Java Container to wrap a Jersey application in Lambda, accessing the Lambda Context from your Jersey resources requires some setup since they aren't directly connected by default. Below are two clean, production-ready approaches to solve this problem:
Approach 1: Inject Lambda Context via Request Attributes (Simple & Direct)
This method uses Jersey's request enhancement to pass the Lambda Context (or just the invoked ARN) into your request context, which you can then access in your resource class.
Step 1: Create a Request Enhancer
First, define a RequestEnhancer to attach the Lambda Context (or its ARN) to the Jersey request:
import com.amazonaws.serverless.proxy.jersey.JerseyLambdaContainerHandler; import com.amazonaws.services.lambda.runtime.Context; import javax.ws.rs.container.ContainerRequestContext; public class LambdaContextRequestEnhancer implements JerseyLambdaContainerHandler.RequestEnhancer { private final Context lambdaContext; public LambdaContextRequestEnhancer(Context context) { this.lambdaContext = context; } @Override public void enhanceRequest(ContainerRequestContext requestContext) { // Store the full Lambda Context (or just the ARN if you only need that) requestContext.setProperty("lambdaContext", lambdaContext); requestContext.setProperty("invokedFunctionArn", lambdaContext.getInvokedFunctionArn()); } }
Step 2: Update the Lambda Handler
Modify your PetLambdaHandler to set the enhancer before processing the request:
@Override public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException { // Attach the enhancer to pass context data to Jersey handler.setRequestEnhancer(new LambdaContextRequestEnhancer(context)); handler.proxyStream(inputStream, outputStream, context); }
Step 3: Access the ARN in Your Jersey Resource
Use @Context to inject the request context and retrieve the ARN:
@Path("/pet") public class PetResource { @Context private ContainerRequestContext requestContext; @POST @Path("/") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.WILDCARD) public Response createPet() { // Get the invoked function ARN String invokedArn = (String) requestContext.getProperty("invokedFunctionArn"); // Extract the environment alias (test/prod) from the ARN // ARN format: arn:aws:lambda:<region>:<account>:function:<name>:<alias/version> String environment = invokedArn.split(":")[invokedArn.split(":").length - 1]; // Load the corresponding properties file (e.g., test.properties or prod.properties) // ResourceBundle config = ResourceBundle.getBundle(environment); return Response.status(200) .entity(String.format("{\"result\": \"success\", \"environment\": \"%s\"}", environment)) .build(); } }
Approach 2: Dependency Injection (Clean & Reusable)
If you prefer a more elegant solution with direct injection of the Lambda Context into your resources, use Jersey's HK2 dependency injection system.
Step 1: Create a Thread-Local Context Holder
This holder safely stores the Lambda Context per request (critical for Lambda's thread reuse):
import com.amazonaws.services.lambda.runtime.Context; public class LambdaContextHolder { private static final ThreadLocal<Context> CONTEXT_HOLDER = new ThreadLocal<>(); public static void setContext(Context context) { CONTEXT_HOLDER.set(context); } public static Context getContext() { return CONTEXT_HOLDER.get(); } public static void clearContext() { CONTEXT_HOLDER.remove(); } }
Step 2: Update the Lambda Handler to Manage the Holder
Set and clear the context in a try/finally block to avoid cross-request contamination:
@Override public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException { try { LambdaContextHolder.setContext(context); handler.proxyStream(inputStream, outputStream, context); } finally { LambdaContextHolder.clearContext(); // Clean up to prevent thread reuse issues } }
Step 3: Configure HK2 Binding for Dependency Injection
Create a factory and binder to make the Lambda Context injectable:
import com.amazonaws.services.lambda.runtime.Context; import org.glassfish.hk2.api.Factory; import org.glassfish.hk2.utilities.binding.AbstractBinder; // Factory to provide the Lambda Context from the holder public class LambdaContextFactory implements Factory<Context> { @Override public Context provide() { return LambdaContextHolder.getContext(); } @Override public void dispose(Context context) { // No cleanup needed since the holder handles it } } // Binder to register the factory with Jersey's DI system public class LambdaContextBinder extends AbstractBinder { @Override protected void configure() { bindFactory(LambdaContextFactory.class).to(Context.class).in(javax.inject.Singleton.class); } }
Step 4: Register the Binder in Your Resource Config
Update your ResourceConfig to include the custom binder:
private static final ResourceConfig jerseyApplication = new ResourceConfig() .register(PetResource.class) .register(JacksonFeature.class) .register(new LambdaContextBinder()); // Add the DI binder
Step 5: Inject the Context Directly in Your Resource
Now you can inject the Lambda Context directly using @Inject:
@Path("/pet") public class PetResource { @Inject private Context lambdaContext; @POST @Path("/") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.WILDCARD) public Response createPet() { String invokedArn = lambdaContext.getInvokedFunctionArn(); String environment = invokedArn.split(":")[invokedArn.split(":").length - 1]; // Load your environment-specific config here // ... return Response.status(200) .entity(String.format("{\"result\": \"success\", \"environment\": \"%s\"}", environment)) .build(); } }
Key Notes
- ARN Parsing: Always use
invokedArn.split(":")[invokedArn.split(":").length - 1]to get the alias/version instead of hardcoding an index—this avoids issues if AWS changes the ARN format. - Thread Safety: When using thread-local storage, always clear the context in a
finallyblock to prevent data leakage between Lambda invocations (Lambda reuses execution environments/threads). - Configuration Loading: Use
ResourceBundle.getBundle(environment)to load your.propertiesfiles (e.g.,test.propertiesfor thetestalias) seamlessly.
内容的提问来源于stack exchange,提问作者alessmar

