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.
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:
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.
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(); > >
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.
Method | Description |
---|---|
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. |
These methods provide basic functionality for interacting with cached data, allowing developers to manage cached entries efficiently within their applications.
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.
Method | Description |
---|---|
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 . |
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.
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.
Method | Description |
---|---|
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 . |
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.
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.
Method | Description |
---|---|
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. |
These methods provide convenient ways to obtain caching providers and cache managers, enabling developers to effectively manage caching functionality within their applications.
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.
Method | Description |
---|---|
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. |
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 Implementation | Description |
---|---|
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. |
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.
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 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 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 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 (CacheEntryEvent extends String, ? extends Integer>event : 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(CacheEntryEvent extends String, ? extends Integer>event) < // 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 Interface | Override Method | Event 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 |
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 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 Implementation | Override Method | Description |
---|---|---|
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. |
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 Implementation | Override Method | Description |
---|---|---|
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. |
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.
Method | Description |
---|---|
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. |
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.
Annotation | Description |
---|---|
@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. |
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"); >>
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.
Here’s a table listing some of the most commonly used JMX APIs for managing and monitoring JCache implementations:
JMX API | Description |
---|---|
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.
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/Interface | Description |
---|---|
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 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.
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:
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:
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.
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.