大三已经结束了,虽然现在面临就业,我还是放下焦虑慢慢补充自己的知识面,实习工作也在不停地面试不停地投简历,不求offer直接飞过来,在这一步一步的找工作的过程中我自己也在不断地进步。

这波我们处理的是redis作为缓存时默认jdk序列化数据的问题,虽然在前端后者后端读取时是利用反序列化机制将数据反转成之前的格式,但是进入redis数据库中查看时却发现是X86X23X76等统一类型的数据格式,这对于维护是一个大障碍。

之前有写过配置Json序列化的配置Bean文章,这一次主要也想统一的把缓存记录一下。

缓存:在Cache机制里,每一块缓存都可以被定义,也可以被分配给固定的组件,但是一整快缓存是被不同的划分出来的manager管理着的,比如这块缓存是redis,另一块是encache等等。

在一块划分好的redis组件的缓存下还可以划分成块,比如存储user的user块,存储book的book块等等。

下面来看一下代码:

四个关于缓存操作的注解如下(代码段里有注释)


/**@CacheConfig注解抽取缓存的公共配置
 *  cacheNames是指定这一块缓存的,当然这个配置完毕后类里的相关注解配置缓存归属就不需要了。
 *  keyGenerator是指定规定的key生成器(那么是否能在其中设置我们的key的过期时间呢?)。
 */
@CacheConfig(cacheNames = "user")
@Service("User")
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao;

    @Override
    /*可使用缓存*/
    /** @Cacheable注解(三个注解基本一致,执行机制是方法的前置处理,执行查询方法之前
     *   先根据id去缓存查找对应的key,找不到就等方法执行结束之后将结果存入缓存)
     *   value:这一块缓存的名称
     *   key:指定存储的键值对的名称默认是参数列表第一个参数,值是注解下方法的返回值
     *   condition:指定符合什么条件才去走缓存
     *   unless:当满足其中的条件时拒绝缓存
     *   sync:是否使用异步模式
     * */
    @Cacheable(cacheNames = {"uer"}, key = "#root.args[0]", condition = "#id>0")
    public User query(int id) {
        /**
         * 走mysql数据库才会去输出这个语句
         */
        System.out.println("查询" + id + "号用户");
        User user = userDao.query(id);
        return user;
    }

    @Override
    /**@Caching复合注解,多缓存操作
     *  当我们的查询操作不是以主要的id为主属性的时候我们在缓存存储的
     *  键值对类型的key又是以id为主的,这个时候@Cacheable就不能拿来
     *  存储正常的键值对,就需要put的后置处理特性来帮我们用查询后的结果
     *  对应的id来存储键值对。
     *  但是也有一个弊端就是同时存储两个键值对,就会浪费一定的缓存空间
     */
    @Caching(
            cacheable = {
                    @Cacheable(cacheNames = "user", key = "#name")
            },
            put = {
                    @CachePut(cacheNames = "user", key = "#result.id")
            }
            /**
            evict = {
            @CacheEvict(cacheNames = "user", key = "#result.id")
            }
            */
    )
    public User queryByName(String name) {
        return userDao.queryByName(name);
    }


    @Override
    /**@CachePut注解
     *   这里的key如果不指定的话按照存入缓存的默认规则
     *   key默认的值是参数列表的第一个参数,可能就是user了
     *   这里的key如果改为result也是对的(原因就是这个@Cacheput
     *   注解的执行顺序与方法关系是后置处理,而Cacheable是前置处理)
     * */
    @CachePut(cacheNames = {"user"}, key = "#user.id", unless = "#result==null")
    public User save(User user) {
        return userDao.save(user);
    }

    @Override
    /**@CacheEvict注解
     *   allEntries = true 这个属性的意义就是在进行清空操作的时候是否删除全部的key
     *   beforeInvocation=true 这个属性的意义就是清除缓存在方法执行之前
     * */
    @CacheEvict(cacheNames = {"user"}, key = "#id", allEntries = false, beforeInvocation = false)
    public void delete(int id) {
        userDao.delete(id);
    }
}

这样的不配置序列化的情况下会出现第一个错误:

对象没有序列化:

序列化就是直接实现这个接口Serializble:

import java.io.Serializable;


@Data
public class User implements Serializable {
    private int id;
    private String name;
    private int age;
}

这样配置完之后可以正常的运行与测试,但是打开redis查看后台缓存数据时会出现第二个问题,就是序列化的数据不能直接辨认(不是Json类型):

自定义第三方bean去帮我们做到这些:

/**问题:目前缓存管理自动更改为redisCacheManager缓存管理器,
 * 应用的redisCache是默认的RedisTemplate模板采用的数据序列化依然是Jdk的内置序列化机制。
 * 因此存入缓存的(User)数据格式都是jdk默认的序列化格式
 *
 */
/*自定义的RedisTemplate模板配置实现键值的json数据序列化*/
@Configuration
public class Redis_config {

    /**自定义RedisTemplate的数据类型
     *  主要配置的就是序列化机制
     *  更改它的序列化器为Json格式的序列化机制
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory){

        /*定义方法的返回值,泛型自动匹配,后面的可以省略*/
        RedisTemplate<String,Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);

        /*1.创建Jackson工具对象*/
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        /*2.为创建的工具对象设置Object映射*/
        jackson2JsonRedisSerializer.setObjectMapper(om);

        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.setHashValueSerializer(jackson2JsonRedisSerializer);

        /*将key与hashKey设置为String的序列化方式*/
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        template.setKeySerializer(stringRedisSerializer);
        template.setHashKeySerializer(stringRedisSerializer);

        template.afterPropertiesSet();
        return template;
    }
    /**自定义RedisCacheManager来实现缓存管理器
     *  RedisTemplate采用我们自己配置的Bean组件
     */
    @Bean
    public RedisCacheManager UserRedisCacheManager(RedisConnectionFactory redisConnectionFactory){
        /**对于新版本的redis而言,它的RedisCacheManager创建与旧版相比发生了一定的变化。
         *  这里定制了这个序列化格式:
         *  serializeKeysWith(RedisSerializationContext.
         *      SerializationPair.fromSerializer(new StringRedisSerializer()))
         *  serializeValuesWith(RedisSerializationContext.
         *      SerializationPair.fromSerializer(new Jackson2JsonRedisSerializer<>(User.class)));
         *  这里则是设置生成的键值对的过期时间(作为缓存确实得有过期时间)
         *  entryTtl(Duration.ofSeconds(120))
         *  这里是禁止插入空值的键值对
         *  disableCachingNullValues()
         */
        RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(120))
                .disableCachingNullValues()
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new Jackson2JsonRedisSerializer<>(Object.class)));

        return RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(cacheConfiguration).build();

    }
}

这些都配置完毕了之后就不会再有数据格式问题了;

附上:

userDao:

@Mapper
public interface UserDao {

    @Select("select * from test.user where id = #{id}")
    User query(int id);

    @Select("select * from test.user where name = #{name}")
    User queryByName(String name);

    @Insert("insert into test.user values (#{name},#{age},#{id})")
    User save(User user);

    @Delete("delete from test.user where id = #{id}")
    void delete(int id);
}

部分依赖:

  <!--配置文件注解组成-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <!--redis(针对业务层用cache实现热共享)-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.27</version>
        </dependency>
        <!--jedis依赖-->
        <!--<dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
            <version>2.11.1</version>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