This guide walks you through the process of enabling caching on a Spring managed bean.
You will build an application that enables caching on a simple book repository.
First, you need to create a simple model for your book. The following listing (from src/main/java/com/example/caching/Book.java
) shows how to do so
public class Book {
private String isbn;
private String title;
}
You also need a repository for that model.
public interface BookRespository {
Book getByIsbn(String isbn);
}
implement, or use spring data
@Component
public class SimpleBookRepository implements BookRepository {
//...
}
You also need a CommandLineRunner
that injects the BookRepository
and calls it several times
@Component
class AppRunner implements CommandLineRunner {
private static final Logger looger = LoggerFactory.getLogger(AppRunner.class);
private final BookRepository bookRepository;
public AppRunner(BookRepository bookRepository) {
this.bookRespository = bookRepository;
}
public void run(String... args) {
logger.info("Fetching books...");
logger.info(bookRepository.getByIsbn("isbn-1234"));
logger.info(bookRepository.getByIsbn("isbn-4567"));
logger.info(bookRepository.getByIsbn("isbn-1234"));
}
}
Now you can enable caching on your SimpleBookRepository
so that the books are cached within the books
cache. The following listing (from src/main/java/com/example/caching/SimpleBookRepository.java
) shows the repository definition:
@Component
class SimpleBookRepository implements BookRepository {
@Override
@Cacheable("books")
public Book getByIsbn(String isbn) {
simulateSlowService();
return new Book(isbn, "Some book");
}
//。。。
}
You now need to enable the processing of the caching annotations,
@SpringBootApplication
@EnableCaching
public class CachingApplication {...}
The @EnableCaching
annotation triggers a post-processor that inspects every Spring bean for the presence of caching annotations on public methods. If such an annotation is found, a proxy is automatically created to intercept the method call and handle the caching behavior accordingly.
The post-processor handles the @Cacheable
, @CachePut
and @CacheEvict
annotations. You can refer to the Javadoc and the reference guide for more detail.
Spring Boot automatically configures a suitable CacheManager
to serve as a provider for the relevant cache. See the Spring Boot documentation for more detail.
Our sample does not use a specific caching library, so our cache store is the simple fallback that uses ConcurrentHashMap
. The caching abstraction supports a wide range of cache libraries and is fully compliant with JSR-107 (JCache).
[^compliant ]: 顺从的 符合的
https://docs.spring.io/spring-framework/docs/current/reference/html/integration.html#cache
Since version 3.1, the Spring Framework provides support for transparently adding caching to an existing Spring application. Similar to the transaction support, the caching abstraction allows consistent use of various caching solutions with minimal impact on the code.
In Spring Framework 4.1, the cache abstraction was significantly extended with support for JSR-107 annotations and more customization options.
At its core, the cache abstraction applies caching to Java methods, thus reducing the number of executions based on the information available in the cache. That is, each time a targeted method is invoked, the abstraction applies a caching behavior that checks whether the method has been already invoked for the given arguments. If it has been invoked, the cached result is returned without having to invoke the actual method. If the method has not been invoked, then it is invoked, and the result is cached and returned to the user so that, the next time the method is invoked, the cached result is returned. This way, expensive methods (whether CPU- or IO-bound) can be invoked only once for a given set of parameters and the result reused without having to actually invoke the method again. The caching logic is applied transparently without any interference to the invoker.
The caching abstraction provides other cache-related operations, such as the ability to update the content of the cache or to remove one or all entries. These are useful if the cache deals with data that can change during the course of the application.
As with other services in the Spring Framework, the caching service is an abstraction (not a cache implementation) and requires the use of actual storage to store the cache data — that is, the abstraction frees you from having to write the caching logic but does not provide the actual data store. This abstraction is materialized by the org.springframework.cache.Cache
and org.springframework.cache.CacheManager
interfaces.
Spring provides a few implementations of that abstraction: JDK java.util.concurrent.ConcurrentMap
based caches, Ehcache 2.x, Gemfire cache, Caffeine, and JSR-107 compliant caches (such as Ehcache 3.x). See Plugging-in Different Back-end Caches for more information on plugging in other cache stores and providers.
The caching abstraction has no special handling for multi-threaded and multi-process environments, as such features are handled by the cache implementation.
If you have a multi-process environment (that is, an application deployed on several nodes), you need to configure your cache provider accordingly. Depending on your use cases, a copy of the same data on several nodes can be enough. However, if you change the data during the course of the application, you may need to enable other propagation mechanisms.
Caching a particular item is a direct equivalent of the typical get-if-not-found-then-proceed-and-put-eventually code blocks found with programmatic cache interaction. No locks are applied, and several threads may try to load the same item concurrently. The same applies to eviction. If several threads are trying to update or evict data concurrently, you may use stale data. Certain cache providers offer advanced features in that area. See the documentation of your cache provider for more details.
To use the cache abstraction, you need to take care of two aspects:
For caching declaration, Spring’s caching abstraction provides a set of Java annotations:
@Cacheable
: Triggers cache population.@CacheEvict
: Triggers cache eviction.@CachePut
: Updates the cache without interfering with the method execution.@Caching
: Regroups multiple cache operations to be applied on a method.@CacheConfig
: Shares some common cache-related settings at class-level.Since caches are essentially key-value stores, each invocation of a cached method needs to be translated into a suitable key for cache access.
The caching abstraction uses a simple CacheResolver
that retrieves the caches defined at the operation level by using the configured CacheManager
.
To provide a different default cache resolver, you need to implement the org.springframework.cache.interceptor.CacheResolver
interface.
The default cache resolution fits well for applications that work with a single CacheManger
and have no complex cache resolution requirements.
For applications that work with several cache managers, you can set the cacheManger
to use for each operation, as the following example shows:
@Cacheable(cacheNames="books", cacheManager="anotherCacheManager") public Book findBook(ISBN isbn) {...}
The cache abstraction provides several storage integration options. To use them, you need to declare an appropriate CacheManager
(an entity that controls and manages Cache
instances and that can be used to retrieve these for storage).
ConcurrentMap
-based CacheThe JDK-based Cache
implementation resides under org.springframework.cache.concurrent
package.