Caching in Spring with Caffeine

Having a caching mechanism in the application (as per business requirements, of course) is an indicator of good architectural design. Don’t you agree?

To make things a bit more interesting, we’re gonna talk specifically about Caching in Spring applications.

Primarily, there are two types of cache providers supported by Spring: Application-side in-memory storage (useful for monolithic applications) and Distributed caching (convenient for micro-services architecture).

While there are multiple cache providers such as Ehcache 2.x, Hazelcast, Infinispan, Couchbase, Redis, Caffeine, and more, it’s fair to throw some light on Caffeine since almost all (if not all) developers have a soft spot for the beverage itself.

So, what is Caffeine all about?

Caffeine is a high-performance Java 8-based caching library that provides an in-memory cache using an API inspired by Google Guava. In simpler terms, it is a rewrite version of Google’s Guava cache, with improvements. Also, using in-memory cache libraries like Caffeine significantly reduces latency.

Key features that Caffeine offers:

  • Automatic loading of entries into the cache, optionally asynchronously.
  • Size-based eviction when a maximum is exceeded, based on frequency and recency.
  • Time-based expiration of entries, measured since last access or last write.
  • Asynchronous refreshing when the first stale request for an entry occurs.
  • Keys automatically wrap in weak references.
  • Values automatically wrap in weak or soft references.
  • Notification of evicted (or otherwise removed) entries.
  • Writes propagated to an external resource.
  • Accumulation of cache access statistics.

Believe it or not, it is quite easy to integrate Caffeine caching in both new and existing Spring applications!

Here’s how:

Dependency

To get started, we need to add spring-boot-starter-cache and Caffeine dependencies in POM/Gradle.

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
  </dependency>
  <dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
  </dependency>
</dependencies>

Configuration

@Configuration
@EnableCaching 
class CaffineConfiguration {

       /* Need to define caffeine bean first with caching behavior, expiration, cache limit, etc. */
	@Bean
	public Caffeine caffeineConfig() {
		return Caffeine.newBuilder().expireAfterWrite(60, TimeUnit.MINUTES);
	}

	/* We need to create one more bean using the Spring CacheManager interface, Caffeine provides its implementation of this interface. */
	@Bean
	public CacheManager cacheManager(Caffeine caffeine) {
		CaffeineCacheManager caffeineCacheManager = new CaffeineCacheManager();
		caffeineCacheManager.setCaffeine(caffeine);
		return caffeineCacheManager;
	}
}

Application Example

public interface NotesService {
	
	/**
	 * Get user notes.
	 * 
	 * @param username
	 * @return
	 */
	 @Cacheable(value = "username", key = "#username")
	 public Map<Integer, String> getNotes(final String username);
	
	/**
	 * Add user notes.
	 * 
	 * @param username
	 * @param plainNotes
	 * @return
	 */
	 @CacheEvict(value = "username",  key = "#username")
	 public Map<Integer, String> addNotes(final String username, Set<String> plainNotes);

}

Conclusion

Caffeine caching is feasible if you are developing monolithic and micro-services applications where you don’t need distributed caching. Utilizing just application memory for caching is not a good practice, especially when resources are limited. In the case of micro-services, a distributed caching mechanism is the best option in order to use Caffeine and Redis altogether.

This article is contributed by Anway Bhutkar, a Software Developer at Coditas.

How useful was this post?

Click on a star to rate it!

Average rating 5 / 5. 3

Your votes help us create better posts!

As you found this post useful...

Follow us on social media!

Leave a Reply