当前位置: 首页>后端>正文

spring boot销毁之前 springboot清除缓存

一 JSR107

Java Caching定义了5个核心接口,分别是CachingProvider, CacheManager, Cache, Entry 和 Expiry。

  • CachingProvider定义了创建、配置、获取、管理和控制多个CacheManager。一个应用可以在运行期访问多个CachingProvider。
  • CacheManager定义了创建、配置、获取、管理和控制多个唯一命名的Cache,这些Cache存在于CacheManager的上下文中。一个CacheManager仅被一个CachingProvider所拥有。
  • Cache是一个类似Map的数据结构并临时存储以Key为索引的值。一个Cache仅被一个CacheManager所拥有。
  • Entry是一个存储在Cache中的key-value对。
  • Expiry 每一个存储在Cache中的条目有一个定义的有效期。一旦超过这个时间,条目为过期的状态。一旦过期,条目将不可访问、更新和删除。缓存有效期可以通过ExpiryPolicy设置。

二、Spring缓存抽象

Spring从3.1开始定义了org.springframework.cache.Cache
和org.springframework.cache.CacheManager接口来统一不同的缓存技术;并支持使用JCache(JSR-107)注解简化我们开发;

  • Cache接口为缓存的组件规范定义,包含缓存的各种操作集合;
  • Cache接口下Spring提供了各种xxxCache的实现;如RedisCache,EhCacheCache , ConcurrentMapCache等;
  • 每次调用需要缓存功能的方法时,Spring会检查检查指定参数的指定的目标方法是否已经被调用过;如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法并缓存结果后返回给用户。下次调用直接从缓存中获取。
  • 使用Spring缓存抽象时我们需要关注以下两点;
    1、确定方法需要被缓存以及他们的缓存策略
    2、从缓存中读取之前缓存存储的数据

三、几个重要概念&缓存注解

spring boot销毁之前 springboot清除缓存,spring boot销毁之前 springboot清除缓存_java,第1张

1.开启基于注解的缓存
@EnableCaching 开启基于注解的缓存
2.标注缓存注解
@Cacheable   主要针对方法配置,能够根据方法的请求参数对其结果进行缓存
@CacheEvict  清空缓存
@CachePut    保证方法被调用,又希望结果被缓存。
@Cacheing   可以同时写多个类型的缓存
@CacheConfig 在类上统一配置一些缓存属性比如ChacheNames

spring boot销毁之前 springboot清除缓存,spring boot销毁之前 springboot清除缓存_缓存_02,第2张

spring boot销毁之前 springboot清除缓存,spring boot销毁之前 springboot清除缓存_spring boot销毁之前_03,第3张

  • 案例
package com.lc.springboot;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

/**
 * 一、搭建基本环境
 * 1.导入数据库文件 创建department和employee表
 * 2.创建Javabean
 * 3.整合Mybatis操作数据库
 *  1)配置数据源信息
 *  2)使用注解板的mybatis
 *      使用@Mapperscan指定需要扫描的Mapper包
 * 二、快速体验缓存
 *  步骤:
 *      1.开启基于注解的缓存
 *          @EnableCaching 开启基于注解的缓存
 *      2.标注缓存注解
 *          @Cacheable   主要针对方法配置,能够根据方法的请求参数对其结果进行缓存
 *          @CacheEvict  清空缓存
 *          @CachePut    报证方法被调用,又希望结果被缓存。
 *
 *
 */
@MapperScan("com.lc.springboot.mapper")
@SpringBootApplication
@EnableCaching
public class Springboot01CacheApplication {

    public static void main(String[] args) {
        SpringApplication.run(Springboot01CacheApplication.class, args);
    }

}
package com.lc.springboot.service;

import com.lc.springboot.bean.Employee;
import com.lc.springboot.mapper.EmployeeMapper;
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.cache.annotation.Caching;
import org.springframework.stereotype.Service;

import java.sql.SQLOutput;

@Service
public class EmployeeService {
    @Autowired
    private EmployeeMapper employeeMapper;

    /**
     * 将方法的运行结果进行缓存;以后再要相同的数据,直接从缓存中获取;不在调用方法
     *  原理:
     *      1.自动配置:CacheAutoConfiguration
     *      2.缓存的配置类
     *          org.springframework.boot.autoconfigure.cache.GenericCacheConfiguration
     *          org.springframework.boot.autoconfigure.cache.JCacheCacheConfiguration   JSR107的配置
     *          org.springframework.boot.autoconfigure.cache.EhCacheCacheConfiguration
     *          org.springframework.boot.autoconfigure.cache.HazelcastCacheConfiguration
     *          org.springframework.boot.autoconfigure.cache.InfinispanCacheConfiguration
     *          org.springframework.boot.autoconfigure.cache.CouchbaseCacheConfiguration
     *          org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration
     *          org.springframework.boot.autoconfigure.cache.CaffeineCacheConfiguration
     *          org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration
     *          org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration
     *       3.默认生效的的缓存配置  SimpleCacheConfiguration
     *       4.给容器注册了一个 CacheManager:ConcurrentMapCache
     *       5.可以获取和创建ConcurrentMapCache类型的缓存组件,将数据保存在ConcurrentMap中
     *       运行流程:
     *          1.方法运行之前先去查询cache(缓存组件),按照cacheNames指定的名字获取(cacheManager 先获取相应的缓存)
     *            如果没有cache组件会自动创建
 *              2.会去cache中根据key查找缓存内容;key默认是方法的参数 ;
     *              key是按照某种策略生成的, 默认使用SimpleKeyGenerator生成
     *                  如果没有参数:key=new SimpleKey()
     *                  如果有一个参数: key = 参数的值
     *                  如果有多个参数:key=new SimpleKey(param)
 *              3.没有查到缓存就会调用目标方法
     *          4.将目标方法返回的结果放进缓存
     *          先检查是否存在,不存在则执行方法,默认使用方法的参数值作为key去查询缓存
     *  属性介绍:
     *  cacheNames/values:指定缓存的名字
     *  key:缓存数据使用的key;可以用它来指定。默认是使用方法参数的值 :id-方法的返回值
     *      编写SpEl #id:参数id的值 #a0 #p0 #root.args[0]
     *      key="#root.methodName+'['+#id+']'"
     *  keyGenerator:key的生成器,可以自己指定key的生成器组件id
     *      key/keyGenerator 二选一使用
     *  cacheManager:指定缓存管理器或者是指定cacheResolver解析器
     *  condition:指定符合条件的情况下才缓存 condition = "#id>0"
     *      互逆
     *  unless: 用于否决缓存的,不像condition,该表达式只在方法执行之后判断,此时可以拿到返回值result进行判断。
     *          条件为true不会缓存,false才缓存
     *          unless = "#result==null"
     *  sync: 是否使用异步模式
     * @param id
     * @return
     */
    @Cacheable(cacheNames = {"emp"}/*, keyGenerator = "myKeyGenerator"*/)
    public Employee getEmp(Integer id){
        System.out.println("查询" + id + "号员工");
        Employee emp = employeeMapper.getEmpById(id);
        return emp;
    }

    /**
     * @CachePut : 即调用方法有更新缓存
     *
     * 运行时机:
     * 1.先调用方法
     * 2.将目标方法的加过缓存起来
     * 更新要注意cacheNames和key这样查询时也显示正常的数据
     * 同步更新缓存
     *
     *
     * @param employee
     * @return
     */

    @CachePut(cacheNames = "emp",key="#result.id")
    public Employee updateEmp(Employee employee){
        System.out.println("员工更新"+ employee);
        employeeMapper.updateEmp(employee);
        return employee;
    }

    /**
     * @CacheEvict 清除缓存
     *  key :指定要清除的数据
     *  allEntries: 是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存
     *  beforeInvocation: 是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,
     *  缺省情况下,如果方法执行抛出异常,则不会清空缓存
     */
    @CacheEvict(cacheNames = "emp", key="#id")
    public void deleteEmp(Integer id){
        System.out.println("删除" + 1);
        //employeeMapper.deleteEmp(id);
    }

    @Caching(
            cacheable = {
                    @Cacheable(value = "emp", key="#lastName")
            },
            put = {
                    @CachePut(value = "emp", key="#result.id"),
                    @CachePut(value = "emp", key="#result.email")
            }
    )
    public Employee getEmpByLastName(String lastName){
        Employee employee = employeeMapper.getEmpByLastName(lastName);
        return employee;
    }
}

==== 可以使用
@CacheConfig(cacheNames = “emp”)
来给类中的所有缓存指定name

@CacheConfig(cacheNames = "emp")
@Service
public class EmployeeService {
// 自定义key生成策略
package com.lc.springboot.config;

import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.lang.reflect.Method;
import java.util.Arrays;

@Configuration
public class MyCacheConfig {

    @Bean("myKeyGenerator")
    public KeyGenerator keyGenerator(){
        return new KeyGenerator(){

            @Override
            public Object generate(Object o, Method method, Object... objects) {
                return method.getName()+ "[" + Arrays.asList(objects).toString() + "]";
            }
        };
    }
}

四、缓存使用

1、引入spring-boot-starter-cache模块
2、@EnableCaching开启缓存
3、使用缓存注解
4、切换为其他缓存

(一)整合Redis缓存(spring boot 2.0)

在springboot1.x系列中,其中使用的是jedis,但是到了springboot2.x其中使用的是Lettuce。 此处springboot2.x,所以使用的是Lettuce。
关于jedis跟lettuce的区别:

  • Lettuce 和 Jedis 的定位都是Redis的client,所以他们当然可以直接连接redis server。
  • Jedis在实现上是直接连接的redis server,如果在多线程环境下是非线程安全的,这个时候只有使用连接池,为每个Jedis实例增加物理连接
  • Lettuce的连接是基于Netty的,连接实例(StatefulRedisConnection)可以在多个线程间并发访问,应为StatefulRedisConnection是线程安全的,所以一个连接实例(StatefulRedisConnection)就可以满足多线程环境下的并发访问,当然这个也是可伸缩的设计,一个连接实例不够的情况也可以按需增加连接实例。
1、安装Redis
docker run -p 6379:6379 
-v /zzyyuse/myredis/data:/data 
-v /zzyyuse/myredis/conf/redis.conf:/usr/local/etc/redis/redis.conf  
-d redis:3.2 redis-server /usr/local/etc/redis/redis.conf --appendonly yes
2. maven 依赖导入
<dependency>
   <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--fastjson-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.47</version>
</dependency>

<!-- redis依赖commons-pool 这个依赖一定要添加 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>
3.redis参数配置
# 配置Redis
# 缓存时长,单位秒
cache.default-exp=72
spring.redis.database=0
spring.redis.host=192.168.43.230
spring.redis.port=6379
# 密码
spring.redis.password=
# 连接超时时间 单位 ms(毫秒)
spring.redis.timeout=3000
# 连接池中的最大空闲连接,默认值也是8
spring.redis.lettuce.pool.max-idle=100
# 连接池中的最小空闲连接,默认值也是0
spring.redis.lettuce.pool.min-idle=50
# 如果赋值为-1,则表示不限制
spring.redis.lettuce.pool.max-wait=2000
4.FastJsonRedisSerializer编写
package com.lc.springboot.config;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
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;

public class FastJsonRedisSerializer<T> implements RedisSerializer<T> {

    public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");

    private Class<T> clazz;
    static {
        ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
        //ParserConfig.getGlobalInstance().addAccept("com.demo.redis.entity.Grade");
    }
    public FastJsonRedisSerializer(Class<T> clazz) {
        super();
        this.clazz = clazz;
    }

    @Override
    public byte[] serialize(T t) throws SerializationException {
        if (null == t) {
            return new byte[0];
        }
        return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
    }

    @Override
    public T deserialize(byte[] bytes) throws SerializationException {
        if (null == bytes || bytes.length <= 0) {
            return null;
        }
        String str = new String(bytes, DEFAULT_CHARSET);
        return (T) JSON.parseObject(str, clazz);
    }

}
5.RedisConfig
package com.lc.springboot.config;

import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializationContext;

import java.lang.reflect.Method;
import java.time.Duration;
import java.util.Arrays;


@Configuration
public class RedisConfig extends CachingConfigurerSupport {
    /**
     *
     * 自定义主键生成策略
     * @return
     */
    @Bean("myKeyGenerator")
    public KeyGenerator keyGenerator(){
        return new KeyGenerator(){

            @Override
            public Object generate(Object o, Method method, Object... objects) {
                return method.getName()+ "[" + Arrays.asList(objects).toString() + "]";
            }
        };
    }
    /**
     * RedisTemplate 配置使用json保存数据而不是用序列化数据
     * @param connectionFactory
     * @return
     */
    @Bean
    public RedisTemplate<Object, Object> redisTemplate(LettuceConnectionFactory connectionFactory) {
        // 配置redisTemplate
        RedisTemplate<Object, Object> template = new RedisTemplate();
        template.setConnectionFactory(connectionFactory);
        //template.setKeySerializer(new StringRedisSerializer());//key序列化
        //redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());//value序列化
        template.setDefaultSerializer(new FastJsonRedisSerializer<>(Object.class));//value序列化
        template.afterPropertiesSet();
        return template;
    }

    /**
     * RedisCacheManger 使用json保存数据的配置
     * @return
     */
    @Bean
    public RedisCacheConfiguration redisCacheConfiguration() {
        FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
        RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig();
        configuration = configuration
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer))
                .entryTtl(Duration.ofDays(30));
        return configuration;
    }
}



https://www.xamrdz.com/backend/3zm1962892.html

相关文章: