`
风过无声
  • 浏览: 88256 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

开源项目springredis

 
阅读更多

项目地址:

https://github.com/CarrowZhu/springredis

 

项目简介:

基于spring-data-redis的注解实现redis缓存操作

 

requirement

JDK6 
Spring4

 

原理&实现

1)AOP
2)实现参考自Spring的Cache注解
区别:
1)支持TTL 
2)支持Hash 

 

配置说明

XML配置文件

xsi:schemaLocation="http://www.siyuan.com/schema/springredis 
http://www.siyuan.com/schema/springredis/springredis.xsd
<springRedis:annotation-driven /> 
属性说明 
redisTemplate:Advice中将使用的redisTemplate,默认为"redisTemplate" 
order:Advice的执行顺序,默认优先级最高(Ordered.HIGHEST_PRECEDENCE) 
exceptionHandler:beanId,操作异常处理器,必须实现接口com.siyuan.springredis.interceptor.SpringRedisExceptionHandler,默认为com.siyuan.springredis.interceptor.LoggerExceptionHandler 

 

注解

1)@SpringRedisConfig:Class级别配置 
属性说明 
value:等同于redisTemplate 
redisTemplate:(String)Advice中将使用的redisTemplate 

2)@SpringRedisValueCache:方法级别,操作的数据类型为String 
对应操作流程:读cache,hit返回,miss -> 获取数据 -> cache 
属性说明 
value:等同于key 
redisTemplate:(String)Advice中将使用的redisTemplate 
condition:(String)支持SpringEL,缓存操作条件 
timeout:(long)TTL,<=0表示永不过期,默认为0 
timeUnit:(TimeUnit)TTL单位,默认为TimeUnit.MILLISECONDS 
key:(String)支持SpringEL,缓存对应的key值,必须提供 
refreshTTL:(boolean)缓存命中时是否刷新TTL,默认为false 

3)@SpringRedisValueEvict :方法级别,操作的数据类型为String 
对应的流程:清除缓存 
属性说明 
value:等同于key 
redisTemplate:(String)Advice中将使用的redisTemplate 
condition:(String)支持SpringEL,缓存操作条件 
key:(String)支持SpringEL,缓存对应的key值,必须提供 

4)@SpringRedisHashCache:方法级别,操作的数据类型为Hash 
对应操作流程:与 @SpringRedisValueCache 类似
属性说明 
value:等同于key 
redisTemplate:(String)Advice中将使用的redisTemplate 
condition:(String)支持SpringEL,缓存操作条件 
timeout:(long)TTL,<=0表示永不过期,默认为0 
timeUnit:(TimeUnit)TTL单位,默认为TimeUnit.MILLISECONDS 
key:(String)支持SpringEL,缓存对应的key值,必须提供 
refreshTTL:(boolean)缓存命中时是否刷新TTL,默认为false 
hashKey:(String)支持SpringEL,缓存对应的hashKey值,必须提供 

5)@SpringRedisHashEvict :方法级别,操作的数据类型为Hash 
对应的流程:与 @SpringRedisValueEvict 类似
属性说明 
value:等同于key 
redisTemplate:(String)Advice中将使用的redisTemplate 
condition:(String)支持SpringEL,缓存操作条件 
key:(String)支持SpringEL,缓存对应的key值,必须提供 
hashKey:(String)支持SpringEL,缓存对应的hashKey值,必须提供 

 

SpringEL

Name > Location > Example 
methodName > root object > #root.methodName 
method > root object > #root.method.name 
target > root object > #root.target 
targetClass > root object > #root.targetClass 
args > root object > #root.args[0] 
argument name > evaluation context > #name (编译时必须保留方法名信息) 
result > evaluation context > #result 

 

示例

1)ApplicationContext-SpringRedis.xml

<?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:context="http://www.springframework.org/schema/context"
	xmlns:springRedis="http://www.siyuan.com/schema/springredis"
	xmlns:p="http://www.springframework.org/schema/p"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
	   http://www.springframework.org/schema/beans/spring-beans.xsd
	   http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-3.0.xsd
       http://www.siyuan.com/schema/springredis
       http://www.siyuan.com/schema/springredis/springredis.xsd">

	<springRedis:annotation-driven />
	<context:component-scan base-package="com.siyuan.springredis" />
	
	<bean id="jedisConnectionFactory"
        class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" />
        
    <bean id="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"
        p:connection-factory-ref="jedisConnectionFactory" p:key-serializer-ref="stringRedisSerializer">
        <property name="defaultSerializer">
            <bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer" />
        </property>
    </bean>
    
    <bean id="studentRedisTemplate" class="org.springframework.data.redis.core.RedisTemplate"
        p:connection-factory-ref="jedisConnectionFactory"
        p:key-serializer-ref="stringRedisSerializer"
        p:hash-key-serializer-ref="stringRedisSerializer">
        <property name="defaultSerializer">
            <bean class="org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer">
                <constructor-arg index="0" value="#{T(com.siyuan.springredis.Student)}" />
            </bean>
        </property>
    </bean>
    
</beans>

 2)Student.java

package com.siyuan.springredis;

public class Student {
	
	private Long id;
	
	private String name;
	
	public Student() {
	}
	
	public Student(Long id, String name) {
		this.id = id;
		this.name = name;
	}

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Student other = (Student) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}

	@Override
	public String toString() {
		return "Student [id=" + id + ", name=" + name + "]";
	}

}

3)StudentDAO.java 

package com.siyuan.springredis;

public interface StudentDAO {
	
	Student getById(long id);
	
	void updateStudent(Student student);
	
}

4)StudentService.java 

package com.siyuan.springredis;

import java.util.concurrent.TimeUnit;

import org.springframework.stereotype.Service;

import com.siyuan.springredis.annotation.SpringRedisConfig;
import com.siyuan.springredis.annotation.SpringRedisHashCache;
import com.siyuan.springredis.annotation.SpringRedisHashEvict;
import com.siyuan.springredis.annotation.SpringRedisValueCache;
import com.siyuan.springredis.annotation.SpringRedisValueEvict;

@Service("studentService")
@SpringRedisConfig("studentRedisTemplate")
public class StudentService {
	
	private StudentDAO studentDAO;
	
	@SpringRedisValueCache(key = "'student:' + #id", condition = "#id > 100", 
			timeout = 60, timeUnit = TimeUnit.MINUTES, refreshTTL = true)
	public Student getById(long id) {
		return studentDAO.getById(id);
	}
	
	@SpringRedisValueEvict(key = "'student:' + #student.id", condition = "#student.id > 100")
	public void updateStudent(Student student) {
		studentDAO.updateStudent(student);
	}
	
	@SpringRedisHashCache(key = "'students'", hashKey = "#id.toString()", condition = "#id > 100", 
			timeout = 60, timeUnit = TimeUnit.MINUTES, refreshTTL = true)
	public Student getById2(long id) {
		return studentDAO.getById(id);
	}
	
	@SpringRedisHashEvict(key = "'students'", hashKey = "#student.id.toString()", condition = "#student.id > 100")
	public void updateStudent2(Student student) {
		studentDAO.updateStudent(student);
	}
	
	public StudentDAO getStudentDAO() {
		return studentDAO;
	}

	public void setStudentDAO(StudentDAO studentDAO) {
		this.studentDAO = studentDAO;
	}
	
}

5)StudentServiceTest.java 

package com.siyuan.springredis.test;

import org.junit.After;

import static org.junit.Assert.*;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import static org.mockito.Mockito.*;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.siyuan.springredis.Student;
import com.siyuan.springredis.StudentDAO;
import com.siyuan.springredis.StudentService;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "/ApplicationContext-SpringRedis.xml")
public class StudentServiceTest {
	
	@Autowired
	@Qualifier("studentRedisTemplate")
	private RedisTemplate<String, Student> redisTemplate;
	
	@Autowired
	private StudentService service;
	
	private StudentDAO studentDAO;
	
	private StudentDAO mock;
	
	@Before
	public void setUp() {
		redisTemplate.delete("student:123");
		redisTemplate.delete("student:100");
		redisTemplate.delete("students");
		studentDAO = service.getStudentDAO();
		mock = mock(StudentDAO.class);
		service.setStudentDAO(mock);
	}
	
	@Test
	public void testGetById() {
		when(mock.getById(123L)).thenReturn(new Student(123L, "name:123"));
		// no cache
		Student stu = service.getById(123L);
		assertArrayEquals(new Object[] { new Student(123L, "name:123") }, new Object[] { stu });
		// cache
		stu = service.getById(123L);
		assertArrayEquals(new Object[] { new Student(123L, "name:123") }, new Object[] { stu });
		verify(mock, times(1)).getById(123L);
		
		when(mock.getById(100L)).thenReturn(new Student(100L, "name:100"));
		// no cache
		stu = service.getById(100L);
		assertArrayEquals(new Object[] { new Student(100L, "name:100") }, new Object[] { stu });
		// no cache
		stu = service.getById(100L);
		assertArrayEquals(new Object[] { new Student(100L, "name:100") }, new Object[] { stu });
		verify(mock, times(2)).getById(100L);
	}
	
	@Test
	public void testUpdateStudent() {
		// evict
		Student stu = new Student(123L, "name:123");
		redisTemplate.opsForValue().set("student:123", stu);
		service.updateStudent(stu);
		assertNull(redisTemplate.opsForValue().get("student:123"));
		
		// do not evict
		stu = new Student(100L, "name:100");
		redisTemplate.opsForValue().set("student:100", stu);
		service.updateStudent(stu);
		assertNotNull(redisTemplate.opsForValue().get("student:100"));
	}
	
	@Test
	public void testGetById2() {
		when(mock.getById(123L)).thenReturn(new Student(123L, "name:123"));
		// no cache
		Student stu = service.getById2(123L);
		assertArrayEquals(new Object[] { new Student(123L, "name:123") }, new Object[] { stu });
		// cache
		stu = service.getById2(123L);
		assertArrayEquals(new Object[] { new Student(123L, "name:123") }, new Object[] { stu });
		verify(mock, times(1)).getById(123L);
		
		when(mock.getById(100L)).thenReturn(new Student(100L, "name:100"));
		// no cache
		stu = service.getById2(100L);
		assertArrayEquals(new Object[] { new Student(100L, "name:100") }, new Object[] { stu });
		// no cache
		stu = service.getById2(100L);
		assertArrayEquals(new Object[] { new Student(100L, "name:100") }, new Object[] { stu });
		verify(mock, times(2)).getById(100L);
	}
	
	@Test
	public void testUpdateStudent2() {
		// evict
		Student stu = new Student(123L, "name:123");
		redisTemplate.opsForHash().put("students", "123", stu);
		service.updateStudent2(stu);
		assertNull(redisTemplate.opsForHash().get("students", "123"));
		
		// do not evict
		stu = new Student(100L, "name:100");
		redisTemplate.opsForHash().put("students", "100", stu);
		service.updateStudent2(stu);
		assertNotNull(redisTemplate.opsForHash().get("students", "100"));
	}
	
	@After
	public void clear() {
		service.setStudentDAO(studentDAO);
	}

}

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics