springboot redis缓存功能的初步了解和使用

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-redis</artifactId>
			<version>1.4.3.RELEASE</version>
		</dependency>

redis的安装就不说了,在springboot中导入这两个包。

在application入口加上这个注解@EnableCaching

@SpringBootApplication
@ServletComponentScan
@RestController
@EnableCaching
public class Application {

配置redis缓存,说实话是在网上找的。

package com.kq.highnet2;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;


/**
 * Redis缓存配置类
 * @author szekinwin
 *
 */
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport{

    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.port}")
    private int port;
    @Value("${spring.redis.timeout}")
    private int timeout;
    
    //自定义缓存key生成策略
//    @Bean
//    public KeyGenerator keyGenerator() {
//        return new KeyGenerator(){
//            @Override
//            public Object generate(Object target, java.lang.reflect.Method method, Object... params) {
//                StringBuffer sb = new StringBuffer();
//                sb.append(target.getClass().getName());
//                sb.append(method.getName());
//                for(Object obj:params){
//                    sb.append(obj.toString());
//                }
//                return sb.toString();
//            }
//        };
//    }
    //缓存管理器
    @Bean 
    public CacheManager cacheManager(@SuppressWarnings("rawtypes") RedisTemplate redisTemplate) {
        RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
        //设置缓存过期时间 
        cacheManager.setDefaultExpiration(10000);
        return cacheManager;
    }
    @Bean
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory){
        StringRedisTemplate template = new StringRedisTemplate(factory);
        setSerializer(template);//设置序列化工具
        template.afterPropertiesSet();
        return template;
    }
     private void setSerializer(StringRedisTemplate template){
            @SuppressWarnings({ "rawtypes", "unchecked" })
            Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
            ObjectMapper om = new ObjectMapper();
            om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
            jackson2JsonRedisSerializer.setObjectMapper(om);
            template.setValueSerializer(jackson2JsonRedisSerializer);
     }
}

然后从网上下载redis desktop manager,可以查看redis


这个还是很有必要的,这样就很好地了解缓存的具体位置,顺便一提,已经使用了spring-session,作为分布式服务器的通用session


下面就是具体的使用了,总共有五个注解@Cacheable、@CacheEvict、@CachePut、@Caching、@CacheConfig

因为此类内容网上一大堆,我也就不搬运了,就说一下我个人的使用心得和感受吧,这些标签都有通用值,重要的有value和key

@Cacheable(value="baseUserModel",key="#p0")
BaseUserModel findByUserId(String baseUserId);

结合数据库管理器就很清楚了


把数据保存到了baseUserModel里面,并且以方法的参数为key。就是说缓存是以方法的参数作为key值而不能以返回值的某一项(比如id)作为key值,保存缓存和删除缓存同样如此。实际上这样局限性很大,因为很多方法并不是用id作为参数的,而是通过多项条件做为参数的。

缓存最重要的一点就是你必须可以修改缓存或者删除它,这种时机的把握很重要,如果只能用方法的参数作为key键,而更新操作一般使用的参数与查询不同,所以实际上最适用的范围还是使用id查询单个对象,我想到的另一种方法就是专门创建删除缓存的方法,专门传入查询的参数,加上@CachePut或@CacheEvict注解。比如:

		@CacheEvict(value="userList",key= "#account.concat(#gender)")
		void deleteCache(String account, String gender);
或者
	@CachePut(value="baseUserModel",key="#baseUser.userId")
	public BaseUserModel updateCache(BaseUserModel baseUser) {
		return baseUser;
	}

要注意的是@CachePut需要返回值,不然就会删除缓存,虽然也没什么关系就是了。

我有个业务需要从好几个不同的表里拿出数据拼接起来,所以cache注解需要加载dao层上,然后发现一个问题

@Cacheable(value="baseDepartmentModel",key="#departmentId")
在接口上使用 #参数名 的形式是不行的,它只会拿到null,而是要使用
@Cacheable(value="baseDepartmentModel",key="#p0")
@Cacheable(value="baseDepartmentModel",key="#root.args[0].userId")

通过参数的位置去拿。

又发现一个问题我有一个用户表BaseUserModel和雇员表BaseEmployeeModel,雇员表直接使用的是用户表的id,结果当我直接使用

    @Cacheable(value="baseEmployeeModel",key="#p0")
	BaseEmployeeModel findByEmployeeId(String baseUserId);

竟然报转化错误了

java.lang.ClassCastException: com.kq.highnet2.framework.base.baseUser.model.BaseUserModel cannot be cast to com.kq.highnet2.framework.base.baseUser.model.BaseEmployeeModel
	at com.sun.proxy.$Proxy166.findByEmployeeId(Unknown Source)
	at com.kq.highnet2.framework.base.baseUser.service.impl.BaseUserMainServiceImpl.getUserById(BaseUserMainServiceImpl.java:706)
	at com.kq.highnet2.framework.base.baseUser.service.impl.BaseUserMainServiceImpl$$FastClassBySpringCGLIB$$8709dccc.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:738)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
	at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282)
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)

也就是说redis在查找缓存时,不是先通过value找到对应位置再通过key来获得对象,而是直接通过key来找到对象


那么注解的value值实际上并没有什么意义,最多起到一个管理key值的作用,那么如果不同对象拥有相同key值的情况怎么办呢?在前面加上专门的前缀就行了,实际上在写html给id取名时也是如此,加上足够复杂的前缀或后缀防止重复

    @Cacheable(value="baseEmployeeModel",key="'employee'+#p0")
	BaseEmployeeModel findByEmployeeId(String baseUserId);


之前主管让我不要使用关联查询的语句,现在的用意我也知道了,当使用缓存的时候,直接保存关联查询的结果,那么一旦某个类更新了,则需要删除所有关联查询语句的缓存。

比如,我的计划单,任务单,都有产品的id,之前使用关联查询拿到产品的名称,一旦产品改变名字了,如果我使用缓存,那么所有的缓存都要删除,重新查询。但是如果我吧每个对象独立保存,使用的时候根据id查询出产品,然后赋值给相应的字段,那么我更新产品时也只要更新产品的缓存就行了。虽然局限性还是很大的

猜你喜欢

转载自blog.csdn.net/qq_36804701/article/details/80570752