Spring 整合EhCache一註解方式

前言

本文spring4通過註解方式 整合EhCache。同時提供xml註解方式和java配置的方式整合的源碼。
主要了解對數據緩存的增加、緩存的更新和緩存的刪除。
閱讀之前需要先了解spring cache相關注解的介紹。請移步http://blog.csdn.net/poorCoder_/article/details/55258253

maven 配置

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.test</groupId>
  <artifactId>ehcahe2</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>
  <build/>

  <properties>  
        <!-- spring版本號 -->  
        <spring.version>4.3.5.RELEASE</spring.version>  
        <junit.version>4.12</junit.version>
  </properties> 
    <dependencies>
        <!-- 添加Spring依賴 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>  
            <groupId>org.springframework</groupId>  
            <artifactId>spring-jdbc</artifactId>  
            <version>${spring.version}</version>  
        </dependency>
        <dependency>  
            <groupId>org.springframework</groupId>  
            <artifactId>spring-web</artifactId>  
            <version>${spring.version}</version>  
        </dependency>
        <dependency>  
            <groupId>org.springframework</groupId>  
            <artifactId>spring-webmvc</artifactId>  
            <version>${spring.version}</version>  
        </dependency> 
         <dependency>  
            <groupId>org.springframework</groupId>  
            <artifactId>spring-tx</artifactId>  
            <version>${spring.version}</version>  
        </dependency>  


        <!--單元測試依賴 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>

        <!--spring單元測試依賴 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
            <scope>test</scope>
        </dependency>

        <!-- ehcache 相關依賴  -->
        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache</artifactId>
            <version>2.7.5</version>
        </dependency>

         <!-- mybatis核心包 -->  
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.2.8</version>
        </dependency> 
        <!-- mybatis/spring包 -->  
        <dependency>  
            <groupId>org.mybatis</groupId>  
            <artifactId>mybatis-spring</artifactId>  
            <version>1.2.2</version>  
        </dependency>  

        <!-- 導入Mysql數據庫鏈接jar包 -->  
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.27</version>
            <type>jar</type>
            <scope>compile</scope>
        </dependency>

        <!-- 導入dbcp的jar包,用來在applicationContext.xml中配置數據庫 -->  
        <dependency>  
            <groupId>commons-dbcp</groupId>  
            <artifactId>commons-dbcp</artifactId>  
            <version>1.2.2</version>  
        </dependency>

         <!-- 日誌文件管理包 -->   

         <!--ehcache依賴slf4j-->
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
                <version>1.7.18</version>
            </dependency>
            <!--ehcache依賴slf4j-->

            <!--slf4j需要log4j-->
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-log4j12</artifactId>
                <version>1.7.18</version>
            </dependency>
            <!--slf4j需要log4j-->

            <!--log4j-->
            <dependency>
                <groupId>org.apache.logging.log4j</groupId>
                <artifactId>log4j-core</artifactId>
                <version>2.5</version>
            </dependency>
            <!--log4j-->

    </dependencies> 

</project>

ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="ehcache.xsd"
         updateCheck="false" monitoring="autodetect"
         dynamicConfig="true">
       <!--   
        diskStore :指定數據存儲位置,可指定磁盤中的文件夾位置   <diskStore path="E:/cachetmpdir"/> 
        defaultCache : 默認的管理策略  

        以下屬性是必須的:  
            name: Cache的名稱,必須是唯一的(ehcache會把這個cache放到HashMap裏)。maxElementsInMemory:在內存中緩存的element的最大數目。   
            maxElementsOnDisk:在磁盤上緩存的element的最大數目,默認值爲0,表示不限制。   
            eternal:設定緩存的elements是否永遠不過期。如果爲true,則緩存的數據始終有效,如果爲false那麼還要根據timeToIdleSeconds,timeToLiveSeconds判斷。   
            overflowToDisk: 如果內存中數據超過內存限制,是否要緩存到磁盤上。   

        以下屬性是可選的:  
            timeToIdleSeconds: 對象空閒時間,指對象在多長時間沒有被訪問就會失效。只對eternal爲false的有效。默認值0,表示一直可以訪問。  
            timeToLiveSeconds: 對象存活時間,指對象從創建到失效所需要的時間。只對eternal爲false的有效。默認值0,表示一直可以訪問。  
            diskPersistent: 是否在磁盤上持久化。指重啓jvm後,數據是否有效。默認爲false。   
            diskExpiryThreadIntervalSeconds: 對象檢測線程運行時間間隔。標識對象狀態的線程多長時間運行一次。   
            diskSpoolBufferSizeMB: DiskStore使用的磁盤大小,默認值30MB。每個cache使用各自的DiskStore。   
            memoryStoreEvictionPolicy: 如果內存中數據超過內存限制,向磁盤緩存時的策略。默認值LRU,可選FIFO、LFU。   

            緩存的3 種清空策略 :  
            FIFO ,first in first out (先進先出).  
            LFU , Less Frequently Used (最少使用).意思是一直以來最少被使用的。緩存的元素有一個hit 屬性,hit 值最小的將會被清出緩存。  
            LRU ,Least Recently Used(最近最少使用). (ehcache 默認值).緩存的元素有一個時間戳,當緩存容量滿了,而又需要騰出地方來緩存新的元素的時候,那麼現有緩存元素中時間戳離當前時間最遠的元素將被清出緩存。  
