您的位置:首页 > 数据库 > Redis

Spring整合Redis用作缓存-注解方式

poorcoder_ 2017-03-02 16:38 78 查看

1.前言

  redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set –有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。(以上来自百度百科)

  之前写过spring整合Ehcache用作缓存的,这里区别一下。使用redis的时候,需要独立安装redis。属于独立运行的程序。所以在写单元测试的时候,第二次运行,照样可以获取到缓存中的值。但是ehcache是和java程序绑定一起的,有着相同的生命周期,所以第一次单元测试存入缓存,第二次运行是取不到的。

整合之前需要自行安装Redis。  

  

2.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>spring-redis</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<properties>
<spring.version>4.3.5.RELEASE</spring.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>

<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.8.0.RELEASE</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.18</version>
</dependency>

<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</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>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>

</dependencies>

<build/>
</project>


3.配置缓存

package com.test.config;

import java.lang.reflect.Method;

import org.apache.log4j.Logger;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;

@Configuration
@EnableCaching
public class CacheConfig{

private static final Logger log = Logger.getLogger(CacheConfig.class);

@Bean
public JedisConnectionFactory redisConnectionFactory() {
JedisConnectionFactory redisConnectionFactory = new JedisConnectionFactory();
redisConnectionFactory.setHostName("127.0.0.1");
redisConnectionFactory.setPort(6379);
return redisConnectionFactory;
}

@Bean
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory cf) {
RedisTemplate<String, String> redisTemplate = new RedisTemplate<String, String>();
redisTemplate.setConnectionFactory(cf);
return redisTemplate;
}

@Bean
public CacheManager cacheManager(RedisTemplate redisTemplate) {
RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
//这里可以设置一个默认的过期时间
cacheManager.setDefaultExpiration(300);
return cacheManager;
}

@Bean
public KeyGenerator customKeyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object o, Method method, Object... params) {
StringBuilder sb = new StringBuilder();
sb.append(o.getClass().getName());
sb.append(method.getName());
for (Object obj : params) {
sb.append(obj.toString());
}
return sb.toString();
}
};
}
}


以上配置中,JedisConnectionFactory的时候只配了地址和端口号,因为我是本地测试,且没有设置密码。如果是远程地址,可以直接修改上面的地址或端口号,还可以添加密码。设置cacheManager的时候设置了一个缓存默认的过期时间,单位是秒。

customKeyGenerator()这个方法可以不写。是自定义生key的,默认也可以。上面的生成方式只是比默认的生成方式多了一个类名。除了KeyGenerator,其他的bean都是必须配置的。这里还省略了JedisPoolConfig的配置,可以设置最大连接数、最大等待毫秒数、逐出连接的最小空闲时间等。有需要的可以直接配置一个JedisPoolConfig。

4.service 实现

一.接口:

package com.test.service;

import com.test.model.User;

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

public String getTimestamp(String param);
}


二.接口实现类

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
public class UserServiceImpl implements UserService{

@Resource
private UserDao userDao;

@Cacheable(cacheNames="user")
@Override
public User findUserById(int id) {
return userDao.findById(id);
}

@CachePut(cacheNames="user",key="#result.id")
@CacheEvict(cacheNames="countUser" ,allEntries = true)
@Override
public User addUser(User user) {
userDao.add(user);
return user;
}

@Override
@CachePut(cacheNames="user",key="#user.id")
public User updateUser(User user) {
userDao.update(user);
return user;
}

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

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

}


以上代码中,

findUserById()上有注解@Cacheable(cacheNames=”user”),表示将返回值存入缓存user。该注解没有指定key,因为findUserById()方法中只有一个参数,所以该方法的默认key就是id。

addUser()方法有两个注解,@CachePut(cacheNames=”user”,key=”#result.id”)表示,执行前不会去检查缓存中是否存在之前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中。可以是添加用户之后直接加入缓存,并且指定key是返回值的id。则下次使用该id去查询user的时候可以直接从缓存中获取。

而addUser()方法中的@CacheEvict(cacheNames=”countUser” ,allEntries = true)注解表示删除countUser中的缓存,allEntries = true表示清除countUser中所有的元素。对应于添加用户之后,将用户数量的缓存删除。以免造成查询数据出错。

updateUser() 中的@CachePut(cacheNames=”user”,key=”#user.id”)将更新后的user直接存入缓存。key是用户的id。所以根据id查询该用户时,直接从缓存中获取。

countUser() ,@Cacheable(cacheNames=”countUser”)将查询出来的数量存入缓存countUser。所以上面addUser方法中@CacheEvict注解的cacheNames也是countUser。

5.测试代码

package com.service;

import javax.annotation.Resource;

import org.junit.Test;

import com.base.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("第一次调用"+userService.findUserById(1));
System.out.println("第二次调用"+userService.findUserById(1));

}

@Test
public void testUpdate(){
User user = userService.findUserById(1);
System.out.println( "第一次调用"+user);
user.setName(user.getName()+"1");
System.out.println("更新");
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"));
Thread.sleep(10000);
System.out.println("12秒之后调用:" + userService.getTimestamp("param"));

}

}


添加用户后,测试testQuery()指定一个id,运行之后,第一次查询了数据库,第二次查询没有经过数据库,直接从缓存中获取。



注意:这里第一次查询的时候访问了数据库,因为我上面设置了缓存失效时间是300秒,所以在300秒内再次运行,这两次的结果都不会经过数据库。

testUpdate()测试更新user信息后第二次查询是更新后的内容。因为在update方法上加了@CachePut(cacheNames=”user”,key=”#user.id”)测试结果如下:



testCount()测试添加用户后,再查询时,数据从数据库中读取。执行结果图太大就不贴了。

测试getTimestampTest()方法时需要将上面cacheManager.setDefaultExpiration(10);设置成10秒,测试缓存失效时间。结果第一次 和第二次调用的时间是一样的。12秒后查询的时间和上面查询的不同。

还有其他的配置就不展示了,直接把项目传上去,大家可自行下载。

http://download.csdn.net/detail/poorcoder_/9768548

转载请注明出处:http://blog.csdn.net/poorcoder_/article/details/59541710
标签: