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.
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? mailto:robenergizedwork.com or better still contact me via the
Grails User mailing list.
- GRAILSPLUGINS-2553 Cached methods with default arguments are not intercepted unless all the arguments are specified. Therefore the cache is completely bypassed (neither hit or missed).
- GRAILSPLUGINS-2544 Due to a bug with Grails itself query string parameters are not included in the generated cache key. This can be a serious issue if you are using pagination for example as query string parameters such as
offset
will be ignored resulting in the same cached content being served for every page. This only affects Grails version 1.3.4.
1.3
- Fixes bug where layout is not applied to cached content when applied by convention rather than explicitly declared in meta tag GRAILSPLUGINS-2541
- Adds
keyGenerator
element to @Cacheable
annotation GRAILSPLUGINS-2548
- Adds
cacheResolver
element to @Cacheable
annotation GRAILSPLUGINS-2167
- Content caching now respects cache control headers GRAILSPLUGINS-2616
- Adds ability to cache taglib output GRAILSPLUGINS-2569
- Deprecates
springcache.disabled = true
in favour of springcache.enabled = false
- Fixes bug with key generation from String parameters
- Updates ehcache-web to version 2.0.2
- Makes
SpringcacheService
methods safe no-ops when plugin is disabled GRAILSPLUGINS-2497
1.2.1
- Support primitive, array and
null
service method arguments in cache keys (thanks Luke Daley)
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
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 cache name specified either as a standalone value or with the
cache
element. The following declarations are equivalent:
@Cacheable("myCache")
@Cacheable(cache = "myCache")
To resolve the cache name in a non-standard way you can supply a
cacheResolver
element, see
Cache Selection. For content caching only you can also supply a
keyGenerator
element, see
Content Cache KeysThe @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 requires a single element or a String array either as a standalone value or with the
caches
element. Either way the elements 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(caches = ["cacheA", "cacheB", "cacheC"])
@CacheFlush([/cache[A-Z]/, "myCache"])
To resolve the cache names in a non-standard way you can supply a
cacheResolver
element, see
Cache Selection.
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). In some cases using Springcache does make sense, e.g. a service that aggregates the results of multiple queries.
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.
Method caching requires AspectJ auto-weaving to be enabled. If you have grails.spring.disable.aspectj.autoweaving = false
set in config then method caching will not work. Content caching is unaffected as it uses a different mechanism.
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.
When a
@Cacheable
annotation is found on a service method the plugin generates a key using:
- The target object, i.e. the service being called.
- The service method name.
- All method parameters.
Since Grails services are typically Spring singletons the target object is not usually an issue. There's no need to implements
equals or
hashCode on your service classes unless you are using a different Spring bean scope and need to differentiate between calls made to different instances of the service.
It is, however,
vital to ensure that
equals and
hashCode is properly implemented on all the types used as parameters to cached methods. If this is not done it is very unlikely that the cache will ever be hit.
Service method caching is implemented via Spring AOP, which utilises proxies. In practical terms, this means that when depending on a service with a cached method in another service or controller (or anything else for that matter), you actual receive a
proxy for the real service. This allows method calls to be intercepted and for caches to be checked or populated.
The implication of this however is that calls to
this
(implicit or explicit) do
NOT go via the proxy.
Consider the following…
class ExampleService { def nonCachedMethod() {
cachedMethod()
} @Cacheable('cachedMethodCache')
def cachedMethod() {
// do some expensive stuff
}
}
You may expect that the
nonCachedMethod()
will use the cache for
cachedMethod()
, but it won't. The call is made on
this
which is the actual instance and not the proxy.
Fortunately, there is an easy workaround…
class ExampleService { def grailsApplication def nonCachedMethod() {
grailsApplication.mainContext.exampleService.cachedMethod()
} @Cacheable('cachedMethodCache')
def cachedMethod() {
// do some expensive stuff
}
}
Instead of calling the method on
this
, we obtain the proxy via the application context (i.e.
grailsApplication.mainContext.exampleService
) and call the method on that. This way we go through the caching mechanism.
The
@Cacheable
and
@CacheFlush
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, or by caching taglib tags. 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, or taglib 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.
The
@Cacheable
and
@CacheFlush
annotations can be applied to controllers 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.
Content caching in the Springcache plugin attempts to respect any cache-control headers present in the original response.
Specifically the plugin will handle certain response headers as follows:
Cache-Control: no-cache
If this header is present in the response the content will
not be cached even if there is a
@Cacheable
annotation
present on the controller or action. This allows you to prevent caching in certain circumstances or override the
controller-wide caching policy in a particular action.
Cache-Control: max-age=x
If the response is cached the time-to-live of the cache entry is set so that it corresponds to the max-age value in
the
Cache-Control
header. If no such header is present the cache's configured time-to-live is used (see
Cache Configuration).
ETag
If the original response set an
ETag
header
Springcache will set the same header if it serves the response from the
cache.
Additionally, if an incoming request has an
If-None-Match
header that matches the
ETag
of the cached response
Springcache will send a
304 Not Modified
status code and an empty response body instead of the cached response.
Last-Modified
If the original response set a
Last-Modified
header
Springcache will set the same header if it serves the response
from the cache.
Additionally, if an incoming request has an
If-Modified-Since
header with a timestamp later than the
Last-Modified
header of the cached response
Springcache will send a
304 Not Modified
status code and an empty
response body instead of the cached response.
The Cache Headers Plugin
The
Springcache plugin integrates well with the
Cache Headers plugin. Some examples:
Preventing caching
If you want to prevent
Springcache from caching a response in certain circumstances:
@Cacheable("myCache")
def myAction = {
// …
if (someConditionHoldsThatMeansThisShouldNotGetCached) {
cache false
}
// …
}
Alternatively you might want to declare
@Cacheable
at the class level and then exclude a particular action from the
cache:
@Cacheable("myCache")
class MyController { // ... def myAction = {
cache false
// …
}
Controlling cache expiry
As explained above cache time-to-live will respect the max-age in a cache control header.
@Cacheable("myCache")
def myAction = {
cache validFor: 3600
// …
}
In this example the response will be cached with a time-to-live of one hour
regardless of the default time-to-live
configured on the cache itself.
Sending Not-Modified responses
@Cacheable("myCache")
def show = {
withCacheHeaders {
def book = Book.get(params.id)
etag {
"${book.ident()}:${book.version}"
}
lastModified {
book.dateCreated ?: book.dateUpdated
}
generate {
render view: "show", model: [item: book]
}
}
}
In this example the response will be cached and any subsequent requests that send matching
If-Modified-Since
and/or
If-None-Match
headers will be sent a
304 Not Modified
response if they hit the cache.
The Springcache plugin uses an instance of the interface
grails.plugin.springcache.key.KeyGenerator
to generate the cache key. The default implementation is a bean named
springcacheDefaultKeyGenerator which is of type
grails.plugin.springcache.web.key.DefaultKeyGenerator
. If you want to use a different key generator for a particular action you just need to add the
keyGenerator
element to the
@Cacheable
annotation specifying the name of a Spring bean that implements the
KeyGenerator
interface.
@Cacheable(cache = "albumControllerCache", keyGenerator = "myKeyGenerator")
def list = {
// …
}
Alternatively you can override the default key generator by redefining the
springcacheDefaultKeyGenerator bean in _resources.groovy_.
The keyGenerator
element is only for content caching and just works on controllers, it is ignored by the @Cacheable
annotation on service methods and taglibs.
grails.plugin.springcache.web.key.DefaultKeyGenerator
The
DefaultKeyGenerator
generates a key based on the controller name, action name and any request parameters (which can be from a query string,
POST body or those added by Grails URL mappings, e.g. the
id parameter on a standard
show or
edit action).
grails.plugin.springcache.web.key.WebContentKeyGenerator
WebContentKeyGenerator
is a multi-purpose
KeyGenerator
implementation that exposes a number of boolean properties that control key generation. All the properties default to
false_.ajax
If _true then keys will differ depending on the presence or absence of the
X-Requested-With
request header so
AJAX requests will be cached separately from regular requests. This is useful when you have an action that renders different content when it is requested via
AJAX_.contentType
If _true keys will differ depending on the requested content format as determined by the
format
meta-property on
HttpServletRequest
. This is useful when you use content negotiation in a request so that responses with different formats are cached separately.
See
Content Negotiation for more detail.
requestMethod
If
true keys will differ depending on the request HTTP method. This is useful for some
RESTful controllers (although if different request methods are mapped to different actions you do not need to use this mechanism).
GET and
HEAD requests are considered the same for the purposes of key generation.
Example configuration
ajaxAwareKeyGenerator(WebContentKeyGenerator) {
ajax = true
}contentTypeAwareKeyGenerator(WebContentKeyGenerator) {
contentType = true
}
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. You can use the
WebContentKeyGenerator
class to do this. You just need to register a key generator bean with Spring and then annotate any content negotiated actions like this:
grails-app/conf/spring/resources.groovy
mimeTypeAwareKeyGenerator(WebContentKeyGenerator) {
contentType = true
}
grails-app/controllers/MyController.groovy
@Cacheable(cache = "albumControllerCache", keyGenerator = "mimeTypeAwareKeyGenerator")
def list = {
def albumList = Album.list()
withFormat {
html { [albumList: albumList] }
xml { render albumList as XML }
json { render albumList as JSON }
}
}
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.
An alternative to using includes for caching smaller page fragments is to use caching on taglib tags. When a cacheable tag is called, the parameters it is called with from the cache key. If there is no entry in the cache, the tag is executed and the output that it generated is cached (as well as being written to the page). If there is an entry in the cache, the tag is not executed and the cached output is written to the page.
class BlogArticlesTagLib { static namespace = "blogarticles" def blogArticlesService @Cacheable("blogArticlesTagCache")
def allArticles = { attrs ->
out << "<ul>"
blogArticlesService.getArticles(attrs.id).each {
out << "<li>${it.title}</li>"
}
out << "</ul>"
}
}
When we call the tag like so…
<blogarticles:allArticles id="${blogId}" />
The cache key is formed by the 'id' tag. This tag can be reused across different views without changing the caching semantics. That is, the controller/action that the cacheable tag is called from
does not affect the cache key.
Tags with a body
When caching a tag with a body, if there is a cache hit the body will not be executed. Therefore it doesn't make sense to cache a tag that is invoked with a different body unless you are ensuring the right cacheability through the tag parameters (i.e. cache keys).
In some specialised circumstances you may need to programatically select the cache used when a
@Cacheable
or
@CacheFlush
annotation is hit. An example might be a multi-tenant application where optimising cache utilisation by caching content for different tenants in their own caches would make sense.
To apply cache selection you need a bean in the Spring context that implements the
grails.plugin.springcache.CacheResolver
interface. The interface is extremely simple having only one method
resolveCacheName(String)
which is passed the "base" cache name declared on your annotation and should return the
actual cache name to use. You can then add a
cacheResolver
parameter to
@Cacheable
and
@CacheFlush
annotations referencing the bean name of your
CacheResolver
implementation.
You can override the default
CacheResolver
used by the plugin by simply redefining the bean
springcacheDefaultCacheResolver in _resources.groovy_.
Example usage
A simple example based on the
Multi-Tenant plugin. Cached actions should use a different cache depending on the current tenant.
First we define a
CacheResolver
implementation that will simply append the current tenant id to the base cache name:
import grails.plugin.springcache.CacheResolver
import org.springframework.web.context.request.RequestContextHolderclass MultiTenantCacheResolver implements CacheResolver { def tenantResolver // a component of the Multi-Tenant plugin, see that plugin's documentation String resolveCacheName(String baseName) {
def request = RequestContextHolder.requestAttributes.currentRequest
def tenantId = tenantResolver.getTenantFromRequest(request)
"${baseName}-tenant-${tenantId}"
}
}
Then we need to wire up our cache resolver in the Spring application context in the
grails-app/conf/spring/resources.groovy
file and wiring in the dependency on the
tenantResolver bean provided by the Multi-Tenant plugin.
multiTenantCacheResolver(MultiTenantCacheResolver) {
tenantResolver = ref("tenantResolver")
}
Finally we just need to reference the
multiTenantCacheResolver bean on any annotations in parts of the code that need to be multi-tenant aware:
@Cacheable(cache = "userCache", cacheResolver = "multiTenantCacheResolver")
def list = {
[users: User.list()] // under the multi-tenant plugin this will only return user instances for the current tenant
}@CacheFlush(caches = "userCache", cacheResolver = "multiTenantCacheResolver")
def save = {
def user = new User(params)
// … save the new user, redirect, handle errors, etc.
}
If you use cache selection when it's not really appropriate it's very easy to get into problems with cache invalidation and stale data.
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.
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 instances of Spring's
EhCacheFactoryBean class. 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.
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. There's also no reason that you shouldn't use the same cache for both service method and content caching the keys will be quite distinct so this will not be a problem.
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.enabled = false
which can be done on a per-environment basis. For example:
springcache {
// cache definitions, etc
}environments {
development {
springcache.enabled = false
}
}
Whilst this makes things simpler I would encourage you to run end-to-end tests and continuous integration in as 'production-like' an environment as possible. If your continuous integration build is running with the plugin enabled you are much less likely to get any surprising behaviour when you release your app to a production environment.
Logging
To see logging from the plugin set the logging level on
grails.plugin.springcache
in your
Config.groovy
file.
Response headers
The plugin sets a header
X-Springcache-Cached with a value of
true or
false to indicate whether or not a controller response was served from the cache. This only applies to the "main" request and not to any content included using the
g:include
tag.
Can I evict only some of the contents of a cache instead of flushing the whole thing?
No. It's not possible to 'reverse engineer' cache keys into the values that were used to generate them so how would you know which keys to evict? If you find yourself asking this question you should consider using more focused caches rather than putting everything into the same bucket. The pursuit of 100% efficiency where no service method or controller action is
ever invoked when its contents could conceivably have been served from a cache is subject to the law of diminishing returns. Any time you flush a cache you may well discard
some entries that
could potentially still have been used but so long as your caches are set up sensibly that's really not something that you should worry about.
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?
In the case of controller caching you can do so by setting a
Cache-Control response header with a value of
"no-cache" which can be done manually, or by using the command
cache false
provided by the
Cache Headers plugin. See
Cache HeadersWhy 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.
If this is something you really want to see, vote for the issue on JIRA here: "GRAILSPLUGINS-2564":http://jira.codehaus.org/browse/GRAILSPLUGINS-2564
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:
Upgrading from 1.2.* to 1.3:
- You can no longer inject keyGenerator into the springcacheFilter bean. Instead you can override the springcacheDefaultKeyGenerator bean or specify the keyGenerator element on individual
@Cacheable
annotations. See Content Cache Keys
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.