采用Redis作为Web系统的缓存。用Spring的Cache整合Redis。
一、关于redis的相关xml文件的写法
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:cache="http://www.springframework.org/schema/cache"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
<!-- 开启缓存注解 -->
<cache:annotation-driven />
<!-- jedis客户端连接工厂 -->
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:host-name="192.168.1.200" p:port="6379"/>
<!-- redisTemplate模板 -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" p:connection-factory-ref="jedisConnectionFactory"/>
<!-- redis缓存管理器 -->
<bean id="cacheManager" class="org.springframework.data.redis.cache.RedisCacheManager" c:template-ref="redisTemplate"/>
</beans>
二、实体类
public class Student implements Serializable{
/**
*
*/
private static final long serialVersionUID = -4676194082313592019L;
private Integer id;
private String name;
private Integer age;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + ", age=" + age + "]";
}
}
三、数据访问层
public interface StudentMapper {
@Insert("insert into student(id,name,age) values(#{id},#{name},#{age})")
public void insert(Student student);
@Delete("delete from student where id = #{id}")
public void delete(@Param("id")Integer id);
@Update("update student set name=#{name},age=#{age} where id=#{id}")
public void update(Student student);
@Select("select * from student where id=#{id}")
public Student queryById(@Param("id")Integer id);
}
四、业务处理层(关键)
public interface IStudentService {
public void add(Student student);
public void delete(Integer id);
public void update(Student student);
public Student queryById(Integer id);
}
public class StudentServiceImpl extends BaseService implements IStudentService{
private StudentMapper mapper;
@CachePut(key="#student.id",value="student")
@Override
public void add(Student student) {
mapper = writableSQLSession.getMapper(StudentMapper.class);
mapper.insert(student);
}
@Override
public void delete(Integer id) {
mapper = writableSQLSession.getMapper(StudentMapper.class);
mapper.delete(id);
}
@Override
public void update(Student student) {
mapper = writableSQLSession.getMapper(StudentMapper.class);
mapper.update(student);
}
@Cacheable(key="#id",value="student")
@Override
public Student queryById(Integer id) {
mapper = readonlySQLSession.getMapper(StudentMapper.class);
return mapper.queryById(id);
}
}
最重要的三个注解是@Cacheable()和@CacheEvict()和@CachePut()
对于缓存声明,缓存抽象类提供两个Java注解:@Cacheable和@CacheEvict,这两个注解可以允许方法触发缓存或者缓存抽取。
@Cacheable缓存
应用到读取数据的方法上,即可缓存的方法,如查找方法。
就像注解名字的含义一样,@Cacheable用于区别可缓存的方法,指的是方法运行的结果被装入缓存的方法,因此,在随后的方法调用中,在缓存的值被返回,在实际上没有执行这个方法。最简单的构成,注解声明需要和被缓存的方法有关系的缓存名字:
@Cacheable("books")
public Book findBook(ISBN isbn) {...}
在上面的片段,findBook方法是和名字为books的缓存有关系的。方法每次被调用,这个缓存被检查去查看是否这个调用已经执行并且没有被重复。在大多数的情况下,只有一个缓存被声明,注解允许多个被指定的名字以至于不知一个缓存被正在使用。在这种情况下,每个缓存将会在执行方法之前被检查,如果至少一个缓存被选中,那么相关的值将被返回:
(注意:所有其他的没有包含这个方法的缓存将会被更新,即使这个被缓存的方法实际上没有被执行)
@Cacheable({ "books", "isbns" })
public Book findBook(ISBN isbn) {...}
默认键的生成
由于缓存实际上是键值对存贮的,一个被缓存的方法每一次的调用需要被转换成一个合适的键为了缓存访问。在这个盒子之外,缓存抽象类用一个简单的KeyGenerator(键生成器)基于以下的算法:
1.如果没有给参数,返回SimpleKey.EMPTY。
2.如果仅仅给了一个参数,返回那个实例。
3.如果不只一个参数被指定,返回一个SimpleKey包含所有参数。
只要参数具有natural keys并且实现有效的hashCode()和equals()方法,这种处理方式对于大多数用例运行完美;如果这不符合这种场景,键的生成策略需要改变。
提供一个不同的默认键生成器,需要实现org.springframework.cache.KeyGenerator接口。一旦配置,这个生成器将会用于每个没有特殊指定它自己的键生成策略的声明中。
用户自定义键生成器声明
因为缓存是一个通用类,它非常像有多种不能简单映射在缓存结构的顶部的签名的目标方法。当目标方法有多个超出了适用于缓存的参数时,这将变得明显。例如:
@Cacheable("books")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
第一眼看去,两个布尔型的参数影响找书的方式,他们对于缓存是没用的。
对于这种场景,@Cacheable注解允许用户通过key属性指定键的生成策略。开发和可以用SpEL去获取参数(或者他们包含的属性),执行的方式甚至不用写任何代码或者实现任何借口调用不同的方法。这是在默认生成器之外推荐的方法。
@CachePut
应用到写数据的方法上,如新增/修改方法
@CacheEvict
即应用到移除数据的方法上,如删除方法
提供的SpEL上下文数据
spring Cache提供了一些供我们使用的SpEL上下文数据,下表直接摘自Spring官方文档:
名字 |
位置 |
描述 |
示例 |
methodName
|
root对象
|
当前被调用的方法名
|
#root.methodName
|
method
|
root对象
|
当前被调用的方法
|
#root.method.name
|
target
|
root对象
|
当前被调用的目标对象
|
#root.target
|
targetClass
|
root对象
|
当前被调用的目标对象类
|
#root.targetClass
|
args
|
root对象
|
当前被调用的方法的参数列表
|
#root.args[0]
|
caches
|
root对象
|
当前方法调用使用的缓存列表(如@Cacheable(value={"cache1", "cache2"})),则有两个cache
|
#root.caches[0].name
|
argument name
|
执行上下文
|
当前被调用的方法的参数,如findById(Long id),我们可以通过#id拿到参数
|
#user.id
|
result
|
执行上下文
|
方法执行后的返回值(仅当方法执行之后的判断有效,如‘unless’,'cache evict'的beforeInvocation=false)
|
#result
|
条件缓存
有时,一个方法不总是适用于缓存。缓存注解通过带有SpEL表达式的conditional参数来评估是true或者false能够达到这种功能。如果返回时true,那么这个方法被缓存,如果不是,这个方法不会被缓存。一个例子,下面的方法将会被缓存,仅当参数name的长度小于32的时候:
@Cacheable(value="book", condition="#name.length < 32")
public Book findBook(String name)