-->  
   <diskStore path="java.io.tmpdir"/>    
    <defaultCache 
        maxElementsInMemory="10000" 
        eternal="true"  
        timeToIdleSeconds="120" 
        timeToLiveSeconds="120" 
        overflowToDisk="true"  
        maxElementsOnDisk="10000000" 
        diskPersistent="false"  
        diskExpiryThreadIntervalSeconds="120" 
        memoryStoreEvictionPolicy="LRU" />

   <cache name="myCache" 
        maxElementsInMemory="10000"  
        maxElementsOnDisk="1000" 
        eternal="false" 
        overflowToDisk="true"  
        diskSpoolBufferSizeMB="20" 
        timeToIdleSeconds="300" 
        timeToLiveSeconds="600"  
        memoryStoreEvictionPolicy="LFU" />  
</ehcache>

實體類

實體類必須要序列化

public class User implements Serializable {

    private static final long serialVersionUID = 5272436524920090884L;
    private Integer id;
    private String name;
    //setter()  getter()...
    }

service接口:

package com.test.service;

import com.test.model.User;

public interface UserService {
    public User findUserById(int id);
    public void addUser(User user);
    public User updateUser(User user);
    public int countUser();

    public String getTimestamp(String param);
}

service實現類:

package com.test.service.impl;

import javax.annotation.Resource;

import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import com.test.dao.UserDao;
import com.test.model.User;
import com.test.service.UserService;

@Service("userService")
public class UserServiceImpl implements UserService{
    @Resource
    private UserDao userDao;

    /**
     * @Cacheable 將結果加入緩存。下次調用時直接從緩存中讀取
     * cacheNames指定緩存,要與ehcache.xml中的cache明顯相同
     */
    @Override
    @Cacheable(cacheNames="myCache",key="#id")
    public User findUserById(int id) {
        return userDao.findById(id);
    }

    /**
     * @CacheEvict 刪除緩存
     * beforeInvocation=true 表示在方法執行之前刪除緩存
     * 默認的 key=0 與下面 countUser()方法中key 相同
     */
    @Override
    @CacheEvict(cacheNames="myCache",key="0",beforeInvocation=true)
    public void addUser(User user) {
        userDao.add(user);
    }

    /**
     * @CachePut更新緩存
     * 將結果根據key 更新緩存中的內容
     */
    @Override
    @CachePut(cacheNames="myCache",key="#user.id")
    public User updateUser(User user) {
        userDao.update(user);
        return userDao.findById(user.getId());
    }

    @Override
    @Cacheable(cacheNames="myCache",key="0")
    public int countUser() {
        return userDao.count();
    }

    @Cacheable(cacheNames="myCache",key="#param")
    @Override
    public String getTimestamp(String param) {
        Long timestamp = System.currentTimeMillis();
        return timestamp.toString();
    }

}

配置

xml 配置形式

<!--啓用註解驅動緩存   -->
    <cache:annotation-driven cache-manager="cacheManager"/>  
    <!--聲明一個緩存管理器(EhCacheCacheManager) 這裏的實現代碼是通過傳入EhCache的CacheManager實例實現的 --> 
    <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">  
        <property name="cacheManager" ref="ehcache"></property>  
    </bean>  

    <bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">  
        <!-- 指定 ehcache配置文件路徑-->
        <property name="configLocation" value="classpath:ehcache.xml"></property>
    </bean>  

java類配置形式:

package com.test.config;

import org.apache.log4j.Logger;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.ehcache.EhCacheCacheManager;
import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;

@Configuration  
@EnableCaching//<!-- 啓用緩存註解 --> <cache:annotation-driven cache-manager="cacheManager" />
public class CachingConfig {

     private static final Logger logger = Logger.getLogger(CachingConfig.class);  

    @Bean  
    public EhCacheManagerFactoryBean ehCacheManagerFactoryBean() {  
        EhCacheManagerFactoryBean ehCacheManagerFactoryBean = new EhCacheManagerFactoryBean();  
        ehCacheManagerFactoryBean.setConfigLocation(new ClassPathResource(  
                "ehcache.xml"));  
        return ehCacheManagerFactoryBean;  
    }  

