栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

SpringCache的使用

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

SpringCache的使用

SpringCache的使用
  • 1 SpringCache的简介
  • 2 SpringCache的使用
        • 1 注解说明
        • 2 案例
        • 3 SpringCache整合Redis

在目前的项目中, 缓存的使用越来越多,适用的场景也越来越广.对于缓存,Spring提供的Spring Cache框架,能够非常方便地使用缓存.

1 SpringCache的简介

缓存,就是将数据从数据库等数据来源获取数据,将数据缓存在内存或其他设备如Redis中,为了二次查询能够快速高效的响应结果.

Spring Cache是3.1开始提供, 通过注解的形式,对于整合业务代码友好.

Spring Cache特点:

  • 提供Cache通用入口 ,方便多种实现切换缓存源,如Redis,Guava Cache等
  • 支持事务, 即食物回滚时,缓存同时自动回滚

Cache源码说明:

// 定义公共缓存操作的接口
public interface Cache {

	
    // 缓存的名称
	String getName();

	
    // 得到底层的缓存 如Ehcache
	Object getNativeCache();

	// 根据可以得到ValueWrapper对象,再给句get方法获取值
	@Nullable
	ValueWrapper get(Object key);

	// 根据key,和value的类型获取value
	@Nullable
	 T get(Object key, @Nullable Class type);

	// 添加缓存
	void put(Object key, @Nullable Object value);


    // 存在key则获取,不存在则添加
	@Nullable
	default ValueWrapper putIfAbsent(Object key, @Nullable Object value) {
		ValueWrapper existingValue = get(key);
		if (existingValue == null) {
			put(key, value);
		}
		return existingValue;
	}

	// 根据key清除对应缓存
	void evict(Object key);

	// 如key存在,则清除对应缓存
	default boolean evictIfPresent(Object key) {
		evict(key);
		return false;
	}

	// 清空缓存
	void clear();

    // 缓存值的ValueWrapper对象
	@FunctionalInterface
	interface ValueWrapper {
        
		@Nullable
		Object get();
	}

}

Cache接口提供的默认实现类有等:

  • AbstractValueAdaptingCache 抽象类
  • ConcurrentMapCache 基于java.util.concurrent.ConcurrentHashMap的缓存
  • JCacheCache 对javax.cache.Cache的实例
  • CaffeineCache 对com.github.benmanes.caffeine.cache.Cache的实例
  • RedisCache 基于Redis的缓存实现类

Spring中提供的缓存管理的类

CacheManager接口:

public interface CacheManager {

	
	@Nullable
    // 根据缓存名字获取缓存
	Cache getCache(String name);

	
    // 得到所有缓存的名字
	Collection getCacheNames();

}

CacheManager接口提供的默认实现类有等:

  • AbstractCacheManager 抽象类
  • ConcurrentMapCacheManager 对应 ConcurrentMapCacheFactoryBean
  • EhCacheCacheManager 对应 EhCacheManagerFactoryBean
  • JCacheCacheManager 对应 EhCacheManagerFactoryBean
  • RedisCacheManager 基于Redis的缓存管理
  • CompositeCacheManager 组合管理CacheManager的实现类

KeyGenerator接口
接口提供了一套生成缓存默认key的策略.

@FunctionalInterface
public interface KeyGenerator {

	
	Object generate(Object target, Method method, Object... params);

}

KeyGenerator接口提供的默认实现类有等:

  • DefaultKeyGenerator Spring3.1版本默认key生成实现类, 已过期,从Spring4.0开始, 使用SimpleKeyGenerator来代替

  • SimpleKeyGenerator Spring4.0开始,默认的实现类

  • KeyGeneratorAdapter key生成相关参数适配

2 SpringCache的使用 1 注解说明

@Cacheable

使用场景: 查询方法接口

该注解会把方法的返回值缓存下来, 下一次调用方法时, 先查询缓存中是否存在,存在则直接返回,不存在,则查询数据返回,并将数据缓存.

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@documented
public @interface Cacheable {

	
	@AliasFor("cacheNames")
    // 缓存的名称,数据可以写入多个缓存
	String[] value() default {};

	
	@AliasFor("value")
    // 缓存的名称,数据可以写入多个缓存
	String[] cacheNames() default {};

	
    // 缓存key, 默认使用KeyGenerator生成
	String key() default "";

	
    // key生成器
	String keyGenerator() default "";

	
    // 缓存管理
	String cacheManager() default "";

	
    // 缓存解析
	String cacheResolver() default "";

	
    // 满足condition条件,才缓存数据 (在方法执行前后都判断)
	String condition() default "";

	
    // 否决缓存更新 (方法执行后判断)
	String unless() default "";

	
    // 如果多个线程试图为同一个键加载一个值,则同步底层方法的调用。
	boolean sync() default false;

}

@CachePut

使用场景: 新增或修改方法

该注解会把方法的返回值Put到缓存中, 供其他查询使用.

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@documented
public @interface CachePut {

	
	@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 "";

}

@CacheEvict

使用场景: 删除或修改方法

该注解会清空指定缓存.

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@documented
public @interface CacheEvict {

	
	@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 "";

	
    // 是否移除所有数据 
	boolean allEntries() default false;

	
	boolean beforeInvocation() default false;

}

@Caching

使用场景: 同时操作多个缓存

该注解是一个组合缓存,可以同时添加几个不同的注解,供不同的应用场景使用.如一个接口,可能同时操作多个缓存,且缓存的处理状态都不一样.

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@documented
public @interface Caching {

	Cacheable[] cacheable() default {};

	CachePut[] put() default {};

	CacheEvict[] evict() default {};

}

@CacheConfig

该注解用于配置该类中用的一些共用的缓存配置

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@documented
public @interface CacheConfig {

	
	String[] cacheNames() default {};

	
	String keyGenerator() default "";

	
	String cacheManager() default "";

	
	String cacheResolver() default "";

}

关于自定义key值的定义策略: 可以使用Spring的EL表达式来指定key

1 直接使用 “#参数名”或者“#p参数index”

   @Cacheable(value = "user", key = "#id")
   public Object select(String id) {
      return null;
   }

   @Cacheable(value="user", key="#p0")
   public Object select(String id) {
      return null;
   }

   @Cacheable(value="user", key="#user.id")
   public Object select(User user) {
      return null;
   }

   @Cacheable(value="user", key="#p0.id")
   public Object select(User user) {
      return null;
   }

2 使用root对象来生成key

属性名称描述示例
methodName当前方法名#root.methodName
method当前方法#root.method.name
target当前被调用的对象#root.target
targetClass当前被调用的对象的class#root.targetClass
args当前方法参数组成的数组#root.args[0]
caches当前被调用的方法使用的Cache#root.caches[0].name
2 案例

1 准备一个SpringBoot环境

2 添加maven依赖

    
      org.springframework.boot
      spring-boot-starter-cache
    

3 添加一个Service类

@Slf4j
@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    // 先从缓存中读取,如果没有再调用方法获取数据,然后把数据添加到缓存中
    @Cacheable(value = "user", key = "#id")
    public User select(String id) {
        log.info("=====select查询数据库啦=====");
        List users = userMapper.selectAll();
        for (User user : users) {
            if (user.getId().equals(id)) {
                return user;
            }
        }
        return null;
    }

    // 移除对应的缓存
    @CacheEvict(value = "user", key = "#user.id")
    public User update(User user) {
        log.info("=====update查询数据库啦=====");
        userMapper.updateByPrimaryKey(user);
        return user;
    }

    // 查询数据库,将查询结果放到缓存
    @CachePut(value = "user", key = "#user.id")
    public User save(User user) {
        log.info("=====save查询数据库啦=====");
        userMapper.insert(user);
        return user;
    }

}

4 添加一个测试类

@RunWith(SpringRunner.class)
// 指定启动类
@SpringBootTest(classes = Application.class)
@Slf4j
public class DemoApplicationTests {
    @Autowired
    private UserService userService;


    @Test
    public void testCache() {
        String id = "8";
        User user = userService.save(new User(id, "李白"));
        log.info("[ save方法  - {} ]",user);
        User user1 = userService.select(id);
        log.info("[ select 方法  - {} ]",user1);
        userService.update(user);
        User user2 = userService.select(id);
        log.info("[ select 方法  - {} ]",user2);
        User user3 = userService.select(id);
        log.info("[ select 方法  - {} ]",user3);
    }
}    

从上面运行结果可知, 当调用save方法时, 将id为8的用户数据给缓存到了user缓存中, 第一次查询select方法时, 因缓存中存在,所以直接从缓存中获取, 当执行了update方法, 缓存中数据被删除了, 第二次查询select方法时, 因为缓存中没有,所以select方法中查询数据库,并将数据缓存起来了. 第三次调用select方法时,因为缓存中又存在数据了,所以直接返回.

3 SpringCache整合Redis

1 添加Redis的maven依赖

    
      org.springframework.boot
      spring-boot-starter-data-redis
    

2 添加Redis的配置信息

#缓存类型
spring.cache.type=redis
#毫秒为单位  1h = 1*60*60*1000ms
spring.cache.redis.time-to-live=3600000
#如果指定了前缀就用我们指定的前缀CACHE_,没有默认就用缓存的名字作为前缀
spring.cache.redis.key-prefix=CACHE_
#允许使用前缀
spring.cache.redis.use-key-prefix=true
#是否缓存空值,防止缓存穿透
spring.cache.redis.cache-null-values=true

3 添加配置类

@EnableConfigurationProperties(CacheProperties.class)
@Configuration
//@EnableCaching // 开启缓存功能 启动类上添加, 此处可省略
public class MyCacheConfig {
    @Bean
    public RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
        config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
        config = config.serializevaluesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));

        CacheProperties.Redis redisProperties = cacheProperties.getRedis();
        //将配置文件中的所有配置都生效
        if (redisProperties.getTimeToLive() != null) {
            config = config.entryTtl((redisProperties.getTimeToLive()));
        }
        if (redisProperties.getKeyPrefix() != null) {
            config = config.prefixCacheNameWith((redisProperties.getKeyPrefix()));
        }
        if (!redisProperties.isCacheNullValues()) {
            config = config.disableCachingNullValues();
        }
        if (!redisProperties.isUseKeyPrefix()) {
            config = config.disableKeyPrefix();
        }

        return config;
    }

}

4 使用上面测试类

@RunWith(SpringRunner.class)
// 指定启动类
@SpringBootTest(classes = Application.class)
@Slf4j
public class DemoApplicationTests {
    @Autowired
    private UserService userService;

    @Test
    public void testCache() {
        String id = "9";
        User user = userService.save(new User(id, "李白"));
        log.info("[ save方法  - {} ]",user);
        User user1 = userService.select(id);
        log.info("[ select 方法  - {} ]",user1);
        userService.update(user);
        User user2 = userService.select(id);
        log.info("[ select 方法  - {} ]",user2);
        User user3 = userService.select(id);
        log.info("[ select 方法  - {} ]",user3);
    }
}  

使用Redis连接工具查看Redis数据库:

 

参考资料:

https://www.jianshu.com/p/33c019de9115

https://www.cnblogs.com/fashflying/p/6908028.html

https://blog.csdn.net/yiyihuazi/article/details/109065327

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/489058.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号