概述

- 采用springboot2.2.4.RELEASE版本,采用lettuce对redis进行整合,并且使用mybatisPlus实战- - 源码地址:https://github.com/Blithe0/CmsServer-master.git

一、添加相关依赖

// An highlighted block<!-- redis --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>2.3.3</version></dependency>

二、编辑redis和MP的yml文件

# redis配置
springcache:type: redisredis:time-to-live: 20000 #缓存超时时间mscache-null-values: false #是否缓存空值redis:port: 6379host: localhost  # 127.0.0.1password: 123456 # Redis 服务器连接密码(默认为空)lettuce:pool:max-active: 8 # 连接池最大连接数(使用负值表示没有限制)max-wait: -1  # 连接池最大阻塞等待时间(使用负值表示没有限制)max-idle: 8   # 连接池中的最大空闲连接min-idle: 0   # 连接池中的最小空闲连接timeout: 10000  #redis 连接超时时间msdatabase: 0 # 数据库索引,默认是0
# MP配置
mybatis-plus:##主键类型  0:"数据库ID自增", 1:"用户输入ID",2:"全局唯一ID (数字类型唯一ID)", 3:"全局唯一ID UUID";#mybatis-plus.global-config.id-type=3##字段策略 0:"忽略判断",1:"非 NULL 判断"),2:"非空判断"#mybatis-plus.global-config.field-strategy=2#驼峰下划线转换global-config:db-column-underline: true#mp2.3+ 全局表前缀 mp_#mybatis-plus.global.table-prefix: mp_#刷新mapper 调试神器refresh-mapper: true#数据库大写下划线转换capital-mode: true# Sequence序列接口实现类配置#mybatis-plus.global-config.key-generator=com.baomidou.mybatisplus.incrementer.OracleKeyGenerator#逻辑删除配置(下面3个配置)logic-delete-value: 1logic-not-delete-value: 0#mybatis-plus.global-config.sql-injector=com.baomidou.springboot.MyMetaObjectHandler#配置返回数据库(column下划线命名&&返回java实体是驼峰命名),自动匹配无需as(没开启这个,SQL需要写as: select user_id as userId)configuration:map-underscore-to-camel-case: truecache-enabled: falselog-impl: org.apache.ibatis.logging.stdout.StdOutImpl#控制台是否显示sql#mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl  mapper-locations: classpath:mapper/*/*.xml

三、编写config类

@Configuration
//继承CachingConfigurerSupport,为了自定义生成KEY的策略。可以不继承。
public class RedisConfiguration extends CachingConfigurerSupport {/*** SpringBoot 2.X 缓存管理器配置** @param redisConnectionFactory* @return*/@Beanpublic CacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) {// 使用json方式序列化,在实体类中可以不进行序列化了RedisSerializer<String> redisSerializer = new StringRedisSerializer();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);
//        CacheKeyPrefix keyPrefix = new CacheKeyPrefix() {//            @Override
//            public String compute(String cacheName) {//                return cacheName + "::";
//            }
//        };// 生成一个默认配置,通过config对象即可对缓存进行自定义配置   配置序列化(解决乱码的问题)RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(0)) // 默认缓存时间.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer)) // 设置key的序列化方式.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)) // 设置value的序列化方式.disableCachingNullValues(); // 不缓存null值// 对每个缓存空间应用不同的配置Map<String, RedisCacheConfiguration> configMap = new HashMap<>();configMap.put("my-redis-cache1", config.entryTtl(Duration.ofHours(30L)));   // my-redis-cache1过期时间为30分钟configMap.put("my-redis-cache2", config.entryTtl(Duration.ofSeconds(120))); // my-redis-cache2过期时间120s// 使用自定义的缓存配置初始化一个cacheManagerreturn RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(config) // 默认缓存.withInitialCacheConfigurations(configMap) // 特殊缓存.transactionAware() // 事务.build();}/*** 自定义key. 此方法将会根据类名+方法名+所有参数的值生成唯一的一个key,即使@Cacheable中的value属性一样,key也会不一样。*/@Overridepublic KeyGenerator keyGenerator() {return new KeyGenerator() {@Overridepublic 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();}};}/*** redisTemplate 自定义序列化* @param redisConnectionFactory* @return*/@Beanpublic RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();redisTemplate.setConnectionFactory(redisConnectionFactory);//使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);ObjectMapper objectMapper = new ObjectMapper();objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);jackson2JsonRedisSerializer.setObjectMapper(objectMapper);//使用StringRedisSerializer来序列化和反序列化redis的key值redisTemplate.setKeySerializer(new StringRedisSerializer());
//      redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);// value序列化方式采用jacksonredisTemplate.setValueSerializer(new RedisObjectSerializer());// hash的key采用String的序列化方式redisTemplate.setHashKeySerializer(new StringRedisSerializer());
//      redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);// value序列化方式采用jacksonredisTemplate.setHashValueSerializer(new RedisObjectSerializer());redisTemplate.afterPropertiesSet();return redisTemplate;}
}

