Implementing a Lambda function in Java is tedious. If you use Spring Cloud Function, the framework seems to absorb the complexity of that area in a nice way. So, I tried to see if it was absorbed in a really good way and I could write it quickly.
By the way, there are many samples of Spring Cloud Function that are not proxy-integrated, but I couldn't find the integrated version, so I hope it helps to consider it.
Create a Maven project like the one below.
SpringCloudFunctionTest
├── pom.xml
├── serverless.yml
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── springcloudfunctiontest
│ │ ├── AWSLambdaHandler.java
│ │ ├── Hello.java
│ │ └── SpringCloudFunctionExampleApplication.java
│ └── resources
│ └── application.properties
└── test
└── java
└── com
└── springcloudfunctiontest
└── SpringCloudFunctionExampleApplicationTests.java
pom.xml should look like this.
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.techprimers.serverless</groupId>
<artifactId>spring-cloud-function-test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-cloud-function-test</name>
<description>Demo project for Spring Boot with Spring Cloud Function</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR2</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-function-adapter-aws</artifactId>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-events</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-core</artifactId>
<version>1.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot.experimental</groupId>
<artifactId>spring-boot-thin-layout</artifactId>
<version>1.0.10.RELEASE</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<shadedArtifactAttached>true</shadedArtifactAttached>
<shadedClassifierName>aws</shadedClassifierName>
</configuration>
</plugin>
</plugins>
</build>
</project>
Create a class that does `` `SpringApplication.run``` like a normal Spring Boot app.
SpringCloudFunctionExampleApplication.java
package com.springcloudfunctiontest;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringCloudFunctionExampleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudFunctionExampleApplication.class, args);
}
}
So, create an event handler for Lambda.
To be honest, I'm not sure why this class is empty ...
Since it is placed on the back end of API Gateway, set APIGatewayProxyRequestEvent
and APIGatewayProxyResponseEvent
for Input and Output, respectively.
AWSLambdaHandler.java
package com.springcloudfunctiontest;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
import org.springframework.cloud.function.adapter.aws.SpringBootRequestHandler;
public class AWSLambdaHandler extends SpringBootRequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {
}
Finally, create a business logic class.
``APIGatewayProxyRequestEventWhen
APIGatewayProxyResponseEvent```Specifications arejavadocLet's check.
However, even though Spring Cloud Function is compatible with multiple clouds, if you use such an interface, it will be a completely AWS-specific implementation.
Don't worry about the JSON handling of the last setBody being messy.
Hello.java
package com.springcloudfunctiontest;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
import org.springframework.stereotype.Component;
import java.util.function.Function;
import java.util.Map;
@Component
public class Hello implements Function<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {
@Override
public APIGatewayProxyResponseEvent apply(APIGatewayProxyRequestEvent input) {
Map<String, String> queryStringParameter = input.getQueryStringParameters();
String id = queryStringParameter.get("id");
String name = null;
if( id.equals("11111") ) {
name = "\"Taro\"";
} else {
name = "\"Nanashi\"";
}
APIGatewayProxyResponseEvent responseEvent = new APIGatewayProxyResponseEvent();
responseEvent.setStatusCode(200);
responseEvent.setBody("{\"name\":" + name + "}");
return responseEvent;
}
}
#Test code You can write test code normally.
SpringCloudFunctionExampleApplicationTests.java
package com.springcloudfunctiontest;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringCloudFunctionExampleApplicationTests {
@Test
public void HelloTest1() {
Hello hello = new Hello();
APIGatewayProxyRequestEvent request = new APIGatewayProxyRequestEvent();
Map<String,String> queryStringParameter = new HashMap<>();
queryStringParameter.put("id", "11111");
request.setQueryStringParameters(queryStringParameter);
APIGatewayProxyResponseEvent response = hello.apply(request);
assertThat(response.getBody()).isEqualTo("{\"name\":\"Taro\"}");
}
@Test
public void HelloTest2() {
Hello hello = new Hello();
APIGatewayProxyRequestEvent request = new APIGatewayProxyRequestEvent();
Map<String,String> queryStringParameter = new HashMap<>();
queryStringParameter.put("id", "22222");
request.setQueryStringParameters(queryStringParameter);
APIGatewayProxyResponseEvent response = hello.apply(request);
assertThat(response.getBody()).isEqualTo("{\"name\":\"Nanashi\"}");
}
}
#Deploy It's a hassle to manually upload to S3 or wait for the pipeline to build, so let's deploy it to Tekito with Serverless Framework because it is not an official version of the app.
serverless.yml
service: test
provider:
name: aws
runtime: java8
timeout: 30
region: ap-northeast-1
package:
artifact: target/spring-cloud-function-test-0.0.1-SNAPSHOT-aws.jar
functions:
hello:
handler: org.springframework.cloud.function.adapter.aws.SpringBootStreamHandler
events:
- http:
path: testapi
method: get
integration: lambda-proxy
request:
template:
application/json: '$input.json("$")'
When you deploy with this, the REST API will be returned properly like this!
$ curl -X GET https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/testapi?id=11111; echo
{"name":"Taro"}
#Conclusion Well, I haven't taken advantage of the Spring framework at all, so it feels like it's the same effort as when I created a normal Lambda event handler. Would it be appreciated if we started to use more framework functions?
This time, I understand that if you write a Lambda function in Java, it's easier to implement using APIGatewayProxyRequestEvent, and it's easier to deploy Serverless Framework.
Recommended Posts