    @Bean  
    public CacheManager cacheManager() {  
        logger.info("EhCacheCacheManager"); 
        EhCacheCacheManager cacheManager = new EhCacheCacheManager();  
        cacheManager.setCacheManager(ehCacheManagerFactoryBean().getObject());  
        return cacheManager;  
    }  
}

測試

基類:

package com.test.baseTest;

import org.junit.runner.RunWith;  
import org.springframework.test.context.ContextConfiguration;  
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;  
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;  

import com.test.config.AppConfig;
import com.test.config.DatabaseConfig;
//XML配置
//@ContextConfiguration(locations = { "classpath:applicationContext.xml" })  
//java類配置
@ContextConfiguration(classes = {AppConfig.class})
@RunWith(SpringJUnit4ClassRunner.class)  
public class SpringTestCase extends AbstractJUnit4SpringContextTests {

}

測試類:

package com.test.testService;

import javax.annotation.Resource;

import org.junit.Test;

import com.test.baseTest.SpringTestCase;
import com.test.model.User;
import com.test.service.UserService;

public class TestService extends SpringTestCase {

    @Resource
    private UserService userService;

    @Test
    public void addUser(){
        User user = new User();
        user.setName("李四");
        userService.addUser(user);
    }

    @Test
    public void testQuery() throws InterruptedException{
        System.out.println("第一次調用");
        System.out.println( userService.findUserById(1));
        System.out.println("第二次調用");
        System.out.println(userService.findUserById(1));

    }
    @Test
    public void testUpdate(){
        User user = userService.findUserById(1);
        System.out.println( "第一次調用"+user);
        user.setName(user.getName()+"1");
        userService.updateUser(user);
        System.out.println("第二次調用"+userService.findUserById(1));
    }

    @Test
    public void testCount(){
        System.out.println("第一次查詢數量:"+userService.countUser());
        addUser();
        System.out.println("第二次查詢數量:"+userService.countUser());
    }

    @Test  
    public void getTimestampTest() throws InterruptedException{  
        System.out.println("第一次調用:" + userService.getTimestamp("param"));
        Thread.sleep(2000);
        System.out.println("2秒之後調用:" + userService.getTimestamp("param"));
    } 

}

測試結果分析

調用testQuery()方法時結果爲:
image
從結果來看:第一次調用時訪問了數據庫 並且將數據放入了緩存。第二次調用時,沒有訪問數據庫就直接返回了,緩存生效。

調用testCount()方法返回結果爲:

image
從結果中可以看出,第一次查詢數量訪問了數據庫。並且將數據放入緩存。但是執行了addUser()之後.緩存中的數據又被清除了。

調用testUpdate()返會結果:
第一次調用返回信息,並放入緩存。中間調用更新用戶信息後,將新的返回信息更到緩存中。第二次調用時,沒有訪問數據庫,但返回的數據是新的。

JDBC Connection [jdbc:mysql://localhost:3306/mytest?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&autoReconnect=true&failOverReadOnly=false, UserName=root@localhost, MySQL Connector Java] will not be managed by Spring
[DEBUG] 2017-02-16 15:42:01,118 method:org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:139)
==>  Preparing: SELECT id,name FROM t_user WHERE id = ? 
[DEBUG] 2017-02-16 15:42:01,137 method:org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:139)
==> Parameters: 1(Integer)
[DEBUG] 2017-02-16 15:42:01,150 method:org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:139)
<==      Total: 1
[DEBUG] 2017-02-16 15:42:01,151 method:org.mybatis.spring.SqlSessionUtils.closeSqlSession(SqlSessionUtils.java:168)
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7463e563]
[DEBUG] 2017-02-16 15:42:01,151 method:org.springframework.jdbc.datasource.DataSourceUtils.doReleaseConnection(DataSourceUtils.java:327)
Returning JDBC Connection to DataSource
[DEBUG] 2017-02-16 15:42:01,152 method:net.sf.ehcache.store.disk.Segment.put(Segment.java:432)
put added 0 on heap
第一次調用User [id=1, name=張三]
[DEBUG] 2017-02-16 15:42:01,156 method:org.springframework.cache.interceptor.AbstractFallbackCacheOperationSource.getCacheOperations(AbstractFallbackCacheOperationSource.java:97)
Adding cacheable method 'updateUser' with attribute: [Builder[public com.test.model.User com.test.service.impl.UserServiceImpl.updateUser(com.test.model.User)] caches=[myCache] | key='#user.id' | keyGenerator='' | cacheManager='' | cacheResolver='' | condition='' | unless='']
[DEBUG] 2017-02-16 15:42:01,156 method:org.mybatis.spring.SqlSessionUtils.getSqlSession(SqlSessionUtils.java:104)
Creating a new SqlSession
[DEBUG] 2017-02-16 15:42:01,156 method:org.mybatis.spring.SqlSessionUtils.getSqlSession(SqlSessionUtils.java:140)
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1fd4340c] was not registered for synchronization because synchronization is not active
[DEBUG] 2017-02-16 15:42:01,156 method:org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:110)
Fetching JDBC Connection from DataSource
[DEBUG] 2017-02-16 15:42:01,157 method:net.sf.ehcache.store.disk.Segment.faultInternal(Segment.java:762)
fault removed 0 from heap
[DEBUG] 2017-02-16 15:42:01,157 method:org.mybatis.spring.transaction.SpringManagedTransaction.openConnection(SpringManagedTransaction.java:86)
JDBC Connection [jdbc:mysql://localhost:3306/mytest?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&autoReconnect=true&failOverReadOnly=false, UserName=root@localhost, MySQL Connector Java] will not be managed by Spring
[DEBUG] 2017-02-16 15:42:01,157 method:net.sf.ehcache.store.disk.Segment.faultInternal(Segment.java:774)
fault added 0 on disk
[DEBUG] 2017-02-16 15:42:01,157 method:org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:139)
==>  Preparing: UPDATE t_user SET name = ? WHERE id = ? 
[DEBUG] 2017-02-16 15:42:01,158 method:org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:139)
==> Parameters: 張三1(String), 1(Integer)
[DEBUG] 2017-02-16 15:42:01,186 method:org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:139)
<==    Updates: 1
[DEBUG] 2017-02-16 15:42:01,186 method:org.mybatis.spring.SqlSessionUtils.closeSqlSession(SqlSessionUtils.java:168)
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1fd4340c]
[DEBUG] 2017-02-16 15:42:01,187 method:org.springframework.jdbc.datasource.DataSourceUtils.doReleaseConnection(DataSourceUtils.java:327)
Returning JDBC Connection to DataSource
[DEBUG] 2017-02-16 15:42:01,187 method:org.mybatis.spring.SqlSessionUtils.getSqlSession(SqlSessionUtils.java:104)
Creating a new SqlSession
[DEBUG] 2017-02-16 15:42:01,187 method:org.mybatis.spring.SqlSessionUtils.getSqlSession(SqlSessionUtils.java:140)
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@215b011c] was not registered for synchronization because synchronization is not active
[DEBUG] 2017-02-16 15:42:01,187 method:org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:110)
Fetching JDBC Connection from DataSource
[DEBUG] 2017-02-16 15:42:01,188 method:org.mybatis.spring.transaction.SpringManagedTransaction.openConnection(SpringManagedTransaction.java:86)
JDBC Connection [jdbc:mysql://localhost:3306/mytest?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&autoReconnect=true&failOverReadOnly=false, UserName=root@localhost, MySQL Connector Java] will not be managed by Spring
[DEBUG] 2017-02-16 15:42:01,188 method:org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:139)
==>  Preparing: SELECT id,name FROM t_user WHERE id = ? 
[DEBUG] 2017-02-16 15:42:01,188 method:org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:139)
==> Parameters: 1(Integer)
[DEBUG] 2017-02-16 15:42:01,189 method:org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:139)
<==      Total: 1
[DEBUG] 2017-02-16 15:42:01,189 method:org.mybatis.spring.SqlSessionUtils.closeSqlSession(SqlSessionUtils.java:168)
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@215b011c]
[DEBUG] 2017-02-16 15:42:01,189 method:org.springframework.jdbc.datasource.DataSourceUtils.doReleaseConnection(DataSourceUtils.java:327)
Returning JDBC Connection to DataSource
[DEBUG] 2017-02-16 15:42:01,193 method:net.sf.ehcache.store.disk.Segment.put(Segment.java:432)
put added 0 on heap
[DEBUG] 2017-02-16 15:42:01,195 method:net.sf.ehcache.store.disk.Segment.put(Segment.java:460)
put updated, deleted 0 on heap
[DEBUG] 2017-02-16 15:42:01,195 method:net.sf.ehcache.store.disk.Segment.put(Segment.java:464)
put updated, deleted 0 on disk
[DEBUG] 2017-02-16 15:42:01,195 method:net.sf.ehcache.store.disk.Segment.faultInternal(Segment.java:762)
fault removed 0 from heap
[DEBUG] 2017-02-16 15:42:01,195 method:net.sf.ehcache.store.disk.Segment.faultInternal(Segment.java:774)
fault added 0 on disk
第二次調用User [id=1, name=張三1]

最後附上源碼例子:
http://download.csdn.net/detail/poorcoder_/9755967

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章