Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.
Get started with mocking and improve your application tests using our Mockito guide:
Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.
Get started with understanding multi-threaded applications with our Java Concurrency guide:
Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:
Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.
But these can also be overused and fall into some common pitfalls.
To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:
Get started with Spring and Spring Boot, through the Learn Spring course:
>> LEARN SPRINGExplore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:
Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.
I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.
You can explore the course here:
Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.
Get started with Spring Data JPA through the guided reference course:
Refactor Java code safely β and automatically β with OpenRewrite.
Refactoring big codebases by hand is slow, risky, and easy to put off. Thatβs where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.
Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions β one for newcomers and one for experienced users. Youβll see how recipes work, how to apply them across projects, and how to modernize code with confidence.
Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.
1. Introduction
In this tutorial, weβll learn how to use Spring Cloud Function.
Weβll build and run a simple Spring Cloud Function locally and then deploy it to AWS.
2. Spring Cloud Function Setup
To start with, letβs implement from scratch and test a simple project with two functions using different approaches:
- A String reverser, using a plain method
- And a greeter using a dedicated class
2.1. Maven Dependencies
The first thing we need to do is include the spring-cloud-starter-function-web dependency. This will act as our local adapter and brings in the necessary dependencies to run our function locally:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-function-web</artifactId>
<version>1.0.1.RELEASE</version>
</dependency>
Stay tuned as weβll modify this a bit when we are deploying to AWS.
2.2. Writing the Spring Cloud Function
With Spring Cloud Function, we can expose @Beans of type Function, Consumer or Supplier as individual methods:
@SpringBootApplication
public class CloudFunctionApplication {
public static void main(String[] args) {
SpringApplication.run(CloudFunctionApplication.class, args);
}
@Bean
public Function<String, String> reverseString() {
return value -> new StringBuilder(value).reverse().toString();
}
}
Like in this code, we can expose a reverse string feature as a Function, which our target functional platform can invoke.
2.3. Testing the Reverse String Function Locally
The spring-cloud-starter-function-web exposes the function as an HTTP endpoint. After we run the CloudFunctionApplication, we can curl our target to test it locally:
curl localhost:8080/reverseString -H "Content-Type: text/plain" -d "Baeldung User"
Note that the endpoint is the name of the bean.
And as expected, we get the reversed string as output:
resU gnudleaB
2.4. Scanning Spring Cloud Function in Packages
Apart from exposing our method as a @Bean, we could also write our software as classes that implement the functional interface Function<T, R>:
public class Greeter implements Function<String, String> {
@Override
public String apply(String s) {
return "Hello " + s + ", and welcome to Spring Cloud Function!!!";
}
}
We can then specify the packages to scan for relevant beans in application.properties:
spring.cloud.function.scan.packages=com.baeldung.spring.cloudfunction.functions
2.5. Testing the Greeter Function Locally
Again, we can start the app and use curl to test the Greeter function:
curl localhost:8080/greeter -H "Content-Type: text/plain" -d "World"
Note that the endpoint is the name of the class that implements the Functional interface.
And, no surprise, we get the expected greeting back:
Hello World, and welcome to Spring Cloud function!!!
3. Spring Cloud Function on AWS
What makes Spring Cloud Function so powerful is that we can build Spring enabled functions that are cloud agnostic. The function itself doesnβt need to know about how it was called or the environment it is deployed into. For example, we can easily deploy this greeter to AWS, Azure or Google Cloud platform without changing any of the business logic.
Since AWS Lambda is one of the popular serverless solutions, letβs focus on how to deploy our app into it.
So, letβs not wait any longer and deploy our function to the cloud!
3.1. Maven Dependencies
Remember the spring-cloud-starter-function-web dependency, which we added originally. Now itβs time to change that.
See, depending on where we are going to run the Spring Cloud Function, we need to add the appropriate dependency.
For AWS, weβll use spring-cloud-function-adapter-aws:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-function-adapter-aws</artifactId>
</dependency>
Next, letβs add the required AWS dependencies to handle Lambda events:
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-events</artifactId>
<version>2.0.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-core</artifactId>
<version>1.1.0</version>
<scope>provided</scope>
</dependency>
Finally, because we are going to upload the artifact generated by the maven build to AWS Lambda, we need to build an artifact that is shaded, meaning, it has all the dependencies exploded out as individual class files instead of jars.
The spring-boot-thin-layout dependency helps us to reduce the size of the artifact by excluding some dependencies that are not needed:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<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>
3.2. AWS Handlers
If we want to expose our string reverser again via an HTTP request, then Spring Cloud Function AWS ships with SpringBootRequestHandler. It implements AWSβs RequestHandler and is in charge of dispatching the AWS request to our function.
public class MyStringHandlers extends SpringBootRequestHandler<String, String> {
}
Spring Cloud Function AWS also ships with SpringBootStreamHandler and FunctionInvokingS3EventHandler as other examples
Now, it may seem a bit odd that the MyStringHandlers is just an empty class but it plays an important role in both acting as the entry point of the Lambda function and also defining its input and output types.
As weβll see in the screenshot below, weβll provide the fully qualified name of this class in the Handler input field of the AWS Lambda configuration page.
3.3. How Does AWS Know Which Cloud Function to Invoke?
As it turns out, even if we have more than one Spring Cloud Function in our application, AWS can invoke only one of them.
In the next section, weβll specify the cloud function name in an environment variable called FUNCTION_NAME on the AWS console.
4. Upload the Function to AWS and Test
Finally, letβs build our jar with maven, and then upload it via the AWS Console UI.
4.1. Create a Lambda Function on AWS Console and Configure It
On the AWS Lambda console page, in the Function code section, we can select a Java 8 runtime and simply click Upload.
After that, we need to indicate in the Handler field the fully-qualified name of the class that implements SpringBootRequestHandler, or com.baeldung.spring.cloudfunction.MyStringHandlers in our case:
π cloud1And then in Environment variables, we indicate which Spring function bean to invoke via the FUNCTION_NAME environment variable:
π cloud2And having done that, itβs time for us to test the Lambda function by creating a test event and supplying a sample string:
π cloud34.2. Testing the Function on AWS
Now, we Save our test, then click the Test button.
And, as expected, we get the same output as what we got when we tested the function locally:
π cloud44.3. Testing Another Function
Remember, we have one more function in our application: greeter. Letβs make sure that works too.
Weβll change the FUNCTION_NAME environment variable to greeter:
π cloud5Click the Save button and finally, the Test button again:
π cloud65. Conclusion
In summary, though in its early stages, Spring Cloud Function is a powerful tool for decoupling the business logic from any specific runtime target.
With it, the same code can run as a web endpoint, on a cloud platform, or as a part of a stream. It abstracts away all of the transport details and infrastructure, allowing the developer to keep all the familiar tools and processes, and focus firmly on business logic.
