Reference: http://www.javaeye.com/topic/99721
There is a need to use spring to implement a simple cache solution with the following requirements: Using any of the existing open source cache frameworks requires that you can return results from a service in the cache system or Get/find in the DAO layer. If the data is updated (using the Create/update/delete method), the corresponding content in the cache is refreshed.
Based on the requirements, the plan uses spring AOP + Ehcache to implement this feature, and one of the reasons for using Ehcache is that spring provides ehcache support, as to why only Ehcache is supported and not supported Oscache and Jbosscache ( Hibernate???), but after all, spring provides support that can reduce some of the workload:). Second, after the implementation of the Oscache and JBoss cache mode, after a simple test found that several caches in the efficiency of not much difference (regardless of the cluster), decided to use EHCAHCE.
AOP, without interceptors, first create an interceptor that implements the Methodinterceptor interface, which intercepts the Service/dao method call, intercepts the method, searches for the result of the method in the cache, and, if present, returns the cached result in the cache. Returns the results of the query database and caches the results in the cache if it does not exist.
Methodcacheinterceptor.java
Package Com.co.cache.ehcache;
Import java.io.Serializable;
Import Net.sf.ehcache.Cache;
Import net.sf.ehcache.Element;
Import Org.aopalliance.intercept.MethodInterceptor;
Import org.aopalliance.intercept.MethodInvocation;
Import Org.apache.commons.logging.Log;
Import Org.apache.commons.logging.LogFactory;
Import Org.springframework.beans.factory.InitializingBean;
Import Org.springframework.util.Assert;
public class Methodcacheinterceptor implements Methodinterceptor, Initializingbean
{
Private static final Log logger = Logfactory.getlog (Methodcacheinterceptor.class);
Private cache cache;
public void Setcache (cache cache) {
This.cache = cache;
}
Public Methodcacheinterceptor () {
Super ();
}
/**
* Intercept the Service/dao method and find out if the result exists and return the value in the cache if it exists.
* Otherwise, the database query results are returned and the query results are placed in the cache
*/
Public Object Invoke (Methodinvocation invocation) throws Throwable {
String targetName = Invocation.getthis (). GetClass (). GetName ();
String methodName = Invocation.getmethod (). GetName ();
object[] arguments = invocation.getarguments ();
Object result;
Logger.debug ("Find object from Cache is" + cache.getname ());
String CacheKey = Getcachekey (TargetName, methodName, arguments);
Element element = Cache.get (CacheKey);
if (element = = null) {
Logger.debug ("Hold up Method, Get method result and create cache........!");
result = Invocation.proceed ();
element = new Element (CacheKey, (Serializable) result);
Cache.put (Element);
}
return Element.getvalue ();
}
/**
* Method for obtaining the cache key, which is the unique identifier of an element in the cache
* Cache key includes package name + class name + method name, such as Com.co.cache.service.UserServiceImpl.getAllUser
*/
private string Getcachekey (String targetName, String methodName, object[] arguments) {
StringBuffer sb = new StringBuffer ();
Sb.append (TargetName). Append ("."). Append (MethodName);
if (arguments! = null) && (arguments.length! = 0)) {
for (int i = 0; i < arguments.length; i++) {
Sb.append ("."). Append (Arguments[i]);
}
}
return sb.tostring ();
}
/**
* Implement Initializingbean, check if the cache is empty
*/
public void Afterpropertiesset () throws Exception {
Assert.notnull (Cache, "need a cache. Please use Setcache (Cache) create it. ");
}
}
As you can see in the code above, in method public Object invoke (Methodinvocation invocation), the search cache/new cache function is completed.
Element element = Cache.get (CacheKey);
The function of this code is to get the element in the cache, and if the element corresponding to CacheKey does not exist, a null value will be returned
result = Invocation.proceed ();
The function of this code is to get the return value of the intercepted method, please refer to the AOP related documentation for details.
Subsequently, an interceptor Methodcacheafteradvice is created to refresh the/remove-related cache content when the user makes a create/update/delete operation. This interceptor implements the Afterreturningadvice interface, which will be executed after the intercepted method executes in public void afterreturning (Object arg0, Method arg1, object[] arg2, Object Arg3) method that is scheduled in the
Package Com.co.cache.ehcache;
Import Java.lang.reflect.Method;
Import java.util.List;
Import Net.sf.ehcache.Cache;
Import Org.apache.commons.logging.Log;
Import Org.apache.commons.logging.LogFactory;
Import Org.springframework.aop.AfterReturningAdvice;
Import Org.springframework.beans.factory.InitializingBean;
Import Org.springframework.util.Assert;
public class Methodcacheafteradvice implements Afterreturningadvice, Initializingbean
{
Private static final Log logger = Logfactory.getlog (Methodcacheafteradvice.class);
Private cache cache;
public void Setcache (cache cache) {
This.cache = cache;
}
Public Methodcacheafteradvice () {
Super ();
}
public void Afterreturning (object arg0, Method arg1, object[] arg2, Object Arg3) throws Throwable {
String className = Arg3.getclass (). GetName ();
List List = Cache.getkeys ();
for (int i = 0;i<list.size (); i++) {
String CacheKey = string.valueof (List.get (i));
if (Cachekey.startswith (ClassName)) {
Cache.remove (CacheKey);
Logger.debug ("Remove cache" + CacheKey);
}
}
}
public void Afterpropertiesset () throws Exception {
Assert.notnull (Cache, "need a cache. Please use Setcache (Cache) create it. ");
}
}
The above code is very simple, implements the Afterreturning method to implement the self-afterreturningadvice interface, the content defined in the method will be executed after the target method executes, in the method
String className = Arg3.getclass (). GetName ();
The function is to get the full name of the target class, such as Com.co.cache.test.TestServiceImpl, and then loop the cache key List,remove all the element in the cache associated with that class.
Then, starting to configure the properties of the Ehcache, Ehcache requires an XML file to set some Ehcache related properties, such as the maximum number of caches, the cache refresh time, and so on.
Ehcache.xml
<ehcache>
<diskstore path= "C://myapp//cache"/>
<defaultcache
maxelementsinmemory= "1000"
Eternal= "false"
Timetoidleseconds= "120"
Timetoliveseconds= "120"
Overflowtodisk= "true"
/>
<cache name= "Default_cache"
Maxelementsinmemory= "10000"
Eternal= "false"
Timetoidleseconds= "300000"
Timetoliveseconds= "600000"
Overflowtodisk= "true"
/>
</ehcache>
Configure the detailed role of each item no longer explained in detail, interested in Google, you need to note that a little Defaultcache tag defines a default cache, the cache cannot be deleted, otherwise it will throw no default cache is Configured exception. In addition, because of the use of interceptors to refresh the cache content, you can define a larger value when defining the cache life cycle, timetoidleseconds= "300000" timetoliveseconds= "600000", as if not large enough.
Then, after configuring the cache and two interceptors to spring, there is no use of the 2.0 AOP label.
Cachecontext.xml
<?xml version= "1.0" encoding= "UTF-8"?>
<! DOCTYPE beans Public "-//spring//dtd bean//en" "Http://www.springframework.org/dtd/spring-beans.dtd" >
<beans>
<!--reference Ehcache configuration--
<bean id= "Defaultcachemanager" class= "Org.springframework.cache.ehcache.EhCacheManagerFactoryBean" >
<property name= "Configlocation" >
<value>ehcache.xml</value>
</property>
</bean>
<!--define the Ehcache factory and set the cache name to use--
<bean id= "EhCache" class= "Org.springframework.cache.ehcache.EhCacheFactoryBean" >
<property name= "CacheManager" >
<ref local= "Defaultcachemanager"/>
</property>
<property name= "CacheName" >
<value>DEFAULT_CACHE</value>
</property>
</bean>
<!--find/create Cache blocker--
<bean id= "Methodcacheinterceptor" class= "Com.co.cache.ehcache.MethodCacheInterceptor" >
<property name= "Cache" >
<ref local= "EhCache"/>
</property>
</bean>
<!--flush cache blocker--
<bean id= "Methodcacheafteradvice" class= "Com.co.cache.ehcache.MethodCacheAfterAdvice" >
<property name= "Cache" >
<ref local= "EhCache"/>
</property>
</bean>
<bean id= "Methodcachepointcut" class= "Org.springframework.aop.support.RegexpMethodPointcutAdvisor" >
<property name= "Advice" >
<ref local= "Methodcacheinterceptor"/>
</property>
<property name= "Patterns" >
<list>
<value>.*find.*</value>
<value>.*get.*</value>
</list>
</property>
</bean>
<bean id= "Methodcachepointcutadvice" class= "Org.springframework.aop.support.RegexpMethodPointcutAdvisor" >
<property name= "Advice" >
<ref local= "Methodcacheafteradvice"/>
</property>
<property name= "Patterns" >
<list>
<value>.*create.*</value>
<value>.*update.*</value>
<value>.*delete.*</value>
</list>
</property>
</bean>
</beans>
The above code eventually creates two "pointcuts", Methodcachepointcut and Methodcachepointcutadvice, which are used to intercept different method names, optionally increasing the name of the interception method as needed.
It is important to note that
<bean id= "EhCache" class= "Org.springframework.cache.ehcache.EhCacheFactoryBean" >
<property name= "CacheManager" >
<ref local= "Defaultcachemanager"/>
</property>
<property name= "CacheName" >
<value>DEFAULT_CACHE</value>
</property>
</bean>
If the name set in the CacheName property cannot be found in Ehcache.xml, the default cache (Defaultcache tag definition) will be used.
In fact here, a simple spring + ehCache framework is basically done, in order to test the effect, to give an example of a practical application, define a testservice and its implementation class Testserviceimpl, which contains
Two methods Getallobject () and Updateobject (Object object), the specific code is as follows
Testservice.java
Package com.co.cache.test;
Import java.util.List;
Public interface Testservice {
Public List getallobject ();
public void Updateobject (Object object);
}
Testserviceimpl.java
Package com.co.cache.test;
Import java.util.List;
public class Testserviceimpl implements Testservice
{
Public List Getallobject () {
SYSTEM.OUT.PRINTLN ("The element does not exist within the---testservice:cache, find and place the cache. ");
return null;
}
public void Updateobject (Object object) {
SYSTEM.OUT.PRINTLN ("---testservice: Update the object, the cache created by this class will be removed. ");
}
}
Configure using the AOP provided by spring
Applicationcontext.xml
<?xml version= "1.0" encoding= "UTF-8"?>
<! DOCTYPE beans Public "-//spring//dtd bean//en" "Http://www.springframework.org/dtd/spring-beans.dtd" >
<beans>
<import resource= "Cachecontext.xml"/>
<bean id= "Testservicetarget" class= "Com.co.cache.test.TestServiceImpl"/>
<bean id= "Testservice" class= "Org.springframework.aop.framework.ProxyFactoryBean" >
<property name= "Target" >
<ref local= "Testservicetarget"/>
</property>
<property name= "Interceptornames" >
<list>
<value>methodCachePointCut</value>
<value>methodCachePointCutAdvice</value>
</list>
</property>
</bean>
</beans>
Here must not forget the import Cachecontext.xml file, otherwise the definition of the two interceptors can not be used.
Finally, write a test code
Maintest.java
Package com.co.cache.test;
Import Org.springframework.context.ApplicationContext;
Import Org.springframework.context.support.ClassPathXmlApplicationContext;
public class maintest{
public static void Main (String args[]) {
String default_context_file = "/applicationcontext.xml";
ApplicationContext context = new Classpathxmlapplicationcontext (default_context_file);
Testservice Testservice = (testservice) context.getbean ("Testservice");
System.out.println ("First time to find and create the cache");
Testservice.getallobject ();
System.out.println ("2--Find in Cache");
Testservice.getallobject ();
System.out.println ("3--remove cache");
Testservice.updateobject (NULL);
SYSTEM.OUT.PRINTLN ("4--needs to re-locate and create the cache");
Testservice.getallobject ();
}
}
Run, the results are as follows
The first time to find and create the cache
The element does not exist in the---testservice:cache and is found and placed in the cache.
2--Find in Cache
3--remove Cache
---testservice: Updated object, the cache generated by this class will be removed.
4--need to re-locate and create the cache
The element does not exist in the---testservice:cache and is found and placed in the cache.
The
is done. As you can see, the first step executes Getallobject (), executes the method within the Testserviceimpl, and creates the cache, the second time the Getallobject () method is executed, because the cache has the caching of the method, Get out of the cache directly from the result of the method, so did not print out the contents of the Testserviceimpl, and the third step, called the Updateobject method, and Testserviceimpl related cache is removed, so when the fourth step is executed , and then execute the method in Testserviceimpl to create the cache.
There are many similar examples, but many of them are not very complete, their own reference to some examples of code, in fact, in Spring-modules also provides several cache support, Ehcache,oscache,jbosscache these, Looked at, basically is a similar way, but the package is more perfect, the main idea is also spring AOP, interested can be studied.