欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2023-12-11 16:30:58
缓存可以说是加速服务响应速度的一种非常有效并且简单的方式。在缓存领域,有很多知名的框架,如ehcache 、guava、hazelcast等。redis作为key-valu...

缓存可以说是加速服务响应速度的一种非常有效并且简单的方式。在缓存领域,有很多知名的框架,如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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

上一篇:

下一篇: