Java Caching Cheatsheet

Java caching plays a crucial role in optimizing application performance, especially in scenarios where data retrieval or computation is resource-intensive. It is essential to carefully design and configure caching strategies based on the specific requirements of the application to strike a balance between improved performance and efficient resource utilization.

The JCache API (JSR 107)

JSR-107, commonly known as JCache, is a Java specification for a caching API and standardizing caching annotations. The goal of JCache is to provide a common way for Java applications to interact with caching systems, fostering portability across different caching providers like the ones mentioned above.

Key features of JCache include:

  1. API Standardization: JCache defines a set of standard interfaces and classes for interacting with caches. It is not tied to a specific caching implementation. Instead, it allows for the integration of multiple caching providers that adhere to the specification.
  2. Annotations: JCache introduces annotations such as @CacheResult , @CachePut , and @CacheRemove that can be used to control caching behavior in a declarative manner. These annotations are similar to those found in frameworks like Spring.
  3. Basic Caching Operations: JCache supports fundamental caching operations, including putting, getting, and removing entries from the cache. It provides a standardized way to perform these operations regardless of the underlying caching provider.
  4. Configuration: The specification defines a standardized way to configure caching settings, such as cache size, eviction policies, and expiration times. This makes it easier to manage and tune caching behavior across different implementations.
  5. Integration with Java EE and SE: JCache is designed to seamlessly integrate with both Java EE (Enterprise Edition) and Java SE (Standard Edition) environments. This ensures that applications can use the same caching API across different Java platforms.

Using a standard API, like JCache, enables developers to switch between different caching implementations and choose the caching solution that best fits their needs with minimal to no code changes in their application. Furthermore it boosts productivity because it ensures that the learning curve is smaller since it is restricted to the knowledge of JCache as a standard and not the specifics of each vendor implementation.

JCache “Hello World”

To create a simple “Hello World” example using JCache, you’ll first need to include the JCache API and a specific caching provider in your project. Let’s use Ehcache as the caching provider in this example. If you’re using Maven, include the following dependencies in your pom.xml

   javax.cache cache-api 1.1.1   org.ehcache ehcache 3.9.6   

The following example sets up a cache, puts a “Hello, World!” greeting into the cache, retrieves it, and prints it to the console.

import javax.cache.Cache; import javax.cache.CacheManager; import javax.cache.Caching; import javax.cache.configuration.MutableConfiguration; public class HelloWorldJCache < public static void main(String[] args) < // Create a CacheManager CacheManager cacheManager = Caching.getCachingProvider().getCacheManager(); // Define cache configuration MutableConfigurationcacheConfig = new MutableConfiguration<>(); cacheConfig.setStoreByValue(false); // Store values by reference cacheConfig.setTypes(String.class, String.class); cacheConfig.setExpiryPolicyFactory(CreatedExpiryPolicy.factoryOf(Duration.ONE_MINUTE)); cacheConfig.setManagementEnabled(true); cacheConfig.setStatisticsEnabled(true); // Create a cache Cache cache = cacheManager.createCache("helloCache", cacheConfig); // Put data into the cache cache.put("greeting", "Hello, World!"); // Get data from the cache String greeting = cache.get("greeting"); System.out.println(greeting); // Close the CacheManager when done cacheManager.close(); > >

Cache

The javax.cache.Cache interface is a fundamental part of the Java Caching API. It represents a cache, which is a temporary storage area for key-value pairs where data can be stored and retrieved quickly. It provides methods for storing, retrieving, and manipulating cached data. It abstracts the underlying caching implementation, allowing developers to interact with different caching providers through a common interface.

Following is a table listing some of the most commonly used methods of the javax.cache.Cache interface.

MethodDescription
V get(K key) Retrieves the value associated with the specified key from the cache, or null if the key is not found.
void put(K key, V value) Associates the specified value with the specified key in the cache.
boolean containsKey(K key) Checks if the cache contains an entry for the specified key.
V getAndPut(K key, V value) Retrieves the current value associated with the specified key, then updates the value with the new one.
void remove(K key) Removes the entry for the specified key from the cache, if present.
boolean remove(K key, V oldValue) Removes the entry for the specified key from the cache only if it is currently mapped to the specified value.
void removeAll(Set keys) Removes entries for multiple keys from the cache.
void removeAll() Removes all entries from the cache.
void putAll(Map entries) Associates multiple key-value pairs with the cache.
boolean putIfAbsent(K key, V value) Associates the specified value with the specified key in the cache if the specified key is not already associated with a value.
void clear() Clears the cache, removing all entries.
void close() Closes the cache, releasing any resources associated with it.
Cache class basic functionality

These methods provide basic functionality for interacting with cached data, allowing developers to manage cached entries efficiently within their applications.

CacheManager

The javax.cache.CacheManager interface is responsible for managing caches and their configurations within a caching environment. It provides methods to create, retrieve, and manage caches, as well as access caching-specific features such as configuration and statistics.

Following is a table listing some of the most commonly used methods of the javax.cache.CacheManager interface.

MethodDescription
CachingProvider getCachingProvider() Retrieves the CachingProvider associated with this CacheManager .
void close() Closes this CacheManager and releases any resources associated with it.
void destroyCache(String cacheName) Destroys the cache specified by its name.
boolean isClosed() Checks if this CacheManager is closed.
boolean isSupported(OptionalFeature feature) Checks if the specified optional feature is supported by this CacheManager .
void enableManagement(String cacheName, boolean enabled) Enables or disables management (statistics and monitoring) for the cache specified by its name.
void enableStatistics(String cacheName, boolean enabled) Enables or disables statistics collection for the cache specified by its name.
void createCache(String cacheName, Configuration configuration) Creates a cache with the specified name and configuration.
void destroyCache(String cacheName) Destroys the cache specified by its name.
C getCache(String cacheName, Class keyType, Class valueType) Retrieves a cache by its name, specifying key and value types.
Iterable getCacheNames() Retrieves an iterable collection of cache names managed by this CacheManager .
ClassLoader getClassLoader() Retrieves the ClassLoader used by caches managed by this CacheManager .
Properties getProperties() Retrieves the properties used to initialize this CacheManager .
CacheManager class basic funationality

These methods provide essential functionality for managing caches and their configurations within a caching environment. Developers can use these methods to create, access, and configure caches as needed for their applications.

CachingProvider

The javax.cache.spi.CachingProvider interface serves as a factory for creating and accessing caching-related entities such as CacheManager instances. It acts as an abstraction layer between the application and the underlying caching implementation.

Below is a table listing some of the most commonly used methods of the javax.cache.spi.CachingProvider interface.

MethodDescription
CacheManager getCacheManager() Retrieves the default CacheManager associated with this CachingProvider .
boolean isSupported(OptionalFeature feature) Checks if the specified optional feature is supported by this CachingProvider .
void close() Closes this CachingProvider and releases any resources associated with it.
String getDefaultURI() Retrieves the default URI of this CachingProvider .
Properties getDefaultProperties() Retrieves the default properties used to configure this CachingProvider .
CachingProvider class basic functionality

These methods enable developers to manage caching providers, create cache managers, and configure caching behavior within their applications. They provide flexibility in handling caching-related tasks while abstracting the underlying caching implementation details.

Caching

javax.cache.Caching is a utility class that serves as the entry point for accessing caching functionality within Java applications. It provides static methods to create and access caching providers and cache managers. It abstracts the process of obtaining caching-related entities, making it easier for developers to integrate caching functionality into their applications.

Here’s a table listing some of the most commonly used methods of the javax.cache.Caching class along with their descriptions.

MethodDescription
CachingProvider getCachingProvider() Retrieves the default caching provider.
CacheManager getCacheManager() Retrieves the default cache manager.
void closeCacheManager(CacheManager cacheManager) Closes the specified cache manager.
Iterable getCachingProviders() Retrieves an iterable collection of all registered caching providers.
Iterable getCacheManagers(CachingProvider cachingProvider) Retrieves an iterable collection of cache managers associated with the specified caching provider.
Caching class basic functionality

These methods provide convenient ways to obtain caching providers and cache managers, enabling developers to effectively manage caching functionality within their applications.

Configuration

