Redis/Spring Cache

在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池,最大空閑實例數(shù),(創(chuàng)建實例時)最大等待時間,(創(chuàng)建實例時)是否驗證 -->
<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連接配置,依次為主機(jī)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,默認(rèn)為JdkSerializationRedisSerializer:

  1. keySerializer :對于普通K-V操作時,key采取的序列化策略
  2. valueSerializer:value采取的序列化策略
  3. hashKeySerializer: 在hash數(shù)據(jù)結(jié)構(gòu)中,hash-key的序列化策略
  4. hashValueSerializer:hash-value的序列化策略
    無論如何,建議key/hashKey采用StringRedisSerializer。
  • JdkSerializationRedisSerializer
    用JdkSerializationRedisSerializer序列化的話,被序列化的對象必須實現(xiàn)Serializable接口。
    速度快,但以字節(jié)流存儲總長度長,且不容易閱讀。
  • StringRedisSerializer
    一般如果key-value都是string的話,使用StringRedisSerializer就可以了
  • JacksonJsonRedisSerializer
    如果需要保存對象為json的話推薦使用JacksonJsonRedisSerializer,它不僅可以將對象序列化為json字符串并保存到redis中,但需要和jackson配合一起使用。
    被序列化的對象不用實現(xiàn)Serializable接口,Jackson序列化的結(jié)果清晰,容易閱讀,而且存儲字節(jié)少,速度較快。
  • OxmSerializer
    以xml形式的字符串存儲。

其中JdkSerializationRedisSerializer和StringRedisSerializer是最基礎(chǔ)的序列化策略,JacksonJsonRedisSerializer與OxmSerializer都是基于stirng存儲,因此它們本質(zhì)上還是String(最終還是使用string解析以及構(gòu)建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=

#設(shè)置緩存失效時間
com.service.impl.xxxRecordManager= 60
com.service.impl.xxxSetRecordManager= 60
defaultCacheExpireTime=3600

fep.local.cache.capacity =10000

Controller:
要緩存的 Java 對象必須實現(xiàn) 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緩存");
        //從數(shù)據(jù)庫取出
        Object value = "World";
        return value;
    }

    @Override
    @Cacheable(value="testCaching")
    public Object testRedisCachingEntity(String key) {
        System.out.println("沒有用到redis緩存");
        //從數(shù)據(jù)庫取出
        //User必須要繼承Serializable接口
        User u = new User("223","abc");
        return u;
    }
    @Override
    @CachePut(value="testCaching")
    public Object testRedisCachingUpdate(String key) {
        System.out.println("更新redis緩存");
        //更新從數(shù)據(jù)庫取出,放入緩存
        Object value = "New World";
        return value;
    }

    @Override
    @CacheEvict(value="testCaching")
    public Object testRedisCachingDelete(String key) {
        System.out.println("刪除redis緩存");
        //刪除緩存
        return null;
    }
}

上面的方法使用了Spring Cache注解:
@Cacheable主要針對方法配置,能夠根據(jù)方法的請求參數(shù)對其結(jié)果進(jìn)行緩存。

屬性名稱 描述 示例
value 緩存的名稱,在spring配置文件中定義,必須指定至少一個。 @Cacheable(value=”mycache”) 或者 @Cacheable(value={"cache1","cache2"}
key 緩存的 key,可以為空,如果指定要按照SpEL表達(dá)式編寫,如果不指定,則缺省按照方法的所有參數(shù)進(jìn)行組合 @Cacheable(value="testcache”,key="#userName")
condition 緩存的條件,可以為空,使用SpEL編寫,返回true或者false,只有為true才進(jìn)行緩存 @Cacheable(value=”testcache”,condition="#userName.length()>2")

@CachEvict主要針對方法配置,能夠根據(jù)一定的條件對緩存進(jìn)行清空,除了@Cacheable的三種配置參數(shù),另有:

屬性名稱 描述 示例
allEntries 是否清空所有緩存內(nèi)容,缺省為false,如果指定為true,則方法調(diào)用后將立即清空所有緩存 @CachEvict(value=”testcache”,allEntries=true)
beforeInvocation 是否在方法執(zhí)行前就清空,缺省為false,如果指定為true,則在方法還沒有執(zhí)行的時候就清空緩存,缺省情況下,如果方法執(zhí)行拋出異常,則不會清空緩存 @CachEvict(value=”testcache”,beforeInvocation=true)

@CachePut主要針對方法配置,能夠根據(jù)方法的請求參數(shù)對其結(jié)果進(jìn)行緩存,和@Cacheable不同的是,它每次都會觸發(fā)真實方法。他的配置參數(shù)和@Cacheable相同

緩存的 key 生成策略:
對于使用 @Cacheable 注解的方法,每個緩存的key生成策略默認(rèn)使用的是參數(shù)名+參數(shù)值,比如以下方法:

    @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表達(dá)式,比如以下方法:

@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 當(dāng)前方法名 #root.methodName
method 當(dāng)前方法 #root.method.name
target 當(dāng)前被調(diào)用的對象 #root.target
targetClass 當(dāng)前被調(diào)用的對象的class #root.targetClass
params 調(diào)用當(dāng)前方法的參數(shù) #root.params[0]
parameter name 調(diào)用當(dāng)前方法的參數(shù) #p0
caches 當(dāng)前被調(diào)用的方法使用的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,
我們發(fā)現(xiàn)redis中新增加了一個com.zzhblh.service.impl.TestServiceImpltestRedisCachingUpdateHello的key,請求/testrediscachingdelete?key=Hello同理。雖然這是符合myKeyGenerator中key的生成規(guī)則的,但卻不符合實際的需要??梢孕薷腗yKeyGenerator

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

無論使用什么方法,滿足需求即可。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容