浅谈SpringCache与redis集成实现缓存解决方案

作者:宇的季节 时间:2022-10-12 01:11:17 

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

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

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

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

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

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

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

浅谈SpringCache与redis集成实现缓存解决方案 

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

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

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

浅谈SpringCache与redis集成实现缓存解决方案

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

对于配置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

来源:http://www.cnblogs.com/chenkeyu/p/8028781.html

标签:Spring,Cache,redis,缓存
0
投稿

猜你喜欢

  • SpringBoot整合screw实现数据库文档自动生成的示例代码

    2023-11-29 05:30:15
  • springboot如何统一设置时区

    2022-01-26 18:16:39
  • Android超清晰6.0权限申请AndPermission

    2023-08-05 10:52:26
  • Spring学习笔记之bean的基础知识

    2021-09-08 10:09:27
  • Java通过PropertyDescriptor反射调用set和get方法

    2023-10-11 19:34:17
  • Java反射机制的简单讲解

    2023-12-12 20:49:13
  • java使用计算md5校验码方式比较两个文件是否相同

    2023-05-10 23:43:16
  • Ireport的安装与使用教程

    2021-08-08 00:16:43
  • Java 8 中 Function 接口使用方法介绍

    2022-12-08 23:54:39
  • JVM常量池的深入讲解

    2021-09-13 15:54:00
  • springboot 定时任务@Scheduled实现解析

    2023-11-10 16:18:24
  • android从资源文件中读取文件流并显示的方法

    2022-05-29 14:11:56
  • proguar在Android混淆中的用法

    2021-10-01 01:12:12
  • 关于maven:pom文件的使用解析

    2022-02-08 06:21:41
  • Spring集成jedis的配置与使用简单实例

    2023-07-02 04:11:39
  • java.util.Collection源码分析与深度理解

    2022-07-31 09:05:52
  • java 可变参数详解及实例

    2021-06-01 10:02:42
  • java打印菱形及直角和等腰三角形的方法

    2023-01-08 20:46:55
  • Mybatis实现自定义类型转换器TypeHandler的方法

    2023-09-28 14:52:22
  • JavaScript 与 Java 区别介绍 学java怎么样

    2023-11-25 12:23:49
  • asp之家 软件编程 m.aspxhome.com