缓存可以说是加速服务响应速度的一种非常有效并且简单的方式。在缓存领域,有很多知名的框架,如EhCache 、Guava、HazelCast等。Redis作为key-value型数据库,由于他的这一特性,Redis也成为一种流行的数据缓存工具。

  在传统方式下对于缓存的处理代码是非常臃肿的。

  例如:我们要把一个查询函数加入缓存功能,大致需要三步。

    一、在函数执行前,我们需要先检查缓存中是否存在数据,如果存在则返回缓存数据

    二、如果不存在,就需要在数据库的数据查询出来。

    三、最后把数据存放在缓存中,当下次调用此函数时,就可以直接使用缓存数据,减轻了数据库压力。

  那么实现上面的三步需要多少代码呢?下面是一个示例:

  

  上图中的红色部分都是模板代码,真正与这个函数有关的代码却只占了1/5,对于所有需要实现缓存功能的函数,都需要加上臃肿的模板代码。可谓是一种极不优雅的解决方案。

  那么如何让臃肿的代码重回清新的当初呢?

  AOP不就是专门解决这种模板式代码的最佳方案吗,幸运的是我们不需要再自己实现切面了,SpringCache已经为我们提供好了切面,我们只需要进行简单的配置,就可以重回当初了,像下面这样:

  

  只需要加一个注解就可以了,对于原来的代码连改都不需要改,是不是已经跃跃欲试了?

  对于配置SpringCache只需要三步:

第一步:加入相关依赖:

<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
  <version>2.9.0</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.6.0.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-lang3</artifactId>
  <version>3.3.2</version>
</dependency>

第二步:配置SpringCache,Redis连接等信息

applicationContext-redis.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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache-4.2.xsd">

  <!-- 配置文件加载 -->

  <context:property-placeholder location="classpath:*.properties"/>

<cache:annotation-driven cache-manager="cacheManager"/>
<!-- redis连接池 -->
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="${redis.maxIdle}" />
<property name="maxWaitMillis" value="${redis.maxWait}" />
<property name="testOnBorrow" value="${redis.testOnBorrow}" />
</bean>
<!-- 连接工厂 -->
<bean id="JedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
p:host-name="${redis.host}" p:port="${redis.port}" p:password="${redis.pass}" p:pool-config-ref="poolConfig"/>
<!-- redis模板 -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="JedisConnectionFactory" />
</bean> <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches">
<set>
<!-- 这里可以配置多个redis -->
<bean class="com.cky.rest.utils.RedisCache">
<property name="redisTemplate" ref="redisTemplate" />
<property name="name" value="content"/>
<!-- name对应的名称要在类或方法的注解中使用 -->
</bean>
</set>
</property>
</bean>
</beans>

redis.properties文件:

# Redis settings
# server IP
redis.host=192.168.100.55
# server port
redis.port=6379
# server pass
redis.pass=
# use dbIndex
redis.database=0
#max idel instance of jedis
redis.maxIdle=300
#if wait too long ,throw JedisConnectionException
redis.maxWait=3000
#if true,it will validate before borrow jedis instance,what you get instance is all usefull
redis.testOnBorrow=true

第三步,编写Cache接口实现类

  Spring对于缓存只是提供了抽象的接口,并且通过接口来调用功能,没有具体的实现类,所以需要我们自己实现具体的操作。

  在上面配置中可知,每个实现类都会注入一个redisTemplate实例,我们就可以通过redisTemplate来操作redis

package com.cky.rest.utils;

import java.io.Serializable;

import org.apache.commons.lang3.SerializationUtils;
import org.springframework.cache.Cache;
import org.springframework.cache.support.SimpleValueWrapper;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate; public class RedisCache implements Cache { private RedisTemplate<String, Object> redisTemplate;
private String name; @Override
public void clear() {
System.out.println("-------緩存清理------");
redisTemplate.execute(new RedisCallback<String>() {
@Override
public String doInRedis(RedisConnection connection) throws DataAccessException {
connection.flushDb();
return "ok";
}
});
} @Override
public void evict(Object key) {
System.out.println("-------緩存刪除------");
final String keyf=key.toString();
redisTemplate.execute(new RedisCallback<Long>() {
@Override
public Long doInRedis(RedisConnection connection) throws DataAccessException {
return connection.del(keyf.getBytes());
} }); } @Override
public ValueWrapper get(Object key) {
System.out.println("------缓存获取-------"+key.toString());
final String keyf = key.toString();
Object object = null;
object = redisTemplate.execute(new RedisCallback<Object>() {
@Override
public Object doInRedis(RedisConnection connection) throws DataAccessException {
byte[] key = keyf.getBytes();
byte[] value = connection.get(key);
if (value == null) {
System.out.println("------缓存不存在-------");
return null;
}
return SerializationUtils.deserialize(value);
}
});
ValueWrapper obj=(object != null ? new SimpleValueWrapper(object) : null);
System.out.println("------获取到内容-------"+obj);
return obj;
} @Override
public void put(Object key, Object value) {
System.out.println("-------加入缓存------");
System.out.println("key----:"+key);
System.out.println("key----:"+value);
final String keyString = key.toString();
final Object valuef = value;
final long liveTime = 86400;
redisTemplate.execute(new RedisCallback<Long>() {
@Override
public Long doInRedis(RedisConnection connection) throws DataAccessException {
byte[] keyb = keyString.getBytes();
byte[] valueb = SerializationUtils.serialize((Serializable) valuef);
connection.set(keyb, valueb);
if (liveTime > 0) {
connection.expire(keyb, liveTime);
}
return 1L;
}
}); } @Override
public <T> T get(Object arg0, Class<T> arg1) {
// TODO Auto-generated method stub
return null;
} @Override
public String getName() {
return this.name;
} @Override
public Object getNativeCache() {
return this.redisTemplate;
} @Override
public ValueWrapper putIfAbsent(Object arg0, Object arg1) {
// TODO Auto-generated method stub
return null;
} public RedisTemplate<String, Object> getRedisTemplate() {
return redisTemplate;
} public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
} public void setName(String name) {
this.name = name;
}
}

  

在配置过程中曾经出现过两次错误:

1.Xxxx.ClassNotFoundException 最后发现是jar下载不完整,把maven本地仓库的对应jar包文件夹删除完从新下载就好了

2.Xxxx.MethodNotFoundException 这种情况是版本不对,换成第一步中的版本就可以了

SpringCache中常见注解的使用:

@Cacheable注解

  最常用的注解,会把被注解方法的返回值缓存。工作原理是:首先在缓存中查找,如果没有执行方法并缓存结果,然后返回数据。此注解的缓存名必须指定,和cacheManager中的caches中的某一个Cache的name值相对应。可以使用value或cacheNames指定。

  如果没有指定key属性,spring会使用默认的主键生成器产生主键。也可以自定义主键,在key中可以使用SpEL表达式。如下:

    @Cacheable(cacheNames=”content”,key=”#user.userId”)
  Public User getUser(User user){
xxxxx
}

  可以使用condition属性,来给缓存添加条件,如下:

@Cacheable(cacheNames=”content”,key=”#user.userId”,condition=”#user.age<40)
Public User getUser(User user){xxxxx}

@CachePut注解

  先执行方法,然后将返回值放回缓存。可以用作缓存的更新。

@CacheEvict注解

  该注解负责从缓存中显式移除数据,通常缓存数据都有有效期,当过期时数据也会被移除。

  此注解多了两个属性:

    allEntries是否移除所有缓存条目。

    beforeInvocation:在方法调用前还是调用后完成移除操作。true/false

SpringCache与redis集成,优雅的缓存解决方案的更多相关文章

  1. 【快学SpringBoot】Spring Cache+Redis实现高可用缓存解决方案

    前言 之前已经写过一篇文章介绍SpringBoot整合Spring Cache,SpringBoot默认使用的是ConcurrentMapCacheManager,在实际项目中,我们需要一个高可用的. ...

  2. SpringBoot30 整合Mybatis-Plus、整合Redis、利用Ehcache实现二级缓存、利用SpringCache和Redis作为缓存

    1 环境说明 JDK: 1.8 MAVEN: 3. SpringBoot: 2.0.4 2 SpringBoot集成Mybatis-Plus 2.1 创建SpringBoot 利用IDEA创建Spri ...

  3. 有赞透明多级缓存解决方案(TMC)设计思路

    引子 TMC 是什么 TMC,即"透明多级缓存(Transparent Multilevel Cache)",是有赞 PaaS 团队给公司内应用提供的整体缓存解决方案. TMC 在 ...

  4. Redis与DB的数据一致性解决方案(史上最全)

    文章很长,而且持续更新,建议收藏起来,慢慢读! 高并发 发烧友社群:疯狂创客圈(总入口) 奉上以下珍贵的学习资源: 疯狂创客圈 经典图书 : 极致经典 + 社群大片好评 < Java 高并发 三 ...

  5. 洞悉Redis技术内幕:缓存,数据结构,并发,集群与算法

    "为什么这个功能用不了?" 程序员:"清一下缓存" 上篇洞悉系列文章给大家详细介绍了MySQL的存储内幕:洞悉MySQL底层架构:游走在缓冲与磁盘之间.既然聊过 ...

  6. CRL快速开发框架系列教程六(分布式缓存解决方案)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

  7. Redis基础知识之—— 缓存应用场景

    转载原文:http://www.cnblogs.com/jinshengzhi/articles/5225718.html 一.MySql+Memcached架构的问题 Memcached采用客户端- ...

  8. 基于Redis的在线用户列表解决方案

    前言: 由于项目需求,需要在集群环境下实现在线用户列表的功能,并依靠在线列表实现用户单一登陆(同一账户只能一处登陆)功能: 在单机环境下,在线列表的实现方案可以采用SessionListener来完成 ...

  9. [项目回顾]基于Redis的在线用户列表解决方案

    迁移:基于Redis的在线用户列表解决方案 前言: 由于项目需求,需要在集群环境下实现在线用户列表的功能,并依靠在线列表实现用户单一登陆(同一账户只能一处登陆)功能: 在单机环境下,在线列表的实现方案 ...

随机推荐

  1. iOS之 Category 属性 的理解

    在 Objective-C 中可以通过 Category 给一个现有的类添加属性,但是却不能添加实例变量 反正读第一遍的时候我是有点晕的,可以添加“属性”,然后又说“添加实例变量”,第一感觉就好像 有 ...

  2. HDU1175 连连看(DFS)

    Problem Description “连连看”相信很多人都玩过.没玩过也没关系,下面我给大家介绍一下游戏规则:在一个棋盘中,放了很多的棋子.如果某两个相同的棋子,可以通过一条线连起来(这条线不能经 ...

  3. 数据库无法连接JDBC

    解决:Connections could not be acquired from the underlying database! 博客分类: 问题解决 数据库 iBatis   og4j:WARN ...

  4. css之定位(position)

    1.什么是定位: css中的position属性,position有四个值:absolute/relative/fixed/static(绝对/相对/固定/静态(默认))通过定位属性可以设置一些不规则 ...

  5. aria-label

    元素中的 aria-label用来命名一个元素     它的值可以是任何字符   读屏软件就会读出aria-label里的内容 <div role=”form” aria-labelledby= ...

  6. mac android apk反编译

    在mac os系统上反编译android apk,首先需要准备好以下3个文件: 1.apktool:https://ibotpeaches.github.io/Apktool/install/ 2.d ...

  7. Linux网络中接收 "二进制" 流的那些事 --- 就recv的返回值和strlen库函数进行对话

    1.    前言 很多朋友在做网络编程开发的时候可能都遇到这样的问题,在进行接收二进制流的数据的时候,使用strlen库函数来得到 二进制数据长度的时候并不准确.为什么呢??首先,使用strlen进行 ...

  8. kafka学习笔记1:测试环境搭建

    最近因为架构中引入了kafka,一些之前在代码中通过RPC调用强耦合但是适合异步处理的内容可以用kafka重构一下. 考虑从头学一下kafka了解其特性和使用场景. 环境选择 首先是测试环境的搭建,平 ...

  9. Kotlin实现LeetCode算法题之String to Integer (atoi)

    题目String to Integer (atoi)(难度Medium) 大意是找出给定字串开头部分的整型数值,忽略开头的空格,注意符号,对超出Integer的数做取边界值处理. 方案1 class ...

  10. transition过度

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...