spring redis集成

本文介绍了Spring3.1中@CachePut和@CacheEvict注解的使用方法,以及Spring Cache的基本原理,利用AOP动态代理实现缓存管理。通过分析动态代理调用图,展示了如何在方法调用前后控制缓存逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Spring3.1 Cache注解

依赖jar包:

<!-- redis -->
    <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-redis</artifactId>
      <version>1.3.4.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>redis.clients</groupId>
      <artifactId>jedis</artifactId>
      <version>2.5.2</version>
    </dependency>

applicationContext-cache-redis.xml

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

  <!-- 启用缓存注解功能,这个是必须的,否则注解不会生效,另外,该注解一定要声明在spring主配置文件中才会生效 -->
  <cache:annotation-driven cache-manager="cacheManager" />

  <!-- spring自己的换管理器,这里定义了两个缓存位置名称 ,既注解中的value -->
  <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
    <property name="caches">
      <set>
        <bean class="org.cpframework.cache.redis.RedisCache">
          <property name="redisTemplate" ref="redisTemplate" />
          <property name="name" value="default"/>
        </bean>
        <bean class="org.cpframework.cache.redis.RedisCache">
          <property name="redisTemplate" ref="redisTemplate02" />
          <property name="name" value="commonCache"/>
        </bean>
      </set>
    </property>
  </bean>

  <!-- 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="connectionFactory"
    class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
    p:host-name="${redis.host}" p:port="${redis.port}" p:pool-config-ref="poolConfig"
    p:database="${redis.database}" />

  <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
    <property name="connectionFactory" ref="connectionFactory" />
  </bean>
  
  <bean id="connectionFactory02"
    class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
    p:host-name="${redis.host}" p:port="${redis.port}" p:pool-config-ref="poolConfig"
    p:database="${redis.database}" />

  <bean id="redisTemplate02" class="org.springframework.data.redis.core.RedisTemplate">
    <property name="connectionFactory" ref="connectionFactory02" />
  </bean>

redis.properties

# Redis settings  
# server IP
redis.host=192.168.xx.xx
# server port
redis.port=6379   
# use dbIndex
redis.database=0
# 控制一个pool最多有多少个状态为idle(空闲的)的jedis实例
redis.maxIdle=300  
# 表示当borrow(引入)一个jedis实例时,最大的等待时间,如果超过等待时间(毫秒),则直接抛出JedisConnectionException;
redis.maxWait=3000  
# 在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的
redis.testOnBorrow=true

RedisCache.java

package org.cpframework.cache.redis;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

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;

  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;
  }

  @Override
  public String getName() {
    // TODO Auto-generated method stub
    return this.name;
  }

  @Override
  public Object getNativeCache() {
    // TODO Auto-generated method stub
    return this.redisTemplate;
  }

  @Override
  public ValueWrapper get(Object key) {
    // TODO Auto-generated method stub
    final String keyf = (String) key;
    Object object = null;
    object = redisTemplate.execute(new RedisCallback<Object>() {
      public Object doInRedis(RedisConnection connection)
          throws DataAccessException {

        byte[] key = keyf.getBytes();
        byte[] value = connection.get(key);
        if (value == null) {
          return null;
        }
        return toObject(value);

      }
    });
    return (object != null ? new SimpleValueWrapper(object) : null);
  }

  @Override
  public void put(Object key, Object value) {
    // TODO Auto-generated method stub
    final String keyf = (String) key;
    final Object valuef = value;
    final long liveTime = 86400;

    redisTemplate.execute(new RedisCallback<Long>() {
      public Long doInRedis(RedisConnection connection)
          throws DataAccessException {
        byte[] keyb = keyf.getBytes();
        byte[] valueb = toByteArray(valuef);
        connection.set(keyb, valueb);
        if (liveTime > 0) {
          connection.expire(keyb, liveTime);
        }
        return 1L;
      }
    });
  }

  /**
   * 描述 : <Object转byte[]>. <br>
   * <p>
   * <使用方法说明>
   * </p>
   * 
   * @param obj
   * @return
   */
  private byte[] toByteArray(Object obj) {
    byte[] bytes = null;
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    try {
      ObjectOutputStream oos = new ObjectOutputStream(bos);
      oos.writeObject(obj);
      oos.flush();
      bytes = bos.toByteArray();
      oos.close();
      bos.close();
    } catch (IOException ex) {
      ex.printStackTrace();
    }
    return bytes;
  }

  /**
   * 描述 : <byte[]转Object>. <br>
   * <p>
   * <使用方法说明>
   * </p>
   * 
   * @param bytes
   * @return
   */
  private Object toObject(byte[] bytes) {
    Object obj = null;
    try {
      ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
      ObjectInputStream ois = new ObjectInputStream(bis);
      obj = ois.readObject();
      ois.close();
      bis.close();
    } catch (IOException ex) {
      ex.printStackTrace();
    } catch (ClassNotFoundException ex) {
      ex.printStackTrace();
    }
    return obj;
  }

  @Override
  public void evict(Object key) {
    // TODO Auto-generated method stub
    final String keyf = (String) key;
    redisTemplate.execute(new RedisCallback<Long>() {
      public Long doInRedis(RedisConnection connection)
          throws DataAccessException {
        return connection.del(keyf.getBytes());
      }
    });
  }

  @Override
  public void clear() {
    // TODO Auto-generated method stub
    redisTemplate.execute(new RedisCallback<String>() {
      public String doInRedis(RedisConnection connection)
          throws DataAccessException {
        connection.flushDb();
        return "ok";
      }
    });
  }

}

@Cacheable、@CachePut、@CacheEvict 注释介绍

通过上面的例子,我们可以看到 spring cache 主要使用两个注释标签,即 @Cacheable、@CachePut 和 @CacheEvict,我们总结一下其作用和配置方法。

表 1. @Cacheable 作用和配置方法
@Cacheable 的作用主要针对方法配置,能够根据方法的请求参数对其结果进行缓存
@Cacheable 主要的参数
value缓存的名称,在 spring 配置文件中定义,必须指定至少一个例如: @Cacheable(value=”mycache”) 或者  @Cacheable(value={”cache1”,”cache2”}
key缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合例如: @Cacheable(value=”testcache”,key=”#userName”)
condition缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存例如: @Cacheable(value=”testcache”,condition=”#userName.length()>2”)
表 2. @CachePut 作用和配置方法
@CachePut 的作用主要针对方法配置,能够根据方法的请求参数对其结果进行缓存,和 @Cacheable 不同的是,它每次都会触发真实方法的调用
@CachePut 主要的参数
value缓存的名称,在 spring 配置文件中定义,必须指定至少一个例如: @Cacheable(value=”mycache”) 或者  @Cacheable(value={”cache1”,”cache2”}
key缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合例如: @Cacheable(value=”testcache”,key=”#userName”)
condition缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存例如: @Cacheable(value=”testcache”,condition=”#userName.length()>2”)
表 3. @CacheEvict 作用和配置方法
@CachEvict 的作用主要针对方法配置,能够根据一定的条件对缓存进行清空
@CacheEvict 主要的参数
value缓存的名称,在 spring 配置文件中定义,必须指定至少一个例如: @CachEvict(value=”mycache”) 或者  @CachEvict(value={”cache1”,”cache2”}
key缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合例如: @CachEvict(value=”testcache”,key=”#userName”)
condition缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才清空缓存例如: @CachEvict(value=”testcache”, condition=”#userName.length()>2”)
allEntries是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存例如: @CachEvict(value=”testcache”,allEntries=true)
beforeInvocation是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存例如: @CachEvict(value=”testcache”,beforeInvocation=true)

基本原理

和 spring 的事务管理类似,spring cache 的关键原理就是 spring AOP,通过 spring AOP,其实现了在方法调用前、调用后获取方法的入参和返回值,进而实现了缓存的逻辑。我们来看一下下面这个图:

图 2. 原始方法调用图
图 2. 原始方法调用图

上图显示,当客户端“Calling code”调用一个普通类 Plain Object 的 foo() 方法的时候,是直接作用在 pojo 类自身对象上的,客户端拥有的是被调用者的直接的引用。

而 Spring cache 利用了 Spring AOP 的动态代理技术,即当客户端尝试调用 pojo 的 foo()方法的时候,给他的不是 pojo 自身的引用,而是一个动态生成的代理类

图 3. 动态代理调用图
图 3. 动态代理调用图

如上图所示,这个时候,实际客户端拥有的是一个代理的引用,那么在调用 foo() 方法的时候,会首先调用 proxy 的 foo() 方法,这个时候 proxy 可以整体控制实际的 pojo.foo() 方法的入参和返回值,比如缓存结果,比如直接略过执行实际的 foo() 方法等,都是可以轻松做到的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值