博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
SpringBoot | 使用FastJson序列化整合Cache实现Redis缓存
阅读量:4182 次
发布时间:2019-05-26

本文共 15729 字,大约阅读时间需要 52 分钟。

POM依赖

org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-data-redis
org.apache.commons
commons-pool2
2.4.3

配置文件

spring.redis.host=localhostspring.redis.port=6379spring.redis.password=xiaobu1994# 连接超时时间(毫秒)spring.redis.timeout=10000# Redis默认情况下有16个分片,这里配置具体使用的分片,默认是0spring.redis.database=0# 连接池最大连接数(使用负值表示没有限制) 默认 8spring.redis.lettuce.pool.max-active=8# 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1spring.redis.lettuce.pool.max-wait=-1# 连接池中的最大空闲连接 默认 8spring.redis.lettuce.pool.max-idle=8# 连接池中的最小空闲连接 默认 0spring.redis.lettuce.pool.min-idle=0#缓存配置# 一般来说是不用配置的,Spring Cache 会根据依赖的包自行装配 先后顺序 JCache -> EhCache -> Redis -> Guavaspring.cache.type=redis

启动类

package com.xiaobu;import lombok.extern.slf4j.Slf4j;import org.springframework.boot.CommandLineRunner;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cache.annotation.EnableCaching;import org.springframework.scheduling.annotation.EnableAsync;import org.springframework.scheduling.annotation.EnableScheduling;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;import tk.mybatis.spring.annotation.MapperScan;/** * @author xiaobu * @EnableCaching  开启缓存 */@EnableCaching@SpringBootApplication@Slf4jpublic class SsmApplication implements WebMvcConfigurer, CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(SsmApplication.class, args); } @Override public void run(String... args) throws Exception {
log.info("服务启动成功。。。。"); }}
  • opsForValue: 对应 String(字符串)
  • opsForZSet: 对应 ZSet(有序集合)
  • opsForHash: 对应 Hash(哈希)
  • opsForList: 对应 List(列表)
  • opsForSet: 对应 Set(集合)
  • opsForGeo: 对应 GEO(地理位置)

直接使用

用FastJson实现序列化

package com.xiaobu.base.entity;import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.serializer.SerializerFeature;import org.springframework.data.redis.serializer.RedisSerializer;import org.springframework.data.redis.serializer.SerializationException;import java.nio.charset.Charset;/** * @author xiaobu * @version JDK1.8.0_171 * @date on  2019/9/4 17:07 * @description 序列化器 */public class FastJsonRedisSerializer
implements RedisSerializer
{
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); private Class
clazz; public FastJsonRedisSerializer(Class
clazz) {
super(); this.clazz = clazz; } @Override public byte[] serialize(T t) throws SerializationException {
if (t == null) {
return new byte[0]; } return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET); } @Override public T deserialize(byte[] bytes) throws SerializationException {
if (bytes == null || bytes.length <= 0) {
return null; } String str = new String(bytes, DEFAULT_CHARSET); return (T) JSON.parseObject(str, clazz); }}

自定义序列化使用

