2 Usage - Reference Documentation
Authors:
Version: 3.0.3
2 Usage
The cache plugin adds Spring bean method call, controller action, and GSP page fragment and template caching to Grails applications. You configure one or more caches inConfig.groovy
and/or one or more Groovy artifact files with names ending in CacheConfig.groovy (for example FooCacheConfig.groovy, BarCacheConfig.groovy, and these can also be in packages) in grails-app/conf
(or a subdirectory if in a package) using an implementation-specific DSL, and annotate methods (either in Spring beans (typically Grails services) or controllers) to be cached. You can also wrap GSP sections in cache tags and render cached templates.There are three annotations; Cacheable, CachePut, and CacheEvict. You use @Cacheable to mark a method as one that should check the cache for a pre-existing result, or generate a new result and cache it. Use @CachePut to mark a method as one that should always be evaluated and store its result in the cache regardless of existing cache values. And use @CacheEvict to flush a cache (either fully or partially) to force the re-evaluation of previously cached results. The annotations are based on the annotations with the same name from Spring (Cacheable, CachePut, and CacheEvict) and support the same syntax but may support extended functionality in the future.When using distributed caching (such as ehcache with distributed cache enabled, or redis with multiple instances of the application running against one redis instance), all classes that use annotation caching or XML caching should override the hashCode
method. The hash code of the object with the method marked as being cacheable is included in the cache key, and the default hashCode
implementation will vary each time the application is run. Overriding hashCode
ensures that each instance of the applications will appropriately share cache keys.This 'core' cache plugin uses an in-memory implementation where the caches and cache manager are backed by a thread-safe java.util.concurrent.ConcurrentMap
. This is fine for testing and possibly for low-traffic sites, but you should consider using one of the extension plugins if you need clustering, disk storage, persistence between restarts, and more configurability of features like time-to-live, maximum cache size, etc. Currently the extension plugins include cache-ehcache, cache-redis, and cache-gemfire.
2.1 Configuration
Config.groovy and artifact files
The caching configuration can be specified inConfig.groovy
or *CacheConfig.groovy files. Both approaches support environments
blocks for environment-specific configuration, and you can specify the loading order, for example to support overriding values. One example of this might be a plugin that specifies a known load order, allowing you to choose a lower value in your file and override some or all of the plugin's configuration.There are a few configuration options for the plugin; these are specified in Config.groovy
.Property | Default | Description |
---|---|---|
grails.cache.enabled | true | Whether to enable the plugin |
grails.cache.proxyTargetClass | false | From the Spring Javadoc: "By default, all proxies are created as JDK proxies. This may cause some problems if you are injecting objects as concrete classes rather than interfaces. To overcome this restriction you can set the proxy-target-class attribute to true which will result in class-based proxies being created." |
grails.cache.aopOrder | Ordered.LOWEST_PRECEDENCE | From the Spring docs: "Defines the order of the cache advice that is applied to beans annotated with @Cacheable or @CacheEvict. No specified ordering means that the AOP subsystem determines the order of the advice." |
grails.cache.clearAtStartup | false | Whether to clear all caches at startup |
grails.cache.keyGenerator | "customCacheKeyGenerator" | Replace the key generator with your own. e.g. if your config is grails.cache.keyGenerator="myCacheKeygen" , you would declare something like this myCacheKeygen(my.company.MyKeyGen) in your resources.groovy. Note: MyKeyGen must implement KeyGenerator. |
grails.cache.cacheManager | "GrailsConcurrentMapCacheManager" | Cache Manager to use. Default cache manager uses Spring Frameworks ConcurrentMapCache which might grow limitless. If you cannot predict how many cache entries you are going to generate use "GrailsConcurrentLinkedMapCacheManager" instead which uses com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap and limits by default to 10000 entries per cache. |
2.2 Cache DSL
The cache implementation used by this plugin is very simple, so there aren't many configuration options (compared to the Ehcache implementation for example, where you have fine-grained control over features like overflowing to disk, time-to-live settings, maximum size of caches, etc.) So there aren't many supported options in the cache configuration DSL, although each plugin's DSL parser is lenient and just logs warnings if you specify options that aren't understood. This lets you share configurations between applications that use different plugins.Since there is no way to configure "time to live" with this plugin, all cached items have no timeout and remain cached until either the JVM restarts (since the backing store is in-memory) or the cache is partially or fully cleared (by calling a method or action annotated with @CacheEvict or programmatically).You specify the cache configuration in
Config.groovy
under the grails.cache.config
key, for examplegrails.cache.config = { cache { name 'message' } cache { name 'maps' } }
grails-app/conf
directory under the config
key, for exampleconfig = { cache { name 'message' } cache { name 'maps' } }
grails.cache.config = { cache { name 'message' eternal false overflowToDisk true maxElementsInMemory 10000 maxElementsOnDisk 10000000 } cache { name 'maps' } }
'GrailsConcurrentLinkedMapCacheManager'
. To specify the limit you can add the maxCapacity
parameter to the cache config. Default value for maxCapacity
is 10000.grails.cache.config = { cacheManager 'GrailsConcurrentLinkedMapCacheManager' cache { maxCapacity = 5000 name 'message' } cache { maxCapacity = 6000 name 'maps' } }
Order
You can configure your cache definitions to be loaded before or after others by setting theorder
attribute. Configurations with higher numbers are loaded later, so these can override previously-configured values, although there is no support for removing caches or cache attributes, only adding or overriding:order = 2000config = { cache { name 'message' } cache { name 'maps' } }
2.3 Annotations
The Cacheable and CacheEvict annotations provided by the plugin have counterparts with the same names provided by Spring. See the Spring documentation for their usage and allowed syntax.Service method caching
Given this simple service, you can see that thegetMessage
method is configured to cache the results in the "message"
cache. The title
parameter will be used as the cache key; if there were multiple parameters they would be combined into the key, and you can always specify the key using the Spring SpEL support. The save
method is configured as one that evicts elements from the cache. There is no need to clear the entire cache in this case; instead any previously cached item with the same title
attribute will be replaced with the current Message
instance.package com.yourcompanyimport grails.plugin.cache.CacheEvict import grails.plugin.cache.Cacheableclass MessageService { @Cacheable('message', key='#title') Message getMessage(String title) { println 'Fetching message' Message.findByTitle(title) } @CachePut(value='message', key='#message.title') void save(Message message) { println "Saving message $message" message.save() } @CacheEvict(value='message', key='#message.title') void delete(Message message) { println "Deleting message $message" message.delete() } }
save
method, which would remove the old cached value but not cache the current value.This service works with the Message
domain class:package com.yourcompanyclass Message implements Serializable { private static final long serialVersionUID = 1 String title String body String toString() { "$title: $body" } }
Serializable
but if you use an implementation that uses Java serialization (for example the Redis plugin, or the Ehcache plugin when you have configured clustered caching) you must implement Serializable
.To test this out, be sure to define a "message"
cache in Config.groovy
and save and retrieve Message
instances using the service. There are println
statements but you can also turn on SQL logging to watch the database access that's needed to retrieve instances that aren't cached yet, and you shouldn't see database access for cached values.Controller action caching
In addition to caching Spring bean return values, you can also cache responses for web requests using the same annotations. Note that since caching is implemented only for methods (Spring creates a proxy for your cached class in the same way that it creates a transactional proxy to start, commit, and roll back transactions for transactional Grails services) so you cannot annotate action closures. This doesn't fail silently; your controller class will not compile since the annotations are only allowed on the class or on methods; since Closures are fields, the annotations aren't valid.For example, in this controller thelookup
action will use the "message"
cache, so the first time you call the action you will see the output from the println
statement but subsequent calls won't execute and you'll see the cached response instead. When you call the evict
action the entire cache will be cleared (because of the allEntries=true
attribute):package com.yourcompanyimport grails.plugin.cache.CacheEvict import grails.plugin.cache.Cacheableclass TestController { @Cacheable('message') def lookup() { // perform some expensive operations println "called 'lookup'" } @CacheEvict(value='message', allEntries=true) def evict() { println "called 'evict'" } }
Caching of dynamically scaffolded actions is not supported. If the scaffolding templates are installed with grails install-templates
and cache related annotations are added to methods in the controller template, those annotations will only be relevant to generated scaffolding, not dynamic scaffolding.
If you can't use annotations
Annotations aren't required, they're just the most convenient approach for configuration. If you like you can define caching semantics ingrails-app/conf/spring/resources.groovy
(or resources.xml
if you like XML). This is also useful if you want to apply caching but can't edit the code to add annotations (for example if you have compiled classes in a jar).This Spring BeanBuilder DSL code will configure the same behavior as the two annotations in the example service class:beans = { xmlns cache: 'http://www.springframework.org/schema/cache' xmlns aop: 'http://www.springframework.org/schema/aop' cache.'advice'(id: 'messageServiceCacheAdvice', 'cache-manager': 'grailsCacheManager') { caching(cache: 'message') { cacheable(method: 'getMessage') 'cache-evict'(method: 'save', key: '#message.title') } } // apply the cacheable behavior to MessageService aop.config { advisor('advice-ref': 'messageServiceCacheAdvice', pointcut: 'execution(* com.yourcompany.MessageService.*(..))') } }
2.4 CacheManager
The plugin registers an instance of the CacheManager iterface as thegrailsCacheManager
Spring bean, so it's easy to access using dependency injection.The most common method you would call on the grailsCacheManager
is getCache(String name)
to access a Cache instance programmatically. This shouldn't be needed often however. From the Cache
instance you can also access the underlying cache implementation using cache.getNativeCache()
.