四、编写对象序列化工具类(更友好)

public class RedisObjectSerializer implements RedisSerializer<Object> {private Converter<Object, byte[]> serializer = new SerializingConverter();private Converter<byte[], Object> deserializer = new DeserializingConverter();static final byte[] EMPTY_ARRAY = new byte[0];public Object deserialize(byte[] bytes) {if (isEmpty(bytes)) {return null;}try {return deserializer.convert(bytes);} catch (Exception ex) {throw new SerializationException("Cannot deserialize", ex);}}public byte[] serialize(Object object) {if (object == null) {return EMPTY_ARRAY;}try {return serializer.convert(object);} catch (Exception ex) {return EMPTY_ARRAY;}}private boolean isEmpty(byte[] data) {return (data == null || data.length == 0);}
}

五、编写实体类:

 // BaseVo
@EqualsAndHashCode(callSuper=false)
@Data
public abstract class BaseVO implements java.io.Serializable{private static final long serialVersionUID = 1L;@TableField(exist = false)private Integer page;@TableField(exist = false)private Integer limit;/*** 接收多个ID*/@TableField(exist = false)private Integer[] ids;@TableField(exist = false)@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")private Date startTime;@TableField(exist = false)@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")private Date endTime;
}

六、单元测试:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = SpringBootRedisApplicationTests.class)
@ComponentScan("com.blithe.cms")
public class SpringBootRedisApplicationTests {// 注入spring为我们提供的接口@Autowiredprivate RedisTemplate redisTemplate;  @Testpublic void testObj(){Loginfo loginfo = new Loginfo();loginfo.setLoginname("小剑");loginfo.setLogintime(new Date());loginfo.setLoginip(IpUtil.getIpAddr(HttpContextUtils.getHttpServletRequest()));redisTemplate.opsForValue().set("loginInfo_",loginfo);/*** 在上面这个例子中我们使用redisTemplate调用了opsForValue会得到一个ValueOperations操作。* 这个是专门操作String类型的数据,所以里面的键值对中键为String,而值是我们的User。* 当然redisTemplate还为我们提供了其他几个,也就是redis为我们提供的数据类型*/
//        Boolean exists = (Boolean) redisTemplate.opsForValue().get("loginInfo_");
//        System.out.println("redis是否存在key" + exists);Loginfo loginInfo_ = (Loginfo)  redisTemplate.opsForValue().get("loginInfo_");System.out.println("从redis取出对象" + loginInfo_.toString());}
}

控制台打印:

此时看下redis数据: 测试没什么问题,但是为什么出来的结果是默认的IO序列化的数据呢?不是编写了自定义JSON序列化吗?这块测试我们只是拿到了spring为我们提供的redisTemplate。

七、编写RedisCompoent组件,更方便的使用redis,这里贴出一部分代码,可自行GitHub下载

八、业务开发使用

@Service
public class DeptServiceImpl extends ServiceImpl<DeptMapper, Dept> implements DeptService {@Autowiredprivate DeptMapper deptMapper;@Autowiredprivate RedisCompoent redisCompoent;@Override@Cacheable(value = {"dept"},key = "'selectById' + #p0",sync=true)public Dept selectById(Serializable id) {return deptMapper.selectById(id);}/***  延时双删策略*         在写库前后都进行redis.del(key)操作,并且设定合理的超时时间。具体步骤是:**         1)先删除缓存**         2)再写数据库**         3)休眠500毫秒(根据具体的业务时间来定)**         4)再次删除缓存。** @param dept* @return*/@Override@Cacheable(value = {"dept"},key = "'updateById_' + #p0.id",sync=true)public boolean updateById(Dept dept) {//      redisCompoent.remove("updateById_" + dept.getId());
//      boolean editFlag = super.updateById(dept);
//      try {//          Thread.sleep( 500 );
//      } catch (InterruptedException e) {//          e.printStackTrace();
//      }
//      redisCompoent.remove("updateById_" + dept.getId());return super.updateById(dept);}@Override@CacheEvict(value = {"dept"},allEntries = true)public boolean deleteById(Serializable id) {return super.deleteById(id);}
}

九、编写controller:

@RestController
@RequestMapping("/dept")
public class DeptController {@Autowiredprivate DeptService deptService;/*** 保存* @param dept* @return*/@PostMapping("/deptSaveOrUpdate")public R deptSaveOrUpdate(Dept dept){try {if(dept.getId() == null){dept.setCreatetime(new Date());this.deptService.insert(dept);}else{this.deptService.updateById(dept);}}catch (Exception e){return R.error(e.getMessage());}return R.ok();}@RequestMapping("/selectById")public R findById(Integer id){Dept dept = this.deptService.selectById(id);return R.ok().put("dept",dept);}
}

十、跑起来

// biubiu测试接口返回数据,看下redis中,也都没问题了。其实在此笔者也踩了很多坑,遇到挺多问题和疑惑的?下面列举一些问题和疑惑?
  • 在整合测试的时候报这个错误
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'springBootRedisApplicationTests': Unsatisfied dependency expressed through field 'redisUtil'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.blithe.cms.common.tools.RedisUtil' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:643) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:116) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1422) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:594) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:879) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:878) ~[spring-context-5.2.3.RELEASE.jar:5.2.3.RELEASE]at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550) ~[spring-context-5.2.3.RELEASE.jar:5.2.3.RELEASE]at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:747) ~[spring-boot-2.2.4.RELEASE.jar:2.2.4.RELEASE]at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) ~[spring-boot-2.2.4.RELEASE.jar:2.2.4.RELEASE]at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[spring-boot-2.2.4.RELEASE.jar:2.2.4.RELEASE]at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:126) ~[spring-boot-test-2.2.4.RELEASE.jar:2.2.4.RELEASE]at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99) ~[spring-test-5.2.3.RELEASE.jar:5.2.3.RELEASE]at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124) ~[spring-test-5.2.3.RELEASE.jar:5.2.3.RELEASE]... 24 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.blithe.cms.common.tools.RedisUtil' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1695) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1253) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1207) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:640) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:116) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1422) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:594) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:879) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:878) ~[spring-context-5.2.3.RELEASE.jar:5.2.3.RELEASE]at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550) ~[spring-context-5.2.3.RELEASE.jar:5.2.3.RELEASE]at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:747) ~[spring-boot-2.2.4.RELEASE.jar:2.2.4.RELEASE]at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) ~[spring-boot-2.2.4.RELEASE.jar:2.2.4.RELEASE]at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[spring-boot-2.2.4.RELEASE.jar:2.2.4.RELEASE]at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:126) ~[spring-boot-test-2.2.4.RELEASE.jar:2.2.4.RELEASE]at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99) ~[spring-test-5.2.3.RELEASE.jar:5.2.3.RELEASE]at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124) ~[spring-test-5.2.3.RELEASE.jar:5.2.3.RELEASE]

其实这个问题就是在注入redisTemplate的bean的时候出现问题,在注入时候直接使用自动注入方式@Autowired时,会出现问题,因为我们自己编写的redisTemplate这个RedisTemplate<String,Object>带有泛型,而@Autowired注入是通过类型来进行注入的,这时候可以采用@Resource注解,它是根据名字来进行注入的。

  • 几个问题?
    1、lettuce与jedis、Redisson区别?
概念:Jedis:是Redis的Java实现客户端,提供了比较全面的Redis命令的支持,Redisson:实现了分布式和可扩展的Java数据结构。Lettuce:高级Redis客户端,用于线程安全同步,异步和响应使用,支持集群,Sentinel,管道和编码器。优点:Jedis:比较全面的提供了Redis的操作特性Redisson:促使使用者对Redis的关注分离,提供很多分布式相关操作服务,例如,分布式锁,分布式集合,可通过Redis支持延迟队列Lettuce:主要在一些分布式缓存框架上使用比较多可伸缩:Jedis:使用阻塞的I/O,且其方法调用都是同步的,程序流需要等到sockets处理完I/O才能执行,不支持异步。Jedis客户端实例不是线程安全的,所以需要通过连接池来使用Jedis。Redisson:基于Netty框架的事件驱动的通信层,其方法调用是异步的。Redisson的API是线程安全的,所以可以操作单个Redisson连接来完成各种操作Lettuce:基于Netty框架的事件驱动的通信层,其方法调用是异步的。Lettuce的API是线程安全的,所以可以操作单个Lettuce连接来完成各种操作结论:建议使用:Jedis + Redisson,个人喜欢使用Lettuce和Jedis 。

2、为什么要自定义RedisTemplate?

其实通过源码可以看出,SpringBoot自动帮我们在容器中生成了一个RedisTemplate和一个StringRedisTemplate。RedisTemplate的泛型是<Object,Object>,写代码不是很方便,一般我们的key是String类型,我们需要一个<String,Object>的泛型。RedisTemplate没有设置数据存储在Redis时,Key和Value的序列化方式。(采用默认的JDK序列化方式)

3、针对StringRedisSerializer,Jackson2JsonRedisSerializer和JdkSerializationRedisSerializer进行测试?

StringRedisSerializer进行序列化后的值,在Java和Redis中保存的内容时一致的。用Jackson2JsonRedisSerializer序列化后,在Redis中保存的内容,比Java中多一对逗号。用JdkSerializationRedisSerializer序列化后,对于Key-Value结构来说,在Redis中不可读;对于Hash的Value来说,比Java的内容多了一些字符。

4、注解式的Redis处理(基于注解的支持)

Spring 提供注解来支持 Spring Cache。其核心主要是 @Cacheable 和 @CacheEvict。
使用 @Cacheable标记的方法在执行后 Spring Cache 将缓存其返回结果,而使用 @CacheEvict标记的方法会在方法执行前或者执行后移除 Spring Cache中的某些元素说白了:注解式的使用就是在方法上面加上Cacheable / CacheEvict / CachePut的注解
*注解支持使用EL表达式 这里就是支持使用相关的参数和属性来表示1> @Cacheable :
@Cacheable 注解 源码如下 :
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Cacheable {@AliasFor("cacheNames")String[] value() default {};@AliasFor("value")String[] cacheNames() default {};String key() default "";String keyGenerator() default "";String cacheManager() default "";String cacheResolver() default "";String condition() default "";String unless() default "";boolean sync() default false;
}
value、cacheNames 属性 : 指定 Cache名称 表示当前方法的返回值是会被缓存在哪个 Cache上,对应 Cache 名称,名称可以是一个 Cache 也可以是多个 Cache,当需要缓存在多个 Cache 上时其是一个数组key 属性 : 用来指定 Spring缓存方法的返回结果时对应的 ke。该属性支持 SpringEL表达式,当没有指定该属性时,Spring 将使用默认策略生成 key。先来看看自定义策略,自定义策略是指可以通过 SpringEL 表达式来指定 key。这里的 EL表达式可以使用方法参数及对应的属性,使用方法参数时可以直接使用 "#参数名" 或者 "#p参数index"
@Cacheable(value = "users", key = "#id")
public User find(Integer id) {return null;
}
@Cacheable(value = "users", key = "#p0")
public User find(Integer id) {return null;
}
@Cacheable(value = "users", key = "#user.id")
public User find(User user) {return null;
}
@Cacheable(value = "users", key = "#p0.id")
public User find(User user) {return null;
}
@Cacheable(value = "UserCache")
public boolean isReserved(String userId) {System.out.println("UserCache:" + userId);return false;
}
@Cacheable(value = "UserCache", key = "'user:' + #userId")
public void removeUser(String userId) {System.out.println("UserCache remove:" + userId);
}除上述使用方法参数作为 key之外,Spring 还提供了一个 root对象可以用来生成key,通过该 root对象可以获取到以下信息
属性名称:
methodName  当前方法名   #root.methodName
method  当前方法    #root.method.name
target  当前被调用的对象    #root.target
targetClass 当前被调用的对象的 class #root.targetClass
args    当前方法参数组成的数组 #root.args[0]
caches  当前被调用的方法使用的 Cache   #root.caches[0].name
示例使用:*  #root.targetClass 是类名*  #p0是第一个参数值* #p1是第二个参数值* @Cacheable(value = "test", key = "#root.targetClass + '_' + #p0 + '_' + #p1")sync属性 : 在多线程环境下,某些操作可能使用相同参数同步调用。默认情况下,缓存不锁定任何资源,可能导致多次计算,而违反了缓存的目的。对于这些特定的情况,属性 sync 可以指示底层将缓存锁住,使只有一个线程可以进入计算,而其他线程堵塞,直到返回结果更新到缓存中
@Cacheable(cacheNames="foos", sync="true")
public Foo executeExpensiveOperation(String id) {
}condition属性 : 指定发生的条件,condition属性默认为空,表示将缓存所有的调用情形。其值是通过 SpringEL表达式来指定的,当为true时表示进行缓存处理;当为 false 时表示不进行缓存处理,即每次调用该方法时该方法都会执行一次
@Cacheable(value={"users"}, key="#user.id", condition="#user.id%2==0")
public User find(User user) {System.out.println("find user by user " + user);return user;
}还有一个 unless 属性可以用来是决定是否添加到缓存,与 condition不同的是,unless表达式是在方法调用之后进行评估的。如果返回false,才放入缓存 (与condition相反),#result指返回值
@Cacheable(cacheNames="book", condition="#name.length < 32", unless="#result.name.length > 5")
public Book findBook(String name)2> @CachePut : 在支持 Spring Cache 的环境下,对于使用 @Cacheable标注的方法,Spring 在每次执行前都会检查 Cache 中是否存在相同 key 缓存元素,如果存在就不再执行该方法,而是直接从缓存中获取结果进行返回,否则才会执行并将返回结果存入指定的缓存中。@CachePut也可以声明一个方法支持缓存功能。与@Cacheable不同的是使用 @CachePut标注的方法在执行前不会去检查缓存中是否存在之前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中。@CachePut 也可以标注在类上和方法上,使用 @CachePut 时可以指定的属性跟 @Cacheable是一样的
@CachePut("users") // 每次都会执行方法,并将结果存入指定的缓存中
public User find(Integer id) {return null;
}3> @CacheEvict : 用来标注在需要清除缓存元素的方法或类上。当标记在一个类上时表示其中所有的方法的执行都会触发缓存的清除操作。@CacheEvict 可以指定的属性有 value、key、condition、allEntries 和 beforeInvocation。其中 value、key 和 condition 的语义与 @Cacheable对应的属性类似。即 value表示清除操作是发生在哪些 Cache 上的 (对应 Cache的名称);key表示需要清除的是哪个 key,如未指定则会使用默认策略生成的key;condition表示清除操作发生的条件。另外 @CacheEvict 还有另外两个属性
allEntries属性 : 是 boolean类型,表示是否需要清除缓存中的所有元素。默认为false,表示不需要。当指定 allEntries为true时,Spring Cache将忽略指定的key 并把缓存信息全部删除
@CacheEvict(value="users", allEntries=true)
public void delete(Integer id) {System.out.println("delete user by id: " + id);
}beforeInvocation属性 : 清除操作默认是在对应方法成功执行之后触发的,即方法如果因为抛出异常而未能成功返回时也不会触发清除操作。使用 beforeInvocation可以改变触发清除操作的时间,当指定该属性值为 true时,Spring会在调用该方法之前清除缓存中的指定元素
@CacheEvict(value="users", beforeInvocation=true)
public void delete(Integer id) {System.out.println("delete user by id: " + id);
}
其实除了使用 @CacheEvict清除缓存元素外,当使用 Ehcache作为实现时,也可以配置 Ehcache自身的驱除策略,其是通过 Ehcache的配置文件来指定的4> @Caching : @Caching注解可以在一个方法或者类上同时指定多 个Spring Cache 相关的注解。其拥有三个属性 : cacheable、put和evict,分别用于指定@Cacheable、@CachePut 和 @CacheEvict
@Caching(cacheable = @Cacheable("users"),evict = { @CacheEvict("cache2"),@CacheEvict(value = "cache3", allEntries = true) })
public User find(Integer id) {return null;
}5> @CacheConfig : 只能用在类上,,允许共享缓存的名称、KeyGenerator、CacheManager 和 CacheResolver,相应的属性可以被方法级别的对应属性值所覆盖

【Spring Boot2.x】整合redis、mybatisPlus这篇文章就够了【真实开发环境实用】相关推荐

  1. spring boot2.x整合redis

    在springboot1.x系列中,其中连接redis服务器使用的是jedis,但是到了springboot2.x使用的是Lettuce. 关于jedis跟lettuce的区别: Lettuce 和 ...

  2. Spring Boot 2 整合Redis哨兵模式

    Spring Boot 2 整合Redis哨兵模式除了配置稍有差异,其它与整合单实例模式类似,配置示例为 1. pom  文件 <parent><groupId>org.spr ...

  3. springboot整合redis实现HyperLogLog统计文章浏览量使用过期策略完成数据库同步

    springboot整合redis实现HyperLogLog统计文章浏览量&&使用过期策略完成数据库同步 本文目录 springboot整合redis实现HyperLogLog统计文章 ...

  4. Vue开发入门看这篇文章就够了

    摘要: 很多值得了解的细节. 原文:Vue开发看这篇文章就够了 作者:Random Fundebug经授权转载,版权归原作者所有. 介绍 Vue 中文网 Vue github Vue.js 是一套构建 ...

  5. Android 8.0新特性(看这篇文章就够了)

    2019独角兽企业重金招聘Python工程师标准>>> 在刚结束不久的谷歌 I/O2017开发者大会上发布的第二个Android O(安卓8.0)开发者预览,并且向普通用户开放了第二 ...

  6. MySQL事务,这篇文章就够了

    原文链接:https://blog.ouyangsihai.cn/ >> MySQL事务,这篇文章就够了 在看这篇文章之前,我们回顾一下前面的几篇关于MySQL的系列文章,应该对你读下面的 ...

  7. 好多人都说存储过程很难?认真看这篇文章就够了

    何为存储过程? 存储过程是在数据库管理系统中保存的.预先编译的并能实现某种功能的sql程序,说直白点,java知道吧?和java的方法一样. 每遇到一个新的知识点时,我们都会看看它的优点,从而加深对它 ...

  8. 使用分层网络模型的两个优点是什么_从零开始学网络|搞懂OSI参考模型和TCP/IP分层模型,看这篇文章就够了...

    从零开始学网络|搞懂OSI参考模型和TCP/IP分层模型,看这篇文章就够了​mp.weixin.qq.com 前言 今天和大家一起谈谈"网络",之前写的文章可能不太通俗易懂,有人就 ...

  9. 本篇文章使用vue结合element-ui开发tab切换vue的不同组件,每一个tab切换的都是一个新的组件。

    本篇文章使用vue结合element-ui开发tab切换vue的不同组件,每一个tab切换的都是一个新的组件. 1.vue如何使用element-ui 上一篇文章已经分享了如何在vue中使用eleme ...

最新文章

  1. [Linux] shell
  2. 数据结构第二章线性表学习笔记
  3. hdu 1392 Surround the Trees
  4. 二次重建基本完成辣!
  5. 人口增长(信息学奥赛一本通-T1070)
  6. Java快捷键的设置与使用
  7. 彻底删除SQL Server注册表的方法(已经多次使用)
  8. python魔兽挂机_实现 WOW 插件的简单步骤
  9. Unity3D 编辑器功能之MonoBehaviour属性
  10. 【NLP】模型压缩与蒸馏!BERT的忒修斯船
  11. Receptive Field Block Net for Accurate and Fast Object Detection(RFB)
  12. Rasa 文档 中英文翻译版本 3 - Tutorial: Building Assistants
  13. php 提取数字,php如何实现只取数字的功能
  14. 查看onedrive教育版或企业版的容量(有福利)
  15. android动态扩容数组,ArrayList 扩容 Android Java 真的不一样
  16. 苹果手机开热点,连上了却无网
  17. 如何组建游戏制作团队?团队人员如何寻找?
  18. 论坛议程|COSCon'22 云计算(C)
  19. k8s部署springcloud架构的一些心得体会
  20. ABB机器人备份的那些事儿

热门文章

  1. Python3.绘图基础matplotlib.pyplot
  2. 格力明年要用上自家的芯片?这似乎有点脱离现实
  3. 360xp专版 v9.6 官方版
  4. org.springframework.mail.MailAuthenticationException: Authentication failed;解决方法
  5. JAVA解压RAR5压缩包
  6. OPPO 1107线刷刷机包 救砖解锁教程
  7. Android 机器人项目Pepper_Android
  8. Mac安装kali双系统
  9. 使用Excel和翻译api批量翻译字符串
  10. 大一学计算机专业有没有买笔记本,大一新生有必要带电脑吗,辅导员一语道破玄机,学生需提前知晓...