package com.xiaobu.config;import com.alibaba.fastjson.parser.ParserConfig;import com.xiaobu.base.entity.FastJsonRedisSerializer;import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;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.context.annotation.Primary;import org.springframework.data.redis.cache.RedisCacheConfiguration;import org.springframework.data.redis.cache.RedisCacheManager;import org.springframework.data.redis.cache.RedisCacheWriter;import org.springframework.data.redis.connection.RedisConnectionFactory;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.serializer.RedisSerializationContext;import org.springframework.data.redis.serializer.StringRedisSerializer;import java.time.Duration;/** * @author xiaobu * @version JDK1.8.0_171 * @date on  2019/9/4 19:25 * @description V1.0 */@Configurationpublic class RedisConfig extends CachingConfigurerSupport {
@Bean @Primary//当有多个管理器的时候,必须使用该注解在一个管理器上注释:表示该管理器为默认的管理器 public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
//初始化一个RedisCacheWriter RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory); //序列化方式1 //设置CacheManager的值序列化方式为JdkSerializationRedisSerializer,但其实RedisCacheConfiguration默认就是使用StringRedisSerializer序列化key,JdkSerializationRedisSerializer序列化value,所以以下(4行)注释代码为默认实现// ClassLoader loader = this.getClass().getClassLoader();// JdkSerializationRedisSerializer jdkSerializer = new JdkSerializationRedisSerializer(loader);// RedisSerializationContext.SerializationPair pair = RedisSerializationContext.SerializationPair.fromSerializer(jdkSerializer);// RedisCacheConfiguration defaultCacheConfig=RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair); //序列化方式1---另一种实现方式 //RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig();//该语句相当于序列化方式1 //序列化方式2 //Jackson2JsonRedisSerializer serializer=new Jackson2JsonRedisSerializer(Object.class); //RedisSerializationContext.SerializationPair pair = RedisSerializationContext.SerializationPair.fromSerializer(serializer); //RedisCacheConfiguration defaultCacheConfig=RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair); //序列化方式3 JSONObject FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class); RedisSerializationContext.SerializationPair pair = RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer); RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair); //设置过期时间 30天 defaultCacheConfig = defaultCacheConfig.entryTtl(Duration.ofDays(30)); //初始化RedisCacheManager RedisCacheManager cacheManager = new RedisCacheManager(redisCacheWriter, defaultCacheConfig); //设置白名单---非常重要******** /* 使用fastjson的时候:序列化时将class信息写入,反解析的时候, fastjson默认情况下会开启autoType的检查,相当于一个白名单检查, 如果序列化信息中的类路径不在autoType中, 反解析就会报com.alibaba.fastjson.JSONException: autoType is not support的异常 可参考 https://blog.csdn.net/u012240455/article/details/80538540 */ ParserConfig.getGlobalInstance().addAccept("com.xiaobu.entity."); return cacheManager; } @Bean(name = "redisTemplate") @SuppressWarnings("unchecked") @ConditionalOnMissingBean(name = "redisTemplate") public RedisTemplate
redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate
template = new RedisTemplate<>(); //使用fastjson序列化 FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer(Object.class); // value值的序列化采用fastJsonRedisSerializer template.setValueSerializer(fastJsonRedisSerializer); template.setHashValueSerializer(fastJsonRedisSerializer); // key的序列化采用StringRedisSerializer template.setKeySerializer(new StringRedisSerializer()); template.setHashKeySerializer(new StringRedisSerializer()); template.setConnectionFactory(redisConnectionFactory); return template; }}
package com.xiaobu;import com.xiaobu.entity.City;import com.xiaobu.mapper.CountryMapper;import com.xiaobu.service.CountryService;import lombok.extern.slf4j.Slf4j;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.data.redis.core.ValueOperations;import org.springframework.test.context.junit4.SpringRunner;import java.util.concurrent.TimeUnit;/** * @author xiaobu * @version JDK1.8.0_171 * @date on  2019/9/4 10:40 * @description V1.0  手动使用redis */@RunWith(SpringRunner.class)@SpringBootTest@Slf4jpublic class RedisTest {
@Autowired private CountryMapper countryMapper; @Autowired CountryService countryService; @Autowired private RedisTemplate redisTemplate; @Autowired private StringRedisTemplate stringRedisTemplate; @Test public void test(){
//字符串 stringRedisTemplate.opsForValue().set("name", "小布"); log.info(stringRedisTemplate.opsForValue().get("name")); City city = new City(); city.setName("深圳"); city.setState("广东"); ValueOperations
ops = redisTemplate.opsForValue(); ops.set("city1", city); City city1= (City) redisTemplate.opsForValue().get("city1"); log.info("city1 ==> [{}]", city1); //设置过期 ops.set("tempCity",city,1, TimeUnit.HOURS); City tempCity= (City) redisTemplate.opsForValue().get("tempCity"); log.info("tempCity ==> [{}]", tempCity); }}

查看redis,可以看出已经设置成功.

1567569594(1).jpg

使用Spring Cache自动根据方法生成缓存

  • key: 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合。例如:@Cacheable(value=”testcache”,key=”#id”)

  • value: 缓存的名称,必须指定至少一个。例如:@Cacheable(value=”mycache”) 或者@Cacheable(value={”cache1”,”cache2”}

  • condition: 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存(如:condition ="#id<2",只缓存id<2的;condition=”#userName.length()>2”只缓存名字长度大于2的)

@Cacheable注解会先查询是否已经有缓存,有会使用缓存,没有则会执行方法并缓存。

@CachePut注解的作用 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存,和 @Cacheable 不同的是,它每次都会触发真实方法的调用 。简单来说就是用户更新缓存数据。但需要注意的是该注解的value 和 key 必须与要更新的缓存相同,也就是与@Cacheable 相同。

@CachEvict 的作用 主要针对方法配置,能够根据一定的条件对缓存进行清空 。

@CacheConfig 一个类中可能会有多个缓存操作,而这些缓存操作可能是重复的。这个时候可以使用@CacheConfig @CacheConfig是一个类级别的注解,允许共享缓存的名称、KeyGenerator、CacheManager 和CacheResolver。

该操作会被覆盖。

  • allEntries: 是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存。
  • beforeInvocation: 是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存。

控制层

@RequestParam 和 @PathVariable 注解是用于从request中接收请求的,两个都可以接收参数,关键点不同的是@RequestParam 是从request里面拿取值,而 @PathVariable 是从一个URI模板里面来填充

package com.xiaobu.controller;import com.xiaobu.entity.City;import com.xiaobu.service.CityService;import org.springframework.cache.annotation.CacheEvict;import org.springframework.cache.annotation.CachePut;import org.springframework.cache.annotation.Cacheable;import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;/** * @author xiaobu * @version JDK1.8.0_171 * @date on  2019/9/4 16:25 * @description */@RestController@RequestMapping("/cache")public class CacheController {
@Resource private CityService cityService; /** * 功能描述:只有当id<2是才会缓存 redis的key为 cacheCity::1 http://localhost:8899/cities/getCityByCache/1 * @author xiaobu * @date 2019/7/29 15:12 * @param id id * @return com.xiaobu.entity.City * @version 1.0 */ @Cacheable(value = "cacheCity",key ="#id", condition ="#id<2") @GetMapping("getCityByCache/{id}") public City getCityByCache(@PathVariable Integer id) {
return cityService.getCityByCache(id); } /** * 功能描述:把更新后的放入缓存 key为 cacheCity::1 http://localhost:8899/cities/getCityByCachePut/1 * @author xiaobu * @date 2019/9/4 13:41 * @param id id * @return com.xiaobu.entity.City * @version 1.0 */ @CachePut(value = "cacheCity",key = "#id") @GetMapping("getCityByCachePut/{id}") public City getCityByCachePut(@PathVariable Integer id) {
return cityService.getCityByCachePut(id); } /** * 功能描述:清除 key为 cacheCity::1的缓存数据 http://localhost:8899/cities/getCityByNoCache/1/xiaobu * @author xiaobu * @date 2019/7/29 15:11 * @param id, name] * @return com.xiaobu.entity.City * @version 1.0 */ @CacheEvict(value = "cacheCity",key = "#id") @GetMapping(value = "getCityByNoCache/{id}") public City getCityByNoCache(@PathVariable Integer id, @RequestParam String name) {
System.out.println("id = " + id+",name="+name); return cityService.getCityByNoCache(id); }}

服务层:

/* * The MIT License (MIT) * * Copyright (c) 2014-2016 abel533@gmail.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */package com.xiaobu.service;import com.github.pagehelper.PageHelper;import com.xiaobu.entity.City;import com.xiaobu.mapper.CityMapper;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.stereotype.Service;import java.util.List;/** * @author xiaobu * @since 2019-09-04 11:09 */@Servicepublic class CityService {
@Autowired private CityMapper cityMapper; public List
getAll(City city) {
if (city.getPage() != null && city.getRows() != null) {
PageHelper.startPage(city.getPage(), city.getRows()); } return cityMapper.selectAll(); } public City getById(Integer id) {
return cityMapper.selectByPrimaryKey(id); } public void deleteById(Integer id) {
cityMapper.deleteByPrimaryKey(id); } public void save(City country) {
if (country.getId() != null) {
cityMapper.updateByPrimaryKey(country); } else {
cityMapper.insert(country); } } @Cacheable(value = "cacheCity",key ="#id", condition ="#id<2") public City getCityByCache(Integer id){
System.out.println("getCityByCache 执行查询。。。。"); return cityMapper.selectByPrimaryKey(id); } @CachePut(value = "cacheCity",key = "#id") public City getCityByCachePut(Integer id){
System.out.println("getCityByCachePut 执行查询。。。。"); City city= cityMapper.selectByPrimaryKey(id); city.setState("广东"); cityMapper.updateByPrimaryKey(city); return city; } @CacheEvict(value = "cacheCity",key = "#id") public City getCityByNoCache(Integer id){
System.out.println("getCityByNoCache 执行查询。。。。"); return cityMapper.selectByPrimaryKey(id); }}

访问 http://localhost:8899/cache/getCityByCache/1 第一次会去数据库查,第二次则直接在缓存里面查找。

1567658275(1).jpg

访问 http://localhost:8899/cache/getCityByCachePut/1 两次都会去数据库查

1567653676(1).jpg

先访问 http://localhost:8899/cache/getCityByNoCache/1/xiaobu 会发现缓存被清除了,数据是从数据库重新查的.

1567653952(1).jpg

参考:

你可能感兴趣的文章
预编译防止sql注入
查看>>
覆盖equals方法时总是要覆盖hashCode
查看>>
clone详解
查看>>
【Java并发编程实战】——AbstractQueuedSynchronizer源码分析(一)
查看>>
【Java并发编程实战】——并发编程基础
查看>>
【Java并发编程实战】——Java内存模型与线程
查看>>
Java复制文件的4种方式
查看>>
mysql的JDBC连接工具类
查看>>
利用多线程(用到原子类AtomicInteger)往数据库批量插入大量数据
查看>>
多个线程操作数组
查看>>
定长线程池的应用
查看>>
ArrayBlockingQueue的简单使用
查看>>
Git 常用命令总结(一)
查看>>
Git 常用命令总结(二)
查看>>
JAVA 并发——synchronized的分析
查看>>
Echarts——使用 dataset 管理数据
查看>>
DES 加解密工具类
查看>>
SpringBoot多模块项目实践(Multi-Module)
查看>>
第一篇: 服务的注册与发现Eureka(Greenwich版)
查看>>
第二篇: 服务消费者(rest+ribbon)(Greenwich版本)
查看>>