The javax.cache.configuration package contains classes and interfaces for configuring caches and cache managers in Java applications. These classes and interfaces allow developers to define and customize various aspects of caching behavior, such as event listeners, cache loaders/writers, and statistics collection. In general, it provides a flexible way to define caching behavior and settings according to specific application requirements.

javax.cache.configuration.MutableConfiguration provides a mutable implementation of the Configuration interface. It allows developers to modify caching settings such as expiry policies, size limits, listeners, and statistics collection during runtime.

Below’s a table listing some of the most commonly used methods of the javax.cache.configuration.MutableConfiguration class along with their descriptions.

MethodDescription
MutableConfiguration(Class keyType, Class valueType) Constructs a new MutableConfiguration with the specified key and value types.
setStoreByValue(boolean isStoreByValue) Sets whether the cache should store values by value or by reference.
setTypes(Class keyType, Class valueType) Sets the key and value types for the cache.
setExpiryPolicyFactory(Factory factory) Sets the factory for creating expiry policies for cache entries.
setStatisticsEnabled(boolean isStatisticsEnabled) Sets whether statistics collection is enabled for the cache.
setManagementEnabled(boolean isManagementEnabled) Sets whether management (monitoring and configuration) is enabled for the cache.
addCacheEntryListenerConfiguration(CacheEntryListenerConfiguration listenerConfiguration) Adds a cache entry listener configuration to the cache.
removeCacheEntryListenerConfiguration(CacheEntryListenerConfiguration listenerConfiguration) Removes a cache entry listener configuration from the cache.
setCacheWriterConfiguration(CacheWriterConfiguration cacheWriterConfiguration) Sets the cache writer configuration for the cache.
setCacheLoaderConfiguration(CacheLoaderConfiguration cacheLoaderConfiguration) Sets the cache loader configuration for the cache.
setReadThrough(boolean isReadThrough) Sets whether the cache should read through to a cache loader if a value is not found in the cache.
setWriteThrough(boolean isWriteThrough) Sets whether the cache should write through to a cache writer.
MutableConfiguration class basic functionality

Expiry Policy

The javax.cache.expiry.ExpiryPolicy is an interface that defines the expiration policy for cached entries. It specifies how long an entry should remain valid in the cache before it is considered expired and potentially evicted from the cache. ExpiryPolicy allows developers to specify different expiration policies based on various criteria such as creation time, last access time, or a combination of both. By default, the entries in a javax.cache.Cache do not expire.

Below is a table listing some of the most common implementations of the javax.cache.expiry.ExpiryPolicy interface along with their descriptions.

ExpiryPolicy ImplementationDescription
AccessedExpiryPolicy Expires entries based on the time of last access. When an entry is accessed, its expiration time is extended by a fixed duration from the last access.
CreatedExpiryPolicy Expires entries based on their creation time. Entries expire after a fixed duration from their creation time.
EternalExpiryPolicy Indicates that cached entries never expire. Entries remain in the cache indefinitely until explicitly removed.
ModifiedExpiryPolicy Expires entries based on their modification time. When an entry is updated, its expiration time is extended by a fixed duration from the last update.
ExpiryPolicy implementations

These ExpiryPolicy implementations offer different strategies for determining the expiration time of cached entries based on factors such as access time, creation time, modification time, or an eternal policy where entries never expire. Developers can choose the appropriate implementation based on their caching requirements and desired eviction behavior.

In addition to the aforementioned policies, JCache allows you to implement custom eviction policies by implementing the javax.cache.expiry.ExpiryPolicy interface.

Listeners and Listener Filters

In the Java Caching API, listeners are mechanisms used to monitor and react to events related to cache operations. These events include entry creation, update, removal, and eviction. JCache provides two main types of listeners: Cache Entry Listeners and Cache Manager Listeners. Additionally, JCache supports listener filters, allowing developers to specify conditions under which listeners should be triggered.

Cache Entry Listeners

Cache Entry Listeners are invoked when specific events occur on cache entries, such as when an entry is created, updated, removed, or evicted. These listeners can perform actions such as logging, triggering notifications, or updating external systems based on the cache events.

Cache Manager Listeners

Cache Manager Listeners monitor events related to cache managers, such as when a cache manager is created or closed. They provide hooks for performing initialization or cleanup tasks when cache managers are instantiated or destroyed.

Listener Filters

Listener Filters allow developers to specify conditions under which listeners should be triggered. For example, a filter can be used to only invoke a listener when certain criteria are met, such as when a cache entry’s value meets a specific condition or when an entry is updated with a particular key.

import javax.cache.event.*; // Define a cache entry listener public class MyCacheEntryListener implements CacheEntryCreatedListener < @Override public void onCreated(Iterable> events) < for (CacheEntryEventevent : events) < System.out.println("Cache entry created: Key=" + event.getKey() + ", Value=" + event.getValue()); >> > // Define a cache listener filter public class MyCacheListenerFilter implements CacheEntryEventFilter  < @Override public boolean evaluate(CacheEntryEventevent) < // Only trigger listener for entries with values greater than 10 return event.getValue() >10; > > public class CacheListenerExample < public static void main(String[] args) < // Create a cache Cachecache = . ; // Get a cache instance // Register the cache entry listener MyCacheEntryListener listener = new MyCacheEntryListener(); cache.registerCacheEntryListener(new MutableConfiguration<>().addCacheEntryListenerConfiguration( new CacheEntryListenerConfiguration<>(MyCacheEntryListener.class, new MyCacheListenerFilter(), true, true))); // Perform cache operations cache.put("key1", 15); cache.put("key2", 5); > >

In this example:

Below is a table listing some of the most popular listener interface classes in the Java Caching API, along with their override method and the event type they handle.

Listener InterfaceOverride MethodEvent Type
CacheEntryCreatedListener onCreated Cache entry creation
CacheEntryUpdatedListener onUpdated Cache entry update
CacheEntryRemovedListener onRemoved Cache entry removal
CacheEntryExpiredListener onExpired Cache entry expiration
CacheEntryEvictedListener onEvicted Cache entry eviction
CacheEntryReadListener onRead Cache entry read (access)
CacheEntryWriteListener onWrite Cache entry write (put or replace)
CacheEntryListener onCreated , onUpdated , onRemoved , onExpired , onEvicted Multiple event types
CacheManagerListener onManagerCreated , onManagerDestroyed Cache manager creation/destruction
Listener implementation classes

Loaders and Writers

Cache loaders and cache writers are components responsible for interacting with external data sources when cache misses or updates occur. They allow developers to integrate caching with underlying data storage systems, enabling seamless synchronization between cached data and persistent data sources.

Cache Loaders

Cache loaders are responsible for loading data from external sources into the cache when requested data is not found in the cache (cache misses). They provide a mechanism for populating the cache with data from persistent storage, such as databases or remote services, to ensure that requested data is available in the cache for subsequent accesses. Cache readers are typically used in conjunction with read-through caching strategies, where cache is treated as the main data store and missing cache entries are immediately fetched from the integrated backend store.

Cache Loader ImplementationOverride MethodDescription
javax.cache.CacheLoader load Loads data for the specified key into the cache from an external source.
loadAll Loads multiple key-value pairs into the cache from an external source.
CacheLoader implementation semantics

Cache Writers

Cache writers, on the other hand, are responsible for propagating changes made to cached data back to external data sources. They update persistent storage with changes made to cached data, ensuring consistency between the cache and the underlying data source. Cache writers are typically used in conjunction with write-through caching strategies, where data modifications are immediately reflected in both the cache and the external data store.

Cache Writer ImplementationOverride MethodDescription
javax.cache.CacheWriter write Writes data for the specified key-value pair from the cache to an external data source.
delete Deletes data for the specified key from the external data source.
writeAll Writes multiple key-value pairs from the cache to an external data source.
deleteAll Deletes multiple keys and their associated values from the external data source.
CacheWriter implementation semantics

Cache Entry Processors

A cache entry processor, represented by the javax.cache.EntryProcessor interface, is a mechanism for performing atomic operations on cache entries. It allows developers to execute custom logic directly within the cache node (JVM), providing a way to manipulate cache entries without the need for external data sources, serialization/de-serialization of data between clients and the cache node, or complex synchronization mechanisms.

