(Quick Reference)

2 Usage - Reference Documentation

Authors:

Version: 3.0.0

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 in Config.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 in Config.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.

PropertyDefaultDescription
grails.cache.enabledtrueWhether to enable the plugin
grails.cache.proxyTargetClassfalseFrom 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.aopOrderOrdered.LOWEST_PRECEDENCEFrom 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.clearAtStartupfalseWhether 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 example

grails.cache.config = {
   cache {
      name 'message'
   }
   cache {
      name 'maps'
   }
}

or in a *CacheConfig.groovy file in the grails-app/conf directory under the config key, for example

config = {
   cache {
      name 'message'
   }
   cache {
      name 'maps'
   }
}

Both of these will create two caches, one with name "message" and one with name "maps". You can also use attributes from other DSLs and they will be ignored, for example:

grails.cache.config = {
   cache {
      name 'message'
      eternal false
      overflowToDisk true
      maxElementsInMemory 10000
      maxElementsOnDisk 10000000
   }
   cache {
      name 'maps'
   }
}

This configuration results in the same caches as the simpler one.

If you want to limit the number of cache entries you have to change the default cache manager to '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 the order 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 = 2000

config = { 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 the getMessage 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.yourcompany

import grails.plugin.cache.CacheEvict import grails.plugin.cache.Cacheable

class MessageService {

@Cacheable('message') 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() } }

Note that you could also use @CacheEvict for the 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.yourcompany

class Message implements Serializable {

private static final long serialVersionUID = 1

String title String body

String toString() { "$title: $body" } }

Note that for in-memory cache implementations it's not required that the objects being cached implement 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 the lookup 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.yourcompany

import grails.plugin.cache.CacheEvict import grails.plugin.cache.Cacheable

class 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 in grails-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 the grailsCacheManager 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().