美文网首页
Redis/Spring Cache

Redis/Spring Cache

作者: 米刀灵 | 来源:发表于2016-09-29 13:57 被阅读934次

    在pom.xml加入:

    <!--redis-->
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-redis</artifactId>
        <version>${spring.redis.version}</version>
    </dependency>
    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>${redis.version}</version>
    </dependency>
    

    在applicationContext.xml中加入

    <!-- 属性文件读入 -->
    <context:property-placeholder location="classpath*:system.properties,classpath*:redis.properties,classpath*:db.properties"/>
    <!-- 启用缓存注解功能(请将其配置在Spring主配置文件中) -->
    <cache:annotation-driven cache-manager="cacheManager"/>
    <!-- =======================================Redis======================================== -->
    <!-- 配置redis池,最大空闲实例数,(创建实例时)最大等待时间,(创建实例时)是否验证 -->
    <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <property name="maxIdle" value="${redis.maxIdle}"/>
        <property name="maxWaitMillis" value="${redis.maxWaitMillis}"/>
        <property name="testOnBorrow" value="${redis.testOnBorrow}"/>
    </bean>
    
    <!-- redis连接配置,依次为主机ip,端口,是否使用池,(usePool=true时)redis的池配置 -->
    <bean id="jedisFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <property name="hostName" value="${redis.host}"/>
        <property name="port" value="${redis.port}"/>
        <property name="password" value="${redis.password}"/>
        <property name="usePool" value="true"/>
        <property name="poolConfig" ref="jedisPoolConfig"/>
    </bean>
    
    <!-- redis模板配置 -->
    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
        <property name="connectionFactory" ref="jedisFactory"/>
        <property name="keySerializer" >
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
        </property>
        //序列化
        <property name="valueSerializer" >
            <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
        </property>
    </bean>
    
    <!-- 声明 CacheManager -->
    <bean id="cacheManager" class="org.springframework.data.redis.cache.RedisCacheManager">
        <constructor-arg ref="redisTemplate"/>
    </bean>
    

    RedisTemplate中需要声明4种serializer,默认为JdkSerializationRedisSerializer:

    1. keySerializer :对于普通K-V操作时,key采取的序列化策略
    2. valueSerializer:value采取的序列化策略
    3. hashKeySerializer: 在hash数据结构中,hash-key的序列化策略
    4. hashValueSerializer:hash-value的序列化策略
      无论如何,建议key/hashKey采用StringRedisSerializer。
    • JdkSerializationRedisSerializer
      用JdkSerializationRedisSerializer序列化的话,被序列化的对象必须实现Serializable接口。
      速度快,但以字节流存储总长度长,且不容易阅读。
    • StringRedisSerializer
      一般如果key-value都是string的话,使用StringRedisSerializer就可以了
    • JacksonJsonRedisSerializer
      如果需要保存对象为json的话推荐使用JacksonJsonRedisSerializer,它不仅可以将对象序列化为json字符串并保存到redis中,但需要和jackson配合一起使用。
      被序列化的对象不用实现Serializable接口,Jackson序列化的结果清晰,容易阅读,而且存储字节少,速度较快。
    • OxmSerializer
      以xml形式的字符串存储。

    其中JdkSerializationRedisSerializer和StringRedisSerializer是最基础的序列化策略,JacksonJsonRedisSerializer与OxmSerializer都是基于stirng存储,因此它们本质上还是String(最终还是使用string解析以及构建java对象)。

    配置文件redis.properties:

    #redis中心
    redis.host=107.170.248.158
    redis.port=6379
    redis.password=123
    redis.maxIdle=100
    redis.maxActive=300
    redis.maxWaitMillis=1000
    redis.testOnBorrow=true
    redis.timeout=100000
    
    # 不需要加入缓存的类
    targetNames=
    # 不需要缓存的方法
    methodNames=
    
    #设置缓存失效时间
    com.service.impl.xxxRecordManager= 60
    com.service.impl.xxxSetRecordManager= 60
    defaultCacheExpireTime=3600
    
    fep.local.cache.capacity =10000
    

    Controller:
    要缓存的 Java 对象必须实现 Serializable 接口,因为 Spring 会将对象先序列化再存入 Redis。

    @Autowired
    private TestService testService;
    
    @RequestMapping(value="/testredis")
    public String testRedis(String key,Model model){
        String value = testService.testRedis(key).toString();
        System.out.println("get from redis key : "+key+"; value : "+value+";");
        return "test";
    }
    
    @RequestMapping(value="/testrediscaching")
    public String testRedisCaching(String key,Model model){
        String value = testService.testRedisCaching(key).toString();
        System.out.println("get from redis if cache exist. key : "+key+"; value : "+value+";");
        return "test";
    }
    
    @RequestMapping(value="/testrediscachingupdate")
    public String testRedisCachingUpdate(String key,Model model){
        String value = testService.testRedisCachingUpdate(key).toString();
        System.out.println("update the redis. key : "+key+"; value : "+value+";");
        return "test";
    }
    
    @RequestMapping(value="/testrediscachingdelete")
    public String testRedisCachingDelete(String key,Model model){
        testService.testRedisCachingDelete(key);
        System.out.println("delete from the redis. key : "+key+";");
        return "test";
    }
    

    Service:

    public interface TestService {
        Object testRedis(String s);
    
        Object testRedisCaching(String s);
    
        Object testRedisCachingEntity(String s);
    
        Object testRedisCachingUpdate(String s);
    
        Object testRedisCachingDelete(String s);
    }
    

    ServiceImpl:

    import com.zzhblh.service.TestService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cache.annotation.CacheEvict;
    import org.springframework.cache.annotation.CachePut;
    import org.springframework.cache.annotation.Cacheable;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.core.ValueOperations;
    import org.springframework.stereotype.Service;
    import java.io.Serializable;
    
    /**
     * Created by chen on 2016/8/25.
     */
    @Service("testService")
    public class TestServiceImpl implements TestService{
    
        @Autowired
        private RedisTemplate redisTemplate;
    
        @Override
        public Object testRedis(String key) {
            ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
            return operations.get(key);
        }
    
        @Override
        @Cacheable(value="testCaching")
        public Object testRedisCaching(String key) {
            System.out.println("没有用到redis缓存");
            //从数据库取出
            Object value = "World";
            return value;
        }
    
        @Override
        @Cacheable(value="testCaching")
        public Object testRedisCachingEntity(String key) {
            System.out.println("没有用到redis缓存");
            //从数据库取出
            //User必须要继承Serializable接口
            User u = new User("223","abc");
            return u;
        }
        @Override
        @CachePut(value="testCaching")
        public Object testRedisCachingUpdate(String key) {
            System.out.println("更新redis缓存");
            //更新从数据库取出,放入缓存
            Object value = "New World";
            return value;
        }
    
        @Override
        @CacheEvict(value="testCaching")
        public Object testRedisCachingDelete(String key) {
            System.out.println("删除redis缓存");
            //删除缓存
            return null;
        }
    }
    

    上面的方法使用了Spring Cache注解:
    @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")

    @CachEvict主要针对方法配置,能够根据一定的条件对缓存进行清空,除了@Cacheable的三种配置参数,另有:

    属性名称 描述 示例
    allEntries 是否清空所有缓存内容,缺省为false,如果指定为true,则方法调用后将立即清空所有缓存 @CachEvict(value=”testcache”,allEntries=true)
    beforeInvocation 是否在方法执行前就清空,缺省为false,如果指定为true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存 @CachEvict(value=”testcache”,beforeInvocation=true)

    @CachePut主要针对方法配置,能够根据方法的请求参数对其结果进行缓存,和@Cacheable不同的是,它每次都会触发真实方法。他的配置参数和@Cacheable相同

    缓存的 key 生成策略:
    对于使用 @Cacheable 注解的方法,每个缓存的key生成策略默认使用的是参数名+参数值,比如以下方法:

        @Cacheable("users")  
        public User findByUsername(String username)  
    

    这个方法的缓存将保存于key为users~keys的缓存下,对于username取值为"abc"的缓存,key为"username-abc"。

        @Cacheable("users")  
        public Integer getLoginCountByUsername(String username)  
    

    这个方法的缓存也将保存于key为users~keys的缓存下。对于username取值为"abc"的缓存,key也为"username-abc",将前一个方法的缓存覆盖掉。

    可以直接在注解中使用springEL表达式,比如以下方法:

    @Cacheable(value="users", key="#userid.toString() + #username.toString()")  
    public User findByUsername(String username, String userid)  
    
    @Cacheable(value="books", key="#isbn.rawNumber")
    public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
    
    @Cacheable(value="users", key="#p0")
    public User find(Integer id) {
        returnnull;
    }
    
    @Cacheable(value="users", key="#p0.id")
    public User find(User user) {
        returnnull;
    }
    
    属性名称 描述 示例
    methodName 当前方法名 #root.methodName
    method 当前方法 #root.method.name
    target 当前被调用的对象 #root.target
    targetClass 当前被调用的对象的class #root.targetClass
    params 调用当前方法的参数 #root.params[0]
    parameter name 调用当前方法的参数 #p0
    caches 当前被调用的方法使用的Cache #root.caches[0].name

    或使用KeyGenerator
    在配置文件中添加:

    //在application.xml中
    <bean id="myKeyGenerator" class="com.zzhblh.utils.MyKeyGenerator" />
    <cache:annotation-driven cache-manager="cacheManager" key-generator="myKeyGenerator"/>
    

    MyKeyGenerator类:

    //KeyGenerator
    import org.springframework.cache.interceptor.KeyGenerator;
    @Override
    public class MyKeyGenerator implements KeyGenerator {
        public Object generate(Object o, Method method, Object... objects) {  
            StringBuilder sb = new StringBuilder();  
            sb.append(o.getClass().getName());  
            sb.append(method.getName());  
            for (Object obj : objects) {  
                sb.append(obj.toString());  
            }  
            return sb.toString();  
        }
    }
    

    这时请求 /testrediscaching?key=Hello,
    redis中的key为 com.zzhblh.service.impl.TestServiceImpltestRedisCachingHello,同时也会有一个叫testCaching~keys的zset存储着testCaching的所有key

    但如果我们请求/testrediscachingupdate?key=Hello,
    我们发现redis中新增加了一个com.zzhblh.service.impl.TestServiceImpltestRedisCachingUpdateHello的key,请求/testrediscachingdelete?key=Hello同理。虽然这是符合myKeyGenerator中key的生成规则的,但却不符合实际的需要。可以修改MyKeyGenerator

    sb.append(method.getName().replaceAll("Update","")).replaceAll("Delete",""));
    

    无论使用什么方法,满足需求即可。

    相关文章

      网友评论

          本文标题:Redis/Spring Cache

          本文链接:https://www.haomeiwen.com/subject/gznkyttx.html