Cache entry processors are typically used in scenarios where multiple cache operations need to be performed atomically, ensuring consistency and avoiding race conditions. They are also particularly useful when the cache is distributed (which is quite often the case) over multiple nodes. They offer a way to encapsulate and execute custom logic on cache entries within a single atomic operation, improving performance and reducing the risk of data inconsistency.

javax.cache.EntryProcessor basic methods are presented below.

MethodDescription
process Executes custom logic on a cache entry atomically, ensuring that the cache entry is locked during processing.
processAll Executes custom logic on multiple cache entries atomically, ensuring that each cache entry is locked during processing.
EntryProcessor basic functionality

Annotations

The javax.cache.annotation package offers annotations that developers can use to mark methods for caching purposes. These annotations provide a convenient way to control caching behavior, such as specifying cache names, cache entry keys, and caching strategies, without requiring explicit caching logic within the method implementation.

Below is a table with the most commonly used JCache annotations and their descriptions.

AnnotationDescription
@CacheResult Marks a method whose return value should be cached. Specifies the cache name and key, and optionally, cache resolver. Applicable also on a class for effect on all methods of that class.
@CachePut Marks a method whose return value should be cached or updated in the cache. Specifies the cache name and key. Applicable also on a class for effect on all methods of that class.
@CacheKey Explicitly specify a method parameter as the cache key.
@CacheValue Explicitly specify a method parameter as the cache value when using @CachePut
@CacheRemove Marks a method that removes an entry from the cache. Specifies the cache name and key. Applicable also on a class for effect on all methods of that class.
@CacheRemoveAll Marks a method that removes all entries from the cache. Specifies the cache name. Applicable also on a class for effect on all methods of that class.
@CacheDefaults Specifies default caching settings for methods within a class. Can define default cache name, key generator, etc. Applicable on classes only.
JCache annotations

And here is an example use case of the aforementioned annotations.

import javax.cache.annotation.*; public class ExampleService < // Define a cache named "exampleCache" @CacheResult(cacheName = "exampleCache") public String getCachedData(@CacheKey String key) < // This method will be cached with the key provided // Simulate data retrieval from an external source return "Data for key: " + key; >// Update cache or add new entry @CachePut(cacheName = "exampleCache") public void updateCache(@CacheKey String key, @CacheValue String data) < // This method will update the cache with new data // No return value is cached // Simulate data update or addition System.out.println("Updating cache for key: " + key + ", with data: " + data); >// Remove specific entry from cache @CacheRemove(cacheName = "exampleCache") public void removeFromCache(@CacheKey String key) < // This method will remove the specified key from the cache System.out.println("Removing entry from cache for key: " + key); >// Remove all entries from cache @CacheRemoveAll(cacheName = "exampleCache") public void removeAllFromCache() < // This method will remove all entries from the cache System.out.println("Removing all entries from cache"); >>

Monitoring And Management

Java Caching API provides management and monitoring options during runtime to facilitate the observation and control of caching behavior. These options enable developers to monitor cache usage, performance metrics, and configuration details, as well as to manage cache lifecycle and operations dynamically.

Management and Monitoring Options

  1. JMX (Java Management Extensions): JCache supports integration with JMX, allowing caching implementations to expose cache management and monitoring functionalities as managed beans. Through JMX, developers can access cache-related attributes and operations programmatically or through management tools such as JConsole or VisualVM.
  2. Metrics and Statistics: JCache implementations often provide built-in support for collecting and exposing cache usage metrics and statistics. These metrics may include hit/miss ratios, cache size, eviction counts, and latency measurements, providing insights into cache performance and effectiveness.

JMX API for JCache Management

Here’s a table listing some of the most commonly used JMX APIs for managing and monitoring JCache implementations:

JMX APIDescription
javax.cache.management.CacheMXBean Exposes cache management and monitoring functionalities such as cache statistics, configuration details, and operations through JMX.
javax.cache.management.CacheStatisticsMXBean Provides access to cache statistics such as hit/miss counts, eviction counts, and cache size through JMX.
javax.cache.management.CacheManagerMXBean Represents a cache manager’s management interface, exposing methods for cache creation, destruction, and management via JMX.
javax.cache.management.CacheManagerStatisticsMXBean Offers cache manager statistics such as the number of caches created, destroyed, and remaining through JMX.
javax.cache.management.CacheMXBean.getCacheMXBeans() Retrieves a collection of cache MXBeans associated with the cache manager.

