前言
本文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()方法时结果为:
从结果来看:第一次调用时访问了数据库 并且将数据放入了缓存。第二次调用时,没有访问数据库就直接返回了,缓存生效。
调用testCount()方法返回结果为:
从结果中可以看出,第一次查询数量访问了数据库。并且将数据放入缓存。但是执行了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