Spring自帶緩存、自建緩存管理器等都可解決項目部分性能問題。結合Ehcache後性能更優,使用也比較簡單。
在進行Ehcache學習之前,最好對Spring自帶的緩存管理有一個總體的認識。
這篇文章不錯:https://www.ibm.com/developerworks/cn/opensource/os-cn-spring-cache/
這裏用的是SpringMVC-3.2.4和Ehcache-2.7.4
介紹二者集成之前,先介紹下GoogleCode上的ehcache-spring-annotations項目
- /**
- * ehcache-spring-annotations簡介
- * @see ----------------------------------------------------------------------------------------------------------------
- * @see 關於Spring與Ehcache的集成,googlecode上有一個經Apache認證的開源項目叫做ehcache-spring-annotations
- * @see 目前已經到了1.2.0版本,它只是簡化了Spring和Ehcache集成的複雜性(事實上我覺得完全沒必要,因爲它倆集成並不複雜)
- * @see 儘管如此還是要提一下,它的項目主頁爲https://code.google.com/p/ehcache-spring-annotations/
- * @see 這篇文章中描述了其用法http://blog.goyello.com/2010/07/29/quick-start-with-ehcache-annotations-for-spring/
- * @see ----------------------------------------------------------------------------------------------------------------
- * @see 1)使用時在項目中引入ehcache-spring-annotations-1.2.0.jar和guava-r09.jar兩個jar文件
- * @see 2)然後在applicationContext.xml按照如下配置
- * @see <beans xmlns:ehcache="http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring"
- * @see xsi:schemaLocation="http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring
- * @see http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring/ehcache-spring-1.2.xsd">
- * @see <ehcache:annotation-driven/>
- * @see <ehcache:config cache-manager="cacheManager">
- * @see <ehcache:evict-expired-elements interval="60"/>
- * @see </ehcache:config>
- * @see <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
- * @see <property name="configLocation" value="classpath:ehcache.xml"/>
- * @see </bean>
- * @see 3)最後在需要緩存的方法上使用@Cacheable和@TriggersRemove註解即可
- * @see 經我親測,@TriggersRemove(cacheName="..", when="..", removeAll=true)可移除緩存中的全部對象
- * @see 但若寫成@TriggersRemove(cacheName="..", when="..")則不會移除緩存中的單一的或所有的對象,即緩存中的對象無變化
- * @see ----------------------------------------------------------------------------------------------------------------
- * @create Oct 3, 2013 4:56:35 PM
- * @author 玄玉<http://blog.csdn.net/jadyer>
- */
- <?xml version="1.0" encoding="UTF-8"?>
- <web-app version="2.5"
- xmlns="http://java.sun.com/xml/ns/javaee"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
- http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
- <!-- SpringMVC核心分發器 -->
- <servlet>
- <servlet-name>SpringMVC</servlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
- <init-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>classpath:applicationContext.xml</param-value>
- </init-param>
- <load-on-startup>1</load-on-startup>
- </servlet>
- <servlet-mapping>
- <servlet-name>SpringMVC</servlet-name>
- <url-pattern>/</url-pattern>
- </servlet-mapping>
- </web-app>
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:mvc="http://www.springframework.org/schema/mvc"
- xmlns:cache="http://www.springframework.org/schema/cache"
- xmlns:context="http://www.springframework.org/schema/context"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
- http://www.springframework.org/schema/mvc
- http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
- http://www.springframework.org/schema/cache
- http://www.springframework.org/schema/cache/spring-cache-3.2.xsd
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context-3.2.xsd">
- <context:component-scan base-package="com.jadyer"/>
- <!-- SpringMVC配置 -->
- <mvc:annotation-driven/>
- <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
- <property name="prefix" value="/"/>
- <property name="suffix" value=".jsp"/>
- </bean>
- <mvc:view-controller path="/" view-name="forward:/index.jsp"/>
- <!-- 緩存配置 -->
- <!-- 啓用緩存註解功能(請將其配置在Spring主配置文件中) -->
- <cache:annotation-driven cache-manager="cacheManager"/>
- <!-- Spring自己的基於java.util.concurrent.ConcurrentHashMap實現的緩存管理器(該功能是從Spring3.1開始提供的) -->
- <!--
- <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
- <property name="caches">
- <set>
- <bean name="myCache" class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean"/>
- </set>
- </property>
- </bean>
- -->
- <!-- 若只想使用Spring自身提供的緩存器,則註釋掉下面的兩個關於Ehcache配置的bean,並啓用上面的SimpleCacheManager即可 -->
- <!-- Spring提供的基於的Ehcache實現的緩存管理器 -->
- <bean id="cacheManagerFactory" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
- <property name="configLocation" value="classpath:ehcache.xml"/>
- </bean>
- <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
- <property name="cacheManager" ref="cacheManagerFactory"/>
- </bean>
- </beans>
- <!-- Ehcache2.x的變化(取自https://github.com/springside/springside4/wiki/Ehcache) -->
- <!-- 1)最好在ehcache.xml中聲明不進行updateCheck -->
- <!-- 2)爲了配合BigMemory和Size Limit,原來的屬性最好改名 -->
- <!-- maxElementsInMemory->maxEntriesLocalHeap -->
- <!-- maxElementsOnDisk->maxEntriesLocalDisk -->
- <ehcache>
- <diskStore path="java.io.tmpdir"/>
- <defaultCache
- maxElementsInMemory="1000"
- eternal="false"
- timeToIdleSeconds="120"
- timeToLiveSeconds="120"
- overflowToDisk="false"/>
- <cache name="myCache"
- maxElementsOnDisk="20000"
- maxElementsInMemory="2000"
- eternal="true"
- overflowToDisk="true"
- diskPersistent="true"/>
- </ehcache>
- <!--
- <diskStore>==========當內存緩存中對象數量超過maxElementsInMemory時,將緩存對象寫到磁盤緩存中(需對象實現序列化接口)
- <diskStore path="">==用來配置磁盤緩存使用的物理路徑,Ehcache磁盤緩存使用的文件後綴名是*.data和*.index
- name=================緩存名稱,cache的唯一標識(ehcache會把這個cache放到HashMap裏)
- maxElementsOnDisk====磁盤緩存中最多可以存放的元素數量,0表示無窮大
- maxElementsInMemory==內存緩存中最多可以存放的元素數量,若放入Cache中的元素超過這個數值,則有以下兩種情況
- 1)若overflowToDisk=true,則會將Cache中多出的元素放入磁盤文件中
- 2)若overflowToDisk=false,則根據memoryStoreEvictionPolicy策略替換Cache中原有的元素
- eternal==============緩存中對象是否永久有效,即是否永駐內存,true時將忽略timeToIdleSeconds和timeToLiveSeconds
- timeToIdleSeconds====緩存數據在失效前的允許閒置時間(單位:秒),僅當eternal=false時使用,默認值是0表示可閒置時間無窮大,此爲可選屬性
- 即訪問這個cache中元素的最大間隔時間,若超過這個時間沒有訪問此Cache中的某個元素,那麼此元素將被從Cache中清除
- timeToLiveSeconds====緩存數據在失效前的允許存活時間(單位:秒),僅當eternal=false時使用,默認值是0表示可存活時間無窮大
- 即Cache中的某元素從創建到清楚的生存時間,也就是說從創建開始計時,當超過這個時間時,此元素將從Cache中清除
- overflowToDisk=======內存不足時,是否啓用磁盤緩存(即內存中對象數量達到maxElementsInMemory時,Ehcache會將對象寫到磁盤中)
- 會根據標籤中path值查找對應的屬性值,寫入磁盤的文件會放在path文件夾下,文件的名稱是cache的名稱,後綴名是data
- diskPersistent=======是否持久化磁盤緩存,當這個屬性的值爲true時,系統在初始化時會在磁盤中查找文件名爲cache名稱,後綴名爲index的文件
- 這個文件中存放了已經持久化在磁盤中的cache的index,找到後會把cache加載到內存
- 要想把cache真正持久化到磁盤,寫程序時注意執行net.sf.ehcache.Cache.put(Element element)後要調用flush()方法
- diskExpiryThreadIntervalSeconds==磁盤緩存的清理線程運行間隔,默認是120秒
- diskSpoolBufferSizeMB============設置DiskStore(磁盤緩存)的緩存區大小,默認是30MB
- memoryStoreEvictionPolicy========內存存儲與釋放策略,即達到maxElementsInMemory限制時,Ehcache會根據指定策略清理內存
- 共有三種策略,分別爲LRU(最近最少使用)、LFU(最常用的)、FIFO(先進先出)
- -->
- package com.jadyer.service;
- import java.util.Map;
- import java.util.concurrent.ConcurrentHashMap;
- import org.springframework.cache.annotation.CacheEvict;
- import org.springframework.cache.annotation.Cacheable;
- import org.springframework.stereotype.Service;
- /**
- * Cacheable註解負責將方法的返回值加入到緩存中
- * CacheEvict註解負責清除緩存(它的三個參數與@Cacheable的意思是一樣的)
- * @see ----------------------------------------------------------------------------------------------------------
- * @see value------緩存位置的名稱,不能爲空,若使用EHCache則其值爲ehcache.xml中的<cache name="myCache"/>
- * @see key--------緩存的Key,默認爲空(表示使用方法的參數類型及參數值作爲key),支持SpEL
- * @see condition--只有滿足條件的情況纔會加入緩存,默認爲空(表示全部都加入緩存),支持SpEL
- * @see ----------------------------------------------------------------------------------------------------------
- * @see 該註解的源碼位於spring-context-3.2.4.RELEASE-sources.jar中
- * @see Spring針對Ehcache支持的Java源碼位於spring-context-support-3.2.4.RELEASE-sources.jar中
- * @see ----------------------------------------------------------------------------------------------------------
- * @create Oct 3, 2013 6:17:54 PM
- * @author 玄玉<http://blog.csdn.net/jadyer>
- */
- @Service
- public class UserService {
- private Map<String, String> usersData = new ConcurrentHashMap<String, String>();
- public UserService(){
- System.out.println("用戶數據初始化..開始");
- usersData.put("2", "玄玉");
- usersData.put("3", "我的博客:http://blog.csdn.net/jadyer");
- System.out.println("用戶數據初始化..完畢");
- }
- //將查詢到的數據緩存到myCache中,並使用方法名稱加上參數中的userNo作爲緩存的key
- //通常更新操作只需刷新緩存中的某個值,所以爲了準確的清除特定的緩存,故定義了這個唯一的key,從而不會影響其它緩存值
- @Cacheable(value="myCache", key="'get'+#userNo")
- public String get(String userNo){
- System.out.println("數據庫中查到此用戶號[" + userNo + "]對應的用戶名爲[" + usersData.get(userNo) + "]");
- return usersData.get(userNo);
- }
- @CacheEvict(value="myCache", key="'get'+#userNo")
- public void update(String userNo){
- System.out.println("移除緩存中此用戶號[" + userNo + "]對應的用戶名[" + usersData.get(userNo) + "]的緩存");
- }
- //allEntries爲true表示清除value中的全部緩存,默認爲false
- @CacheEvict(value="myCache", allEntries=true)
- public void removeAll(){
- System.out.println("移除緩存中的所有數據");
- }
- }
- package com.jadyer.controller;
- import javax.annotation.Resource;
- import org.springframework.stereotype.Controller;
- import org.springframework.ui.Model;
- import org.springframework.web.bind.annotation.PathVariable;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RequestMethod;
- import com.jadyer.service.UserService;
- /**
- * 這裏用到的jar如下
- * aopalliance.jar
- * commons-logging-1.1.2.jar
- * ehcache-2.7.4.jar
- * slf4j-api-1.7.5.jar
- * spring-aop-3.2.4.RELEASE.jar
- * spring-beans-3.2.4.RELEASE.jar
- * spring-context-3.2.4.RELEASE.jar
- * spring-context-support-3.2.4.RELEASE.jar
- * spring-core-3.2.4.RELEASE.jar
- * spring-expression-3.2.4.RELEASE.jar
- * spring-web-3.2.4.RELEASE.jar
- * spring-webmvc-3.2.4.RELEASE.jar
- * @create Oct 3, 2013 6:22:43 PM
- * @author 玄玉<http://blog.csdn.net/jadyer>
- */
- @Controller
- @RequestMapping("cacheTest")
- public class UserController {
- @Resource
- private UserService userService;
- @RequestMapping(value="/get/{userNo}", method=RequestMethod.GET)
- public String get(@PathVariable String userNo, Model model){
- String username = userService.get(userNo);
- model.addAttribute("username", username);
- return "getUser";
- }
- @RequestMapping(value="/update/{userNo}", method=RequestMethod.GET)
- public String update(@PathVariable String userNo, Model model){
- userService.update(userNo);
- model.addAttribute("userNo", userNo);
- return "updateUser";
- }
- @RequestMapping(value="/removeAll", method=RequestMethod.GET)
- public String removeAll(){
- userService.removeAll();
- return "removeAllUser";
- }
- }
- <%@ page language="java" pageEncoding="UTF-8"%>
- 查看<a href="<%=request.getContextPath()%>/cacheTest/get/2" target="_blank">2號</a>用戶名
- <br/>
- <br/>
- 查看<a href="<%=request.getContextPath()%>/cacheTest/get/3" target="_blank">3號</a>用戶名
- <br/>
- <br/>
- 更新<a href="<%=request.getContextPath()%>/cacheTest/update/3" target="_blank">3號</a>用戶名
- <br/>
- <br/>
- 移除<a href="<%=request.getContextPath()%>/cacheTest/removeAll" target="_blank">所有</a>用戶名
- <%@ page language="java" pageEncoding="UTF-8"%>
- 當前用戶名爲${username}
- <%@ page language="java" pageEncoding="UTF-8"%>
- 已更新${userNo}號用戶
- <%@ page language="java" pageEncoding="UTF-8"%>
- 已移除所有用戶
測試時,訪問index.jsp之後點擊各個鏈接並依次觀察控制檯輸出即可
緩存有效果的特徵是:第二次查詢數據時不會訪問數據庫(即不打印日誌)