1 Introduction to Ehcache
EhCache is a pure Java in-process caching framework, with fast and lean features, and is the default CacheProvider in Hibernate.
Ehcache is a widely used open source Java distributed cache. Mainly aimed at universal cache, Java EE and lightweight containers. It has the features of memory and disk storage, cache loader, cache extension, cache exception handler, a gzip cache servlet filter, and supports REST and SOAP APIs.
Ehcache was originally developed by Greg Luck in 2003. In 2009, the project was purchased by Terracotta. The software is still open source, but some new major features (e.g., consistency between fast restartability) can only be used in commercial products, such as Enterprise EHCache and BigMemory. Wikimedia Foundationannounced currently uses Ehcache technology.
In short, Ehcache is still a good caching technology. Let’s take a look at how Spring is implemented with Ehcache.
2 Spring with Ehcache
The system results are as follows:
3 Specific configuration introduction
There are combinations of these parts:
src: java code, including interceptor, calling interface, testing class
src/cache-bean.xml: Configure the bean corresponding to Ehcache, interceptor, and test classes, etc.
src/ehcache.xml: Ehcache cache configuration information
WebRoot/lib: Library
4 Detailed content introduction
4.1 src
4.1.1 Interceptor
Two interceptors are first configured in the code:
The first interceptor is:
com.test.ehcache.CacheMethodInterceptor
The content is as follows:
package com.test.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.springframework.beans.factory.InitializingBean;import org.springframework.util.Assert;public class CacheMethodInterceptor implements MethodInterceptor, InitializingBean { private Cache cache; public void setCache(Cache cache) { this.cache = cache; } public CacheMethodInterceptor() { super(); } /** * Intercept the ServiceManager method and find out whether the result exists. If it exists, return the value in the cache. * Otherwise, return the database query result and put the query result into the cache */ public Object invoke(MethodInvocation invocation) throws Throwable { //Get the class String targetName to be intercepted = invocation.getThis().getClass().getName(); //Get the method of the class to be intercepted String methodName = invocation.getMethod().getName(); //Get the parameters of the method of the class to be intercepted Object[] arguments = invocation.getArguments(); Object result; //Create a string to make the key in cache String cacheKey = getCacheKey(targetName, methodName, arguments); //Get data from cache Element element = cache.get(cacheKey); if (element == null) { //If there is no data in the cache, look for non-cache, such as the database, and put the found ones into cache result = invocation.proceed(); //Generate the key and value that will be stored in the cache element = new Element(cacheKey, (Serializable) result); System.out.println("----Enter non-cache search, such as searching for the database directly, putting it into the cache after searching"); //Store the key and value into cache cache.put(element); } else { //If there is data in the cache, look for cache System.out.println("---Enter the cache search, do not look for the database, alleviating the pressure on the database"); } return element.getValue(); } /** * Method to get the cache key. The cache key is the unique identifier of an Element in the cache. * Including the package name + class name + method name, such as: com.test.service.TestServiceImpl.getObject */ 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 whether the cache is empty 70 */ public void afterPropertiesSet() throws Exception { Assert.notNull(cache, "Need a cache. Please use setCache(Cache) create it."); }}CacheMethodInterceptor is used to intercept methods starting with "get". Note that this interceptor first intercepts and then executes the original calling interface.
There is also an interceptor:
com.test.ehcache.CacheAfterReturningAdvice
Specific content:
package com.test.ehcache;import java.lang.reflect.Method;import java.util.List;import net.sf.ehcache.Cache;import org.springframework.aop.AfterReturningAdvice;import org.springframework.beans.factory.InitializingBean;import org.springframework.util.Assert;public class CacheAfterReturningAdvice implements AfterReturningAdvice, InitializingBean { private Cache cache; public void setCache(Cache cache) { this.cache = cache; } public CacheAfterReturningAdvice() { 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); System.out.println("-----Clear cache"); } } } public void afterPropertiesSet() throws Exception { Assert.notNull(cache, "Need a cache. Please use setCache(Cache) create it."); }}CacheAfterReturningAdvice is used to intercept methods starting with "update". Note that this interceptor first executes the original calling interface and then is intercepted.
4.1.2 Calling the interface
The interface name is:
com.test.service.ServiceManager
The specific content is as follows:
package com.test.service;import java.util.List;public interface ServiceManager { public List getObject(); public void updateObject(Object Object); }The implementation class name is:
com.test.service.ServiceManagerImpl
The specific content is as follows:
package com.test.service;import java.util.ArrayList;import java.util.List;public class ServiceManagerImpl implements ServiceManager { @Override public List getObject() { System.out.println("----ServiceManager: This element does not exist in the cache cache, look up the database, and put it in the cache!"); return null; } @Override public void updateObject(Object Object) { System.out.println("----ServiceManager: The object is updated, and all caches generated by this class will be removed!"); }}4.1.3 Test Class
The test class name is:
com.test.service.TestMain
The specific content is:
package com.test.service;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class TestMain { public static void main(String[] args) { String cacheString = "/cache-bean.xml"; ApplicationContext context = new ClassPathXmlApplicationContext( cacheString); //Get the bean generated by the proxy factory proxyFactory to generate an interception effect ServiceManager testService = (ServiceManager) context.getBean("proxyFactory"); // The first time searching System.out.println("=====First search"); testService.getObject(); // The second time searching System.out.println("=====Second search"); testService.getObject(); // The second time searching System.out.println("======The third time searching"); testService.updateObject(null); // The third time searching System.out.println("======The third time searching"); testService.getObject(); } }Note here that obtaining beans is produced by the proxy factory proxyFactory, so that there will be an interception effect.
It can be seen that four calls are set in the test class, and the execution order is:
First lookup Second lookup First update Third lookup
4.2 src/cache-bean.xml
cache-bean.xml is used to configure beans corresponding to Ehcache, interceptor, and test classes. The content is as follows:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <!-- Quote the configuration of ehCache--> <bean id="defaultCacheManager" > <property name="configLocation"> <value>ehcache.xml</value> </property> </bean> <!-- Define the factory of ehCache and set the name of the cache used, that is, "com.tt" --> <bean id="ehCache"> <property name="cacheManager"> <ref local="defaultCacheManager" /> </property> <!-- The name of the Cache--> <property name="cacheName"> <value>com.tt</value> </property> </bean> <!-- The interceptor for creating cache and query cache--> <bean id="cacheMethodInterceptor"> <property name="cache"> <ref local="ehCache" /> </property> </bean> <!-- The interceptor for updating cache and deleting cache--> <bean id="cacheAfterReturningAdvice"> <property name="cache"> <ref local="ehCache" /> </property> </bean> <!-- Call the interface, the intercepted object --> <bean id="serviceManager" /> <!-- Insert the interceptor to confirm which interceptor is called, the method name and characteristics of the interceptor intercepting, etc., call the interceptor here com.test.ehcache.CacheMethodInterceptor --> <bean id="cachePointCut" > <!-- Add a section, the section is the section that is added after executing the print method --> <property name="advice"> <ref local="cacheMethodInterceptor" /> </property> <property name="patterns"> <list> <!-- ### .Denotes conforming to any single character### + means conforming to the previous character once or multiple times### * means conforming to the previous character zero or multiple times### /Escape any symbol used in Regular expression --> <!-- .* means the previous prefix (including package name), which means the getObject method --> <value>.*get.*</value> </list> </property> </bean> <!-- Insert the interceptor, confirm which interceptor is called, the method name and characteristics of the interceptor intercepting method name, etc., call the interceptor com.test.ehcache.CacheAfterReturningAdvice --> <bean id="cachePointCutAdvice" > <property name="advice"> <ref local="cacheAfterReturningAdvice" /> </property> <property name="patterns"> <list> <!-- .* means the prefix (including the package name), which means updateObject method--> <value>.*update.*</value> </list> </property> </bean> <!-- Agent factory--> <bean id="proxyFactory"> <!-- Description Call interface bean name--> <property name="target"> <ref local="serviceManager" /> </property> <!-- Description Interceptor bean name--> <property name="interceptorNames"> <list> <value>cachePointCut</value> <value>cachePointCutAdvice</value> </list> </property> </beans>
The contents of each bean have been commented and noted that do not forget to agent factory beans.
4.3 src/ehcache.xml
ehcache.xml stores detailed information about Ehcache cache configuration, as follows:
<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"> <!-- Cache file location--> <diskStore path="D://temp//cache" /> <defaultCache maxElementsInMemory="1000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true" /> <!-- Define cache file information, where "com.tt" is the name of the cache file --> <cache name="com.tt" maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="300000" timeToLiveSeconds="600000" overflowToDisk="true" /> </ehcache>
You can see that the storage location of the cached storage is set to "D:/temp/cache", and the cache name is set to "com.tt", as shown in the figure:
4.4 WebRoot/lib
For the required java library, please see the system structure picture at the beginning, omitted here.
5 Test
Execute the test class, and the test results are as follows:
Through the execution results we can see:
After the first search is intercepted, it is found that it is the first intercept and the cache has not been cached. So first execute the original interface class to get the data to be queryed. It may be obtained through database query, and then generate the cache and put the queryed data into the cache.
After the second search was intercepted, it was found that the cache already exists, so the original interface class is no longer executed, that is, the database is no longer querying, and the query data is directly obtained through the cache. Of course, it's just a simple print here.
Then there is the first update . The operation done after being intercepted is to store all the data in the cache into the database and delete the cache.
Finally, there is the third query . After being intercepted, it is found that the system does not have a cache, so the original interface class query database, create a cache, and put the data obtained from the new query into the cache. The same method as the first query.
So far we have implemented what needs to be done by Spring with Ehcache.
6 Attachment source code
The attachment source code can be obtained from my github website.
The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.