These JMX APIs provide standardized interfaces for accessing cache and cache manager management and monitoring functionalities. By integrating with JMX, JCache implementations offer a consistent and interoperable approach to managing and monitoring caching operations during runtime.

Vendor Specific Features

The unwrap method allows developers to obtain the underlying implementation-specific object associated with a specific JCache class or interface. This method is useful when developers need to access implementation-specific features or functionalities that are not provided by the standard JCache API.

Classes and Interfaces Supporting unwrap :

Class/InterfaceDescription
javax.cache.Cache Represents a cache in the JCache API. Allows for storing, retrieving, and managing cached key-value pairs.
javax.cache.CacheManager Represents a cache manager in the JCache API. Manages the lifecycle of caches and provides cache creation.
javax.cache.Cache.Entry Represents an entry in a cache. Provides access to the key, value, and metadata associated with the entry.
unwrap support
// Unwrap Cache to Hazelcast ICache ICache hazelcastCache = cache.unwrap(ICache.class); // Unwrap CacheManager to Hazelcast ICacheManager ICacheManager hazelcastCacheManager = cacheManager.unwrap(ICacheManager.class); // Unwrap Cache.Entry to Hazelcast ICache.Entry com.hazelcast.cache.Cache.Entry hazelcastCacheEntry = cacheEntry.unwrap(com.hazelcast.cache.Cache.Entry.class);

Keep in mind that using this feature is not recommended if true caching provider portability is what you need, since your application would be coupled to vendor-specific APIs.

Cache Topologies And Modes

In the context of caching systems, a cache topology refers to the arrangement or structure of caches within a distributed caching environment. Different cache topologies offer various trade-offs in terms of performance, scalability, and consistency. Here are some common cache topologies:

  1. Single-node Cache: In this topology, there is only one cache node, typically running on a single server or instance. It’s the simplest form of caching and provides basic caching capabilities. However, it lacks scalability and fault tolerance.
  2. Multiple Independent Caches: Multiple cache nodes operate independently, with each node managing its own cache. This topology allows for better scalability compared to a single-node cache as multiple cache instances can handle requests concurrently. However, it lacks data consistency across caches.
  3. Replicated Cache: In a replicated cache topology, the entire dataset is replicated across all cache nodes. This ensures that each cache node holds a complete copy of the data. It offers high availability and fault tolerance since any node failure can be mitigated by other replicas. However, it may lead to increased network traffic and memory consumption due to data duplication.
  4. Partitioned Cache: In a partitioned cache topology, the dataset is partitioned across multiple cache nodes based on a consistent hashing algorithm. Each node is responsible for storing and managing a subset of the data. This allows for horizontal scalability as the dataset can grow beyond the capacity of a single node. However, managing data consistency and cache coherence across partitions can be challenging.

Similar to cache topologies, a cache mode refers to wether the cache is part of the client application or running as a separate service. The following modes are common across caches in general:

  1. Client-side Cache (Embedded mode): In this mode, caching is performed at the client-side, typically within the application’s JVM. Cached data is stored locally, reducing the need to fetch data from remote caches or servers. It can improve application performance by reducing network latency and server load. However, it may lead to data inconsistency if the cache is not synchronized with the server-side data.
  2. Server-side Cache (Client-Server mode): In contrast to client-side caching, server-side caching stores cached data within cache servers or nodes. It offloads caching responsibilities from the client application and centralizes cache management and coordination. Server-side caching can provide better control over caching policies and data consistency but may introduce additional network latency for cache retrieval operations.

Each cache topology and mode has its advantages and disadvantages, and the choice of topology/mode combination depends on factors such as application requirements, scalability needs, fault tolerance, and data consistency considerations. Organizations often employ a combination of cache topologies and modes to meet their specific caching requirements effectively.

Resources

The following resources offer a wealth of information on caching systems, from basic concepts to advanced topics, making them valuable references for developers, architects, and system administrators working with caching technologies.

  1. Oracle Java Caching API Documentation:
  2. Ehcache Documentation:
  3. Apache Ignite Documentation:
  4. Spring Caching Documentation: