| 
									
										
										
										
											2023-04-19 23:26:16 +08:00
										 |  |  | [[cache-jsr-107]] | 
					
						
							|  |  |  | = JCache (JSR-107) Annotations | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Since version 4.1, Spring's caching abstraction fully supports the JCache standard | 
					
						
							|  |  |  | (JSR-107) annotations: `@CacheResult`, `@CachePut`, `@CacheRemove`, and `@CacheRemoveAll` | 
					
						
							|  |  |  | as well as the `@CacheDefaults`, `@CacheKey`, and `@CacheValue` companions. | 
					
						
							|  |  |  | You can use these annotations even without migrating your cache store to JSR-107. | 
					
						
							|  |  |  | The internal implementation uses Spring's caching abstraction and provides default | 
					
						
							|  |  |  | `CacheResolver` and `KeyGenerator` implementations that are compliant with the | 
					
						
							|  |  |  | specification. In other words, if you are already using Spring's caching abstraction, | 
					
						
							|  |  |  | you can switch to these standard annotations without changing your cache storage | 
					
						
							|  |  |  | (or configuration, for that matter). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | [[cache-jsr-107-summary]] | 
					
						
							|  |  |  | == Feature Summary | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | For those who are familiar with Spring's caching annotations, the following table | 
					
						
							|  |  |  | describes the main differences between the Spring annotations and their JSR-107 | 
					
						
							|  |  |  | counterparts: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | .Spring vs. JSR-107 caching annotations | 
					
						
							|  |  |  | [cols="1,1,3"] | 
					
						
							|  |  |  | |=== | 
					
						
							|  |  |  | | Spring | JSR-107 | Remark | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | | `@Cacheable` | 
					
						
							|  |  |  | | `@CacheResult` | 
					
						
							|  |  |  | | Fairly similar. `@CacheResult` can cache specific exceptions and force the | 
					
						
							|  |  |  |   execution of the method regardless of the content of the cache. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | | `@CachePut` | 
					
						
							|  |  |  | | `@CachePut` | 
					
						
							|  |  |  | | While Spring updates the cache with the result of the method invocation, JCache | 
					
						
							|  |  |  |   requires that it be passed it as an argument that is annotated with `@CacheValue`. | 
					
						
							|  |  |  |   Due to this difference, JCache allows updating the cache before or after the | 
					
						
							|  |  |  |   actual method invocation. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | | `@CacheEvict` | 
					
						
							|  |  |  | | `@CacheRemove` | 
					
						
							|  |  |  | | Fairly similar. `@CacheRemove` supports conditional eviction when the | 
					
						
							|  |  |  |   method invocation results in an exception. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | | `@CacheEvict(allEntries=true)` | 
					
						
							|  |  |  | | `@CacheRemoveAll` | 
					
						
							|  |  |  | | See `@CacheRemove`. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | | `@CacheConfig` | 
					
						
							|  |  |  | | `@CacheDefaults` | 
					
						
							|  |  |  | | Lets you configure the same concepts, in a similar fashion. | 
					
						
							|  |  |  | |=== | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | JCache has the notion of `javax.cache.annotation.CacheResolver`, which is identical | 
					
						
							|  |  |  | to the Spring's `CacheResolver` interface, except that JCache supports only a single | 
					
						
							|  |  |  | cache. By default, a simple implementation retrieves the cache to use based on the | 
					
						
							|  |  |  | name declared on the annotation. It should be noted that, if no cache name is | 
					
						
							|  |  |  | specified on the annotation, a default is automatically generated. See the javadoc | 
					
						
							|  |  |  | of `@CacheResult#cacheName()` for more information. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | `CacheResolver` instances are retrieved by a `CacheResolverFactory`. It is possible | 
					
						
							|  |  |  | to customize the factory for each cache operation, as the following example shows: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | [source,java,indent=0,subs="verbatim,quotes"] | 
					
						
							|  |  |  | ---- | 
					
						
							|  |  |  | 	@CacheResult(cacheNames="books", cacheResolverFactory=MyCacheResolverFactory.class) <1> | 
					
						
							|  |  |  | 	public Book findBook(ISBN isbn) | 
					
						
							|  |  |  | ---- | 
					
						
							|  |  |  | <1> Customizing the factory for this operation. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | NOTE: For all referenced classes, Spring tries to locate a bean with the given type. | 
					
						
							|  |  |  | If more than one match exists, a new instance is created and can use the regular | 
					
						
							|  |  |  | bean lifecycle callbacks, such as dependency injection. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Keys are generated by a `javax.cache.annotation.CacheKeyGenerator` that serves the | 
					
						
							|  |  |  | same purpose as Spring's `KeyGenerator`. By default, all method arguments are taken | 
					
						
							|  |  |  | into account, unless at least one parameter is annotated with `@CacheKey`. This is | 
					
						
							| 
									
										
										
										
											2023-04-19 23:26:17 +08:00
										 |  |  | similar to Spring's xref:integration/cache/annotations.adoc#cache-annotations-cacheable-key[custom key generation declaration] | 
					
						
							|  |  |  | . For instance, the following are identical operations, one using | 
					
						
							| 
									
										
										
										
											2023-04-19 23:26:16 +08:00
										 |  |  | Spring's abstraction and the other using JCache: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | [source,java,indent=0,subs="verbatim,quotes"] | 
					
						
							|  |  |  | ---- | 
					
						
							|  |  |  | 	@Cacheable(cacheNames="books", key="#isbn") | 
					
						
							|  |  |  | 	public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	@CacheResult(cacheName="books") | 
					
						
							|  |  |  | 	public Book findBook(@CacheKey ISBN isbn, boolean checkWarehouse, boolean includeUsed) | 
					
						
							|  |  |  | ---- | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | You can also specify the `CacheKeyResolver` on the operation, similar to how you can | 
					
						
							|  |  |  | specify the `CacheResolverFactory`. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | JCache can manage exceptions thrown by annotated methods. This can prevent an update of | 
					
						
							|  |  |  | the cache, but it can also cache the exception as an indicator of the failure instead of | 
					
						
							|  |  |  | calling the method again. Assume that `InvalidIsbnNotFoundException` is thrown if the | 
					
						
							|  |  |  | structure of the ISBN is invalid. This is a permanent failure (no book could ever be | 
					
						
							|  |  |  | retrieved with such a parameter). The following caches the exception so that further | 
					
						
							|  |  |  | calls with the same, invalid, ISBN throw the cached exception directly instead of | 
					
						
							|  |  |  | invoking the method again: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | [source,java,indent=0,subs="verbatim,quotes"] | 
					
						
							|  |  |  | ---- | 
					
						
							|  |  |  | 	@CacheResult(cacheName="books", exceptionCacheName="failures" | 
					
						
							|  |  |  | 				cachedExceptions = InvalidIsbnNotFoundException.class) | 
					
						
							|  |  |  | 	public Book findBook(ISBN isbn) | 
					
						
							|  |  |  | ---- | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | [[enabling-jsr-107-support]] | 
					
						
							|  |  |  | == Enabling JSR-107 Support | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | You do not need to do anything specific to enable the JSR-107 support alongside Spring's | 
					
						
							|  |  |  | declarative annotation support. Both `@EnableCaching` and the `cache:annotation-driven` | 
					
						
							|  |  |  | XML element automatically enable the JCache support if both the JSR-107 API and the | 
					
						
							|  |  |  | `spring-context-support` module are present in the classpath. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | NOTE: Depending on your use case, the choice is basically yours. You can even mix and | 
					
						
							|  |  |  | match services by using the JSR-107 API on some and using Spring's own annotations on | 
					
						
							|  |  |  | others. However, if these services impact the same caches, you should use a consistent | 
					
						
							|  |  |  | and identical key generation implementation. |