Springcache Plugin
The Springcache plugin allows you to easily add the following functionality to your Grails project:
- Caching of Spring bean methods (typically Grails service methods).
- Caching of page fragments generated by Grails controllers.
- Cache flushing when Spring bean methods or controller actions are invoked.
The plugin depends on the
EhCache and
EhCache-Web libraries.
Upgrading from earlier plugin versions
Successive versions of the plugin have introduced some non-backwards compatible changes. If you are upgrading from an earlier version you will need to consider the following:
to 1.2:">Upgrading from 1.1. to 1.2:
- The plugin no longer uses caching and flushing models. Instead cache names are referenced directly by the annotations. This means you will need to 'inline' your model definitions from
Config.groovy to your annotations.
- The plugin no longer supports alternate cache libraries, it's EhCache or nothing.
From pre 1.1:
- You will need to change the import statements for your
Cacheable and CacheFlush annotations to point to the grails.plugin.springcache.annotations package.
Basics
The Springcache plugin provides two annotations that are the basis of how you can apply caching and flushing behaviour to both Spring bean methods and page fragments. Both annotations are in the
grails.plugin.springcache.annotations package.
The @Cachable annotation
The @Cacheable annotation is applied to methods on Spring managed beans such as Grails services to cache method results or to controller actions to cache page fragments. The annotation requires a single argument which is the name of the cache that will be used.
The @CacheFlush annotation
The @CacheFlush annotation can be applied in the same places as the
Cacheable annotation but instead of caching results it will cause a cache or set of caches to be flushed. The
CacheFlush annotation can take a single argument or a String array. Either way the arguments can simply be literal cache names or regular expression patterns that may match multiple cache names. For example:
@CacheFlush("myCache")
@CacheFlush(/w+ControllerCache/)
@CacheFlush(["cacheA", "cacheB", "cacheC"])
@CacheFlush([/cache[A-Z]/, "myCache"])
Applying caching and flushing behaviour to Spring bean methods
The typical use case for method caching is when you have Grails service methods that invoke expensive operations such as HTTP gets, web service calls, filesystem IO, etc. Although you
can use the Springcache plugin to cache service methods that query or update GORM domain objects you should consider whether it's more appropriate to use the Hibernate 2nd level cache (see the relevant sections in the Grails documentation).
Simply add an
Cacheable annotation to methods that should cache their results and a
CacheFlush annotation to methods that should flush caches.
Be aware that the annotations will only have any effect on Spring-managed beans. If you create instances of your class directly rather than getting them from the application context they will not be decorated with caching/flushing behaviour.
A simple example might be:
PiracyService.groovy
@Cacheable("pirateCache")
def getPirates() {
// return a list of pirates
}@Cacheable("pirateCache")
def findPirates(name) {
// return a particular pirate
}@Cacheable("shipCache")
def getShips() {
// return a list of ships
}@CacheFlush("pirateCache")
void registerNewPirate(Pirate sailor) {
// store a new pirate
}@CacheFlush("shipCache")
void registerNewShip(Ship ship) {
// store a new ship
}@CacheFlush(["pirateCache", "shipCache"])
void registerNewShipWithCrew(Ship ship, Collection<Sailor> crew) {
// store a new ship and associated pirates
}
This ties the flushes on the
register methods to the particular caches they affect, so after calling
registerNewPirate the methods
getPirates and
findPirates will re-populate their cached results but
getShips would still use any cached results from previous calls. Calling
registerNewShipWithCrew will flush both caches.
It is fine for multiple methods to share the same caches. Both
getPirates and
findPirates in the example above share the same cache. Cache entries are keyed on target object (the service instance in this case), method name and call parameters so there should be no confusion when using the same caches on multiple methods.
There are various strategies you can adopt in naming and grouping caches, this example shouldn't be seen as definitive.
Page fragment caching
The same annotations can be applied to controller actions and the plugin will then cache the page fragment generated by the controller whether this is done by rendering a GSP, using a
MarkupBuilder closure, rendering text directly or whatever. Only successful page renders are cached, so redirects, 404s, errors and so on will not be.
Composing pages so that they can be optimally cached requires some thought. The plugin uses a servlet filter that runs 'inside' the SiteMesh filter provided by Grails. This means that cached output is decorated by SiteMesh and the resulting page can therefore contain uncached content from the SiteMesh template. In addition you can use caching at a modular level to cache the output of controller actions invoked using the
g:include tag. Combining these techniques leads to powerful modular page caching. For example, you can cache the output of the 'main' controller then use
g:include tags in the SiteMesh layout to include content on the page that is cached separately - and can be flushed separately - from the main body of the page.
Example: caching Grails CRUD pages
Grails' standard scaffolded
CRUD pages provide a good example of how caching and flushing can be applied. For example, let's take an
Album domain class. The scaffolded controller could be annotated like this:
AlbumController.groovy
class AlbumController {
// the index action is uncached as it just performs a redirect to list
def index = {
redirect(action: "list", params: params)
} @Cacheable("albumControllerCache")
def list = {
// standard Grails scaffolding code omitted
} @Cacheable("albumControllerCache")
def create = {
// standard Grails scaffolding code omitted
} @CacheFlush(["albumControllerCache", "artistControllerCache", "latestControllerCache", "popularControllerCache"])
def save = {
// standard Grails scaffolding code omitted
} @Cacheable("albumControllerCache")
def show = {
// standard Grails scaffolding code omitted
} @Cacheable("albumControllerCache")
def edit = {
// standard Grails scaffolding code omitted
} @CacheFlush(["albumControllerCache", "latestControllerCache", "popularControllerCache"])
def update = {
// standard Grails scaffolding code omitted
} @CacheFlush(["albumControllerCache", "artistControllerCache", "latestControllerCache", "popularControllerCache"])
def delete = {
// standard Grails scaffolding code omitted
}
}
The
list_, _show_, _create and
edit pages are all cached. The
show and
edit rely on an domain object id parameter and this will be included in the cache key so that
/album/show/1 and
/album/show/2 are cached separately. The
save_, _update and
delete actions will flush caches. Note that in addition to flushing the cache used by the
list_, _show_, _create and
edit actions they are flushing other caches which are content caches for controllers whose output should be refreshed if
Album data changes.
Example: decorating a cached page with dynamic content using SiteMesh
It is often necessary to have portions of a page be dynamic. A typical example is when something is displayed to logged in users that will be different for each user. Those sorts of page sections are not really candidates for caching. At the same time other parts of the page may well be able to take advantage of caching. For example, if you want to display a
"Welcome back $username" type message in page headers while caching the main body of the page you can use SiteMesh templates like this:
grails-app/views/layouts/main.gsp
<html>
<head>
<title><g:layoutTitle default="Welcome to My Grails Application"/></title>
<%-- render the page head from the controller - may be cached --%>
<g:layoutHead/>
</head>
<body>
<%-- render a "welcome back" header (tags used here are from the Spring Security plugin) --%>
<g:isLoggedIn>
<div id="loggedInUser"><g:message code="auth.loggedInAs" args="[loggedInUsername()]" default="Logged in as {0}"/></div>
</g:isLoggedIn>
<g:isNotLoggedIn>
<div id="loginLink"><g:link controller="login"><g:message code="default.login.label" default="Login here"/></g:link></div>
</g:isNotLoggedIn> <%-- render the page body from the controller - may be cached --%>
<g:layoutBody/>
</body>
</html>
If the controller action invoked uses
Cacheable everything will work fine because the content of the SiteMesh layout is
not cached - only the content generated by the cached action. The SiteMesh template is applied to cached and uncached content alike so the correct username will be displayed to your users even though the main body of the page may have been loaded from a cache.
Example: a modular page using multiple cached sections
One of the most powerful features of page fragment caching is that the generated page can be composed from multiple cached sections. This is accomplished using Grails'
g:include tag. For example, in this page the main body of the page is rendered by some controller action and the output of other controllers are included in the SiteMesh layout using the
g:include tag:
grails-app/views/layouts/main.gsp
<html>
<head>
<title><g:layoutTitle default="Welcome to My Grails Application"/></title>
<%-- render the page head from the controller - may be cached --%>
<g:layoutHead/>
</head>
<body>
<%-- render the page body from the controller - may be cached --%>
<g:layoutBody/> <div class="sidebar">
<%-- each of these controller actions can be cached separately as well --%>
<g:include controller="latest" action="albums"/>
<g:include controller="popular" action="albums"/>
</div>
</body>
</html>
LatestController.groovy
@Cacheable("latestAlbums")
def albums = {
def albums = Album.list(sort: "dateCreated", order: "desc", max: 10)
[albumInstanceList: albums]
}
LatestController.groovy
@Cacheable("popularAlbums")
def albums = {
def albums = Album.listOrderByAverageRating(max: 10)
return [albumInstanceList: albums]
}
If all the caches are hit the final rendered page will be composed of 3 separate cached sections. What is more, each individual section can be flushed without affecting the others so with some thought about how to compose your page and apply your caches you can optimise cache usage without delivering stale data to the user.
Example: annotations applied at class level
The
Cacheable and
CacheFlush annotations can be applied at class level. This is more likely useful with
Cacheable but it is certainly possible to apply
CacheFlush at class level so that any action on that controller will flush a set of caches. Any annotation on an individual action will be applied in preference to an annotation at class level, so a class level annotation behaves like a default. An annotation at class level will work with dynamic scaffolded actions so you don't have to generate a concrete action in order to benefit from caching behaviour.
@Cacheable("albumControllerCache")
class AlbumController { static scaffold = true // all dynamically scaffolded actions will be cached @Cacheable("albumListCache")
def list = {
// …
} @CacheFlush(/albumw+Cache/)
def save = {
// …
} def show = {
// …
}
}
In this example:
- The show action will use the default class level
Cacheable annotation and its page fragment will be cached in the albumControllerCache cache.
- The list action will not use the default as it specifies its own
Cacheable annotation and its content will be cached separately.
- The save action uses a
CacheFlush and will therefore not be cached at all.
- Dynamically scaffolded actions (e.g. edit_, _update_, etc.) will use the class level annotation and their results will be cached in the _albumControllerCache cache.
Customising key generation for page fragment caching
By default page fragment cache entries are keyed on controller name, action name and any request parameters (which can be from a query string or those added by Grails URL mappings, e.g. the
id parameter on a standard
show or
edit action). If you need to use some kind of special key generation you can implement the interface
grails.plugin.springcache.web.key.KeyGenerator (or extend
grails.plugin.springcache.web.key.AbstractKeyGenerator or one of the existing implementations that the plugin provides) then simply override the Spring bean property on the filter in
Config.groovy like this:
beans {
springcacheFilter.keyGenerator = new MyKeyGenerator()
}
Page fragment caching and content negotiation
By default the key generator used by the page fragment caching filter does not take content negotiation into account. However, if you are caching controller actions that use Grails'
@withFormat@ dynamic method to render different content types you will want to cache results separately according to the output format. The plugin provides a key generator implementation that supports this, you just need to override the filter's key generator in
Config.groovy like this:
import grails.plugin.springcache.web.key.MimeTypeAwareKeyGeneratorbeans {
springcacheFilter.keyGenerator = new MimeTypeAwareKeyGenerator()
}
Full page caching
The plugin only provides page fragment caching rather than full page caching. Full page caching is very simple to apply using the EhCache-Web library that the Springcache plugin uses. See my blog post
here for details.
Programmatic caching and flushing
Both the servlet filter used for content caching and the AOP aspects used for service method caching use a Grails service to handle caching and flushing. Your application can access this service directly if you need to do any programmatic caching or flushing. The service is called
springcacheService and can be auto-injected into your Grails artefacts just like any other Spring bean. The service provides the following methods:
- doWithCache(String, Serializable, Closure) : Parameters are cache name, cache key and closure invoked when there is no cached value. The method returns either the cached value or the return value of the closure. If the closure is invoked the return value is cached.
- doWithBlockingCache(String, Serializable, Closure) : A variant of doWithCache that ensures a BlockingCache is used and handles exceptions so that the cache's lock is relinquished correctly.
- flush(patterns) : Flushes all caches matching the specified names/patterns. The parameter can be a String, a regex pattern or a Collection or array of them.
- flushAll() : Flushes all caches.
- clearStatistics() : Clears statistics for all caches.
- getOrCreateCache(name) : Gets the named cache or creates it from defaults if it does not exist.
- getOrCreateBlockingCache(name) : As getOrCreateCache but will decorate the cache with a BlockingCache if it is non-blocking.
The plugin encourages you to use declarative caching and flushing to maintain a good separation of concerns. Over-using the
springcacheService is likely to render your code harder to test and maintain. That said programmatic caching may be necessary in some places but there are some caveats:
- If you try to perform caching or flushing in interceptors on controller actions bear in mind those actions, and therefore any interceptors, will not be invoked at all if they are annotated with
Cacheable and the cache is hit.
- Controller actions don't return HTML output so you can't do fine grained content caching by using
springcacheService.doWithCache in a controller action.
Configuring caches
Caches referenced by the annotations can be configured, either in an
ehcache.xml (usually kept in the
grails-app/conf directory) file, using
EhCacheFactoryBean definitions in
grails-app/conf/spring/resources.groovy or via
Config.groovy@. If you do not configure caches individually they will be created on demand using defaults.Configuring caches with resources.groovy
You can configure caches in @grails-app/conf/spring/resources.groovy using Spring's
EhCacheFactoryBean. For example:
grails-app/conf/spring/resources.groovy
pirateCache(EhCacheFactoryBean) { bean ->
cacheManager = ref("springcacheCacheManager")
cacheName = "pirateCache"
// these are just examples of properties you could set
eternal = false
diskPersistent = false
memoryStoreEvictionPolicy = "LRU"
}
You can inherit default cache properties from those defined in
Config.groovy by setting the factory bean's parent to '@springcacheDefaultCache@'. For example:
pirateCache(EhCacheFactoryBean) { bean ->
bean.parent = ref("springcacheDefaultCache")
cacheName = "pirateCache"
// set any properties unique to this cache
memoryStoreEvictionPolicy = "LRU"
}
Configuring caches with Config.groovy
The Springcache plugin enables you to define caches in
Config.groovy for convenience. For example:
grails-app/conf/Config.groovy
springcache {
defaults {
// set default cache properties that will apply to all caches that do not override them
eternal = false
diskPersistent = false
}
caches {
pirateCache {
// set any properties unique to this cache
memoryStoreEvictionPolicy = "LRU"
}
}
}
Under the hood this is simply setting up
EhCacheFactoryBean instances in the Spring context, so it is up to you whether you prefer to use
resources.groovy or
Config.groovy there is not much difference.
The properties shown are just examples, see the
EhCacheFactoryBean documentation for full details of all the properties you can set.
Tips
Flushing content caches with service methods and vice-versa
There is nothing special about the different types of cache so it's perfectly fine to flush a content cache with a
CacheFlush annotation on a service method or a service method cache with a
CacheFlush annotation on a controller action.
Tearing down caches in tests
In integration test and some types of functional test (e.g. Selenium RC tests when not running in remote mode) your tests can have Spring beans automatically injected. You can use this facility to tear down caches between tests. For example:
def springcacheService // auto-injected service bean from pluginvoid tearDown() {
super.tearDown()
springcacheService.flushAll()
// only need to do this if your tests are making assertions about hit/miss counts, etc.
springcacheService.clearStatistics()
}
Disabling
Rather than tearing down caches between tests you may prefer to disable the plugin altogether. This is done by setting the config key
springcache.disabled = true which can be done on a per-environment basis. For example:
springcache {
// cache definitions, etc
}environments {
test {
springcache.disabled = true
}
}
Logging Output
To see logging from the plugin set the logging level on
grails.plugin.springcache in your
Config.groovy file.
FAQ
My cache config doesn't seem to be working.
Ensure all your config for the Springcache plugin is nested inside a single
springcache block in
Config.groovy otherwise only the last block will take effect.
Can I programatically disable caching in specific circumstances such as when a user is logged in?
Not right now, although I hope to add this to a future release of the plugin. I'd encourage you to think about structuring your pages using SiteMesh layouts and the
g:include tag such that it is possible to separate the dynamic and cacheable sections.
Why isn't there a taglib so I can just wrap parts of my page that need caching?
It's something I may add but from a purist point of view I'm not very keen on the idea. Caching is a separate concern from view rendering and the two really shouldn't be mixed up. So far the plugin has deliberately taken a declarative approach to caching which encourages you to maintain a good separation of concerns.
Can I use @Cacheable on taglib Closures?
Not yet. This is definitely on the roadmap for a future version of the plugin.
Release Notes
1.2
- Adds page fragment caching via annotations on controllers.
- Simplifies config by getting rid of caching and flushing models and having annotations refer to cache names directly.
- Adds configurable cache defaults that apply to configured caches and auto-created caches
- Removes pluggable cache implementation in favour of using EhCache.
1.1.3
- Fixes bug where an expired ehcache element whose key is still in the cache can cause the plugin to think the key still maps to a valid value.
- Allows configuration of ehcache caches directly in @Config.groovy@
1.1.2
- Automatically create ehcache caches if they are not explicitly configured in @ehcache.xml@
1.1.1
- Fixes bug where plugin crashes if disabled
1.1
- Complete rewrite to support Grails 1.2 and Spring 3.0.
- Requires Grails 1.2+
1.0.1
- Fixes bug where plugin causes crash if disabled when debug logging is switched on.
- Fixes compatibility with Java 1.5.
1.0
- Configure alternate caching providers via
Config.groovy rather than having to override bean definitions in @resources.groovy@
- Removed dependency on joda-time which was only there for testing
- Better synchronization for getting caches from the mapcache CacheManager
0.2
- Configure caching and flushing models via @Config.groovy@
- Flag to disable plugin entirely for testing environments
0.1
Contact
The plugin code is hosted on
GitHub. Please feel free to fork the plugin and contribute patches.
Please raise defects or enhancements against the Grails Springcache plugin component on the
Codehaus JIRA.
Questions, comments?
rob@energizedwork.com.