An extra step we need to take here is the creation of a ‘docker-compose.yml‘ file to run Redis. We are going to be using the official image provided by Docker Hub. The content of our compose file will be:
Once we have Redis running and, our new endpoint ready to go, it is time to start configuring Redis.
First, we are going to create our configuration class. To activate the cache capabilities on Spring we can use the configuration and enable configuration annotations:
@Configuration
@EnableCaching
And, surprisingly, that’s all the Java configuration we need to write because Spring auto-configuration takes care of the rest. To allow this, we need to add our Redis properties to the ‘application.properties‘ file.
As simple as that, now, if we have the docker container running, when we start our application it will be able to talk to Redis.
Now on the service, we just need to add the appropriate annotation to indicate we want to use the cache.
@Cacheable(value = "md5-cache")
@Override
public String generateMd5(final String text) {
log.info("Generating the MD5 hash...");
try {
final MessageDigest md = MessageDigest.getInstance("MD5");
md.update(text.getBytes());
return DatatypeConverter.printHexBinary(md.digest()).toUpperCase();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Unable to get MD5 instance");
}
}
And, with this, everything should be in place to test it. We just need to run our application and invoke our endpoints, for example, using ‘curl’.
curl http://localhost:8080/api/hashes/hola
The result should be something like this:
2020-11-01 10:30:06.297 : Generating the MD5 hash...
As we can see, invoking multiple times the endpoint only created the first log line and, from this point, any invocation we’ll be taken from the cache.
Obviously, this is a pretty simple example but, this can help us to increase the performance of our system for more complex operations.
Today, we are going to explore a little bit one of the cache options have available when working with Java projects. This option is Caffeine.
Caffeine is a high performance, near optimal caching library based on Java 8. For more details, see our user’s guide and browse the API docs for the latest release.
— Caffeine wiki —
Let’s do it.
As a base project we are going to use a similar code to the one written for the previous article “Cache: Spring Boot + Ehcache“.
The only thing we are going to change is we are going to duplicate the existing endpoint to allow be able to try two different ways of working with Caffeine.
Once we have our new endpoint ready to go, it is time to start configuring Caffeine. We are going to take two different approaches:
Make use of Spring injection capabilities.
More manual approach.
Leveraging Spring injection capabilities
First, we are going to create our configuration class. To activate the cache capabilities on Spring we can use the configuration and enable configuration annotations:
@Configuration
@EnableCaching
With this, we can add now the beans to create our cache and configure appropriately Caffeine.
@Bean
@SuppressWarnings("all")
public Caffeine caffeineConfig() {
return Caffeine.newBuilder()
.maximumSize(50)
.expireAfterWrite(10, TimeUnit.SECONDS)
.removalListener(CacheEventLogger.removalListener());
}
@Bean
@SuppressWarnings("all")
public CacheManager cacheManager(final Caffeine caffeine) {
final CaffeineCacheManager caffeineCacheManager = new CaffeineCacheManager();
caffeineCacheManager.setCaffeine(caffeine);
return caffeineCacheManager;
}
As you can see, something pretty simple. I have tried to mimic the configuration set for the Ehcache example on the previous article. If you have not done it, you can check it now. A summary of this configuration is:
Cache size: 50 entries.
And the expiration policy: Expiration after the write of 10 seconds.
Now on the service, we just need to add the appropriate annotation to indicate we want to use the cache.
@Cacheable(cacheNames = MD5_CACHE_ID)
@Override
public String generateMd5SpringCache(final String text) {
log.info("The value was not cached by Spring");
return generateMd5(text);
}
That simple.
Manual approach
We have the possibility of creating the cache manually. This can be desired for multiple reasons like: not having Spring available, wanting component isolation, not dealing with the cache manages an multiple caches or, whatever reason, us, as a developers decide that it fits the best our use case.
To do this manual configuration we just need to exclude the configuration class and the beans’ creation, create the cache in our service class and invoke it when a request arrives.
Nothing too fancy. It is worth it a note about the method ‘generateMd5Wrapper‘. It is completely unnecessary, the only reason it has been created is to be able to write an extra log line to run the demo and to have visible effects of the cache working.
The last thing we have defined is a removal listener to log when an object is removed from the cache. Again, this is just for demo purposes and, it is not necessary.
2020-10-31 08:15:19.610 : The value was not cached by Spring
2020-10-31 08:15:35.316 : The value was not cached by Spring
2020-10-31 08:15:35.317 : Key hola was removed (EXPIRED)
2020-10-31 08:15:39.717 : The value was not cached manually
2020-10-31 08:15:55.443 : The value was not cached manually
2020-10-31 08:15:55.443 : Key hola was removed (EXPIRED)
As we can see, invoking multiple times the endpoint only created the first log line and, it is just after waiting for some time (more than 10 seconds) when the cache entry gets expired and re-created.
Obviously, this is a pretty simple example but, this can help us to increase the performance of our system for more complex operations.
Today, we are going to explore a little bit one of the cache options have available when working with Java projects. This option is Ehcache.
Ehcache is an open-source, standards-based cache that boosts performance, offloads your database, and simplifies scalability. It’s the most widely-used Java-based cache because it’s robust, proven, full-featured, and integrates with other popular libraries and frameworks. Ehcache scales from in-process caching, all the way to mixed in-process/out-of-process deployments with terabyte-sized caches.
— Ehcache web page —
In our case, we are going to use Ehcache version 3 as this provides an implementation of a JSR-107 cache manager and Spring Boot to create a simple endpoint that is going to return the MD5 hash of a given text.
Let’s do it.
We are going to be starting a maven project and adding various dependencies:
Dependency
Version
Comment
spring-boot-starter-parent
2.3.4.RELEASE
Parent of our project
spring-boot-starter-web
—
Managed by the parent
spring-boot-starter-actuator
—
Managed by the parent
spring-boot-starter-cache
—
Managed by the parent
lombok
—
Managed by the parent
javax.cache:cache-api
1.1.1
org.ehcache:ehcache
3.8.1
Project dependencies
Now, let’s create the endpoint and the service we are going to cache. Assuming you, the reader, have some knowledge of spring, I am not going to go into details on this.
// Controller code
@RestController
@RequestMapping(value = "/api/hashes")
@AllArgsConstructor
public class HashController {
private final HashService hashService;
@GetMapping(value = "/{text}", produces = APPLICATION_JSON_VALUE)
public HttpEntity<String> generate(@PathVariable final String text) {
return ResponseEntity.ok(hashService.generateMd5(text));
}
}
// Service code
@Service
public class HashServiceImpl implements HashService {
@Override
public String generateMd5(final String text) {
try {
final MessageDigest md = MessageDigest.getInstance("MD5");
md.update(text.getBytes());
return DatatypeConverter.printHexBinary(md.digest()).toUpperCase();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Unable to get MD5 instance");
}
}
}
Simple stuff. Let’s know add the cache capabilities.
First, we will add the cache configuration to our service as a new annotation.
@Cacheable(value = "md5-cache")
This is going to define the name that it is going to be used for this cache ‘md5-cache’. As a key, the content of the method parameter will be used.
The next step is to add the configuration. To activate the cache capabilities on Spring we can use the configuration and enable configuration annotations:
@Configuration
@EnableCaching
Even with this, and using the Spring Boot auto-configuration, no caches are created by default and we need to create them. There are two ways this can be done:
Using and XML file with the configuration.
Programmatically.
If you are a follower of this blog or you have read some of the existing posts, probably, you have realised I am not a big fan of the XML configuration and I prefer to do things programmatically and, this is what we are going to do. In any case, I will try to add the XML equivalent to the configuration but, it has not been tested.
As we can see, invoking multiple times the endpoint only created the first log line and, it is just after waiting for some time (more than 10 seconds) when the cache entry gets expired and re-created.
Obviously, this is a pretty simple example but, this can help us to increase the performance of our system for more complex operations.