Create the following folder structure:

pom.xml: (if using Gradle, replace pom.xml with build.gradle)
<?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>3.2.2</version> <relativePath/> </parent> <groupId>net.maxjava</groupId> <artifactId>SpringBootRESTAPIRedis</artifactId> <version>1.0</version> <packaging>jar</packaging> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <scope>compile</scope> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>5.1.2</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.30</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
plugins { id 'java' id 'org.springframework.boot' version '3.2.5' id 'io.spring.dependency-management' version '1.1.4' } group 'net.maxjava' version '1.0' repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation group: 'redis.clients', name: 'jedis', version: '5.1.2' compileOnly 'org.projectlombok:lombok:1.18.30' annotationProcessor 'org.projectlombok:lombok:1.18.30' }
Some notes about pom.xml:
- Use spring-boot-starter-parent as parent POM for Spring Boot application
- Spring Boot 3.2.5 requires Java 17
- Use jedis for interacting with Redis
- lombok is used to remove boilerplate code like get and set
- Use spring-boot-maven-plugin to re-package all dependencies into a single, executable jar
- By running mvn clean package, a jar artifact which is ready to run by using java -jar will be created
Some notes about gradle.build:
- Use org.springframework.boot plugin for Spring Boot application, to make the jar executable and re-package all dependencies into a single jar
- Spring Boot 3.2.5 requires Java 17
- Use jedis for interacting with Redis
- Use io.spring.dependency-management plugin for dependency management
- lombok is used to remove boilerplate code like get and set
- By running gradle clean build, a jar artifact which is ready to run by using java -jar will be created
MySpringBootWebApplication.java:
package maxjava; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MySpringBootWebApplication { public static void main(String[] args) { SpringApplication.run(maxjava.MySpringBootWebApplication.class, args); } }
Some notes about MySpringBootWebApplication.java:
- Mark a class with @SpringBootApplication and pass the class in SpringApplication.run will be good enough to run a Spring Boot application with web server (default setting)
- We could just put SpringApplication.run inside the static main function and annotate the same class for simplicity
MyController.java:
package maxjava.controller; import maxjava.caching.RedisService; import maxjava.model.Customer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/myapp") public class MyController { final String CUSTOMER_PREFIX = "customer:"; @Autowired RedisService redisService; @PutMapping("/customer") public void putCustomer(@RequestBody Customer customer) { try { String key = CUSTOMER_PREFIX + customer.getName(); String result = redisService.setCache(key, customer); if (!"OK".equals(result)) { // Throw exception or handle error } } catch (Exception e) { // Handle Exception } } @GetMapping("/customer") public Customer getCustomer(@RequestParam("name") String name) { Customer customer = null; try { String key = CUSTOMER_PREFIX + name; customer = (Customer) redisService.getCache(key, Customer.class); } catch (Exception e) { // Handle Exception } return customer; } }
Some notes about MyController.java:
- Specifying @RestController will make this a controller to handle web request. Using @RestController instead of @Controller to perform serialization automatically, without the need of using @ResponseBody
- The putCustomer is mapped to PUT /myapp/customer with the request body as the Customer object
- The Customer object will be passed on to the RedisService, using the name (a prefix is added to the key for segregation) as the key
- The getCustomer is mapped to GET /myapp/customer
- Using the name (prefix added) as the key, the Customer object will be retrieved from RedisService
Customer.java:
package maxjava.model; import lombok.Data; @Data public class Customer { private String name; private String email; private int age; }
Some notes about Customer.java:
- This is the object used in the request or response body on the Controller APIs
- Use Lombok @Data for get and set
RedisService.java:
package maxjava.caching; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.annotation.PostConstruct; import org.springframework.stereotype.Service; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; @Service public class RedisService { protected JedisPool jedisPool; private final String serverIP = "localhost"; private final int port = 6379; @PostConstruct private void init() { jedisPool = new JedisPool(serverIP, port); } public String setCache(String key, Object object) throws Exception { ObjectMapper objectMapper = new ObjectMapper(); String json = objectMapper.writeValueAsString(object); try (Jedis jedis = jedisPool.getResource()) { return jedis.set(key, json); } } public Object getCache(String key, Class clazz) throws Exception { ObjectMapper objectMapper = new ObjectMapper(); try (Jedis jedis = jedisPool.getResource()) { String json = jedis.get(key); Object object = objectMapper.readValue(json, clazz); return object; } } }
Some notes about RedisService.java:
- During PostConstruct, initiate the JedisPool, which is thread safe for sharing for all requests
- The setCache function stores the object in Redis. First serialize the object into json string then call the Jedis set function
- The getCache function retrieve the object (as json string) from Redis then de-serialize the json string into actual object
Sample requests and responses:
- Put a Customer record:
curl --request PUT '<CONTEXT_PATH>/myapp/customer' --header 'Content-Type: application/json' --data-raw ' { "name": "Ethan", "email": "ethan@maxjava.net", "age": 26 }'
- Retrieve the Customer record:
Response:curl --request GET '<CONTEXT_PATH>/myapp/customer?name=Ethan'{ "name": "Ethan", "email": "ethan@maxjava.net", "age": 26 }