好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

使用Spring Cache设置缓存条件操作

Spring Cache设置缓存条件

原理

从Spring3.1开始,Spring框架提供了对Cache的支持,提供了一个对缓存使用的抽象,通过在既有代码中添加少量它定义的各种 annotation,即能够达到缓存方法的返回对象的作用。

提供的主要注解有@Cacheable、@CachePut、@CacheEvict和@Caching,具体见下表:

注解 说明
@Cacheable 可以标注在类或方法上:标注在方法上表示该方法支持数据缓存;标在类上表示该类的所有方法都支持数据缓存。 具体功能:在执行方法体之前,检查缓存中是否有相同key值的缓存存在,如果存在对应的缓存,直接返回缓存中的值;如果不存在对应的缓存,则执行相应的方法体获取数据,并将数据存储到缓存中。
@CachePut 可以标注在类或方法上,表示支持数据缓存。 具体功能:在方法执行前不会检查缓存中是否存在相应的缓存,而是每次都会执行方法体,并将方法执行结果存储到缓存中,如果相应key值的缓存存在,则更新key对应的value值。
@CacheEvict 可以标注在类或方法上,用于清除相应key值的缓存。
@Caching 可以标注在类或方法上,它有三个属性cacheable、put、evict分别用于指定@Cacheable、@CachePut和@CacheEvict

当需要在类上或方法上同时使用多个注解时,可以使用@Caching,如:

?

1

@Caching (cacheable= @Cacheable ( "User" ), evict = { @CacheEvict ( "Member" ), @CacheEvict (value = "Customer" , allEntries = true )})

@Cacheable的常用属性及说明

如下表所示:

@Cacheable属性 说明
key 表示缓存的名称,必须指定且至少要有一个值,比如:@Cacheable(value=[Dept])或@Cacheable(value={[Dept],[Depts]})
condition 表示是否需要缓存,默认为空,表示所有情况都会缓存。通过SpEL表达式来指定,若condition的值为true则会缓存,若为false则不会缓存,如@Cacheable(value=[Dept],key="‘deptno_'+# deptno [,condition=]#deptno<=40")
value 表示缓存的key,支持SpEL表达式,如@Cacheable(value=[Dept],key="‘deptno_' +#deptno"),可以不指定值,如果不指定,则缺省按照方法的所有参数进行组合。除了上述使用方法参数作为key之外,Spring还提供了一个root对象用来生成key,使用方法如下表所示,其中"#root"可以省略。

Root对象

Root对象 说明
methodName 当前方法名,比如#root.methodName
method 当前方法,比如#root.method.name
target 当前被调用的对象,比如#root.target
targetClass 当前被调用的对象的class,比如#root.targetClass
args 当前方法参数组成的数组,比如#root.args[0]
caches 当前被调用的方法使用的缓存,比如#root.caches[0].name

@CachePut的常用属性同@Cacheable

@CacheEvict的常用属性如下表所示:

@CacheEvict属性 说明
value 表示要清除的缓存名
key 表示需要清除的缓存key值,
condition 当condition的值为true时才清除缓存
allEntries 表示是否需要清除缓存中的所有元素。默认为false,表示不需要,当指定了allEntries为true时,将忽略指定的key。
beforeInvocation 清除操作默认是在方法成功执行之后触发的,即方法如果因为抛出异常而未能成功返回时不会触发清除操作。使用beforeInvocation可以改变触发清除操作的时间,当该属性值为true时,会在调用该方法之前清除缓存中的指定元素。


示例:设置当 dname 的长度大于3时才缓存

?

1

2

3

4

5

6

7

8

9

10

//条件缓存

@ResponseBody

@GetMapping ( "/getLocByDname" )

@Cacheable (cacheNames = "dept" , key = "#dname" , condition = "#dname.length()>3" )

public String getLocByDname( @RequestParam ( "dname" ) String dname) { //key动态参数

     QueryWrapper<Dept> queryMapper = new QueryWrapper<>();

     queryMapper.eq( "dname" , dname);

     Dept dept = deptService.getOne(queryMapper);

     return dept.getLoc();

}

示例:unless 即条件不成立时缓存

#result 代表返回值,意思是当返回码不等于 200 时不缓存,也就是等于 200 时才缓存。

?

1

2

3

4

5

6

7

8

9

10

11

12

@ResponseBody

@GetMapping ( "/getDeptByDname" )

@Cacheable (cacheNames = "dept" , key = "#dname" , unless = "#result.code != 200" )

public Result<Dept> getDeptByDname( @RequestParam ( "dname" ) String dname){ //key动态参数

     QueryWrapper<Dept> queryMapper = new QueryWrapper<>();

     queryMapper.eq( "dname" , dname);

     Dept dept = deptService.getOne(queryMapper);

     if (dept == null )

         return ResultUtil.error( 120 , "dept is null" );

     else

         return ResultUtil.success(dept);

}

Cache缓存配置

1、pom.xml

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

< dependency >

  < groupId >org.springframework.boot</ groupId >

  < artifactId >spring-boot-starter-cache</ artifactId >

</ dependency >

< dependency >

  < groupId >org.springframework.boot</ groupId >

  < artifactId >spring-boot-starter-aop</ artifactId >

</ dependency >

<!-- 反射工具类用于手动扫描指定包下的注解,根据defaultCache模块增加ehcache缓存域(非Spring Cache必须)-->

<!-- https://mvnrepository测试数据/artifact/org.reflections/reflections -->

< dependency >

     < groupId >org.reflections</ groupId >

     < artifactId >reflections</ artifactId >

     < version >0.9.11</ version >

</ dependency >

2、Ehcache配置文件

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

<? xml version = "1.0" encoding = "UTF-8" ?>

< ehcache xmlns:xsi = "http://HdhCmsTestw3.org/2001/XMLSchema-instance"

          xsi:noNamespaceSchemaLocation = "http://ehcache.org/ehcache.xsd"

          updateCheck = "false" >

     <!-- 磁盘缓存位置 -->

     < diskStore path = "java.io.tmpdir" />

     <!--

         name:缓存名称。

         maxElementsInMemory:缓存最大个数。

         eternal:对象是否永久有效,一但设置了,timeout将不起作用。

         timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。

         timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。

         overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。

         diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。

         maxElementsOnDisk:硬盘最大缓存个数。

         diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.

         diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。

         memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。

         clearOnFlush:内存数量最大时是否清除。

     -->

     <!-- 默认缓存 -->

     < defaultCache

             eternal = "false"

             maxElementsInMemory = "200000"

             overflowToDisk = "false"

             diskPersistent = "false"

             timeToIdleSeconds = "0"

             timeToLiveSeconds = "600"

             memoryStoreEvictionPolicy = "LRU" />

</ ehcache >

3、配置类

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

@Configuration

@EnableCaching

public class CustomConfiguration {

     /**

      * @see org.springframework.cache.interceptor.SimpleKeyGenerator

      * Generate a key based on the specified parameters.

      */

     public static Object generateKey(Object... params) {

         if (params.length == 0 ) {

             return SimpleKey.EMPTY;

         }

         if (params.length == 1 ) {

             Object param = params[ 0 ];

             if (param != null && !param.getClass().isArray()) {

                 return param;

             }

         }

         return new SimpleKey(params);

     }

/**

  * 若将target作为key的一部分时,CGLIB动态代理可能导致重复缓存

  * 注意:返回的key一定要重写hashCode()和toString(),防止key对象不一致导致的缓存无法命中

  * 例如:ehcache 底层存储net.sf.ehcache.store.chm.SelectableConcurrentHashMap#containsKey

  */

     @Bean

     public KeyGenerator customKeyGenerator(){

         return (target, method, params) -> {

             final Object key = generateKey(params);

             StringBuffer buffer = new StringBuffer();

             buffer.append(method.getName());

             buffer.append( "::" );

             buffer.append(key.toString());

// 注意一定要转为String,否则ehcache key对象可能不一样,导致缓存无法命中

             return buffer.toString();

         };

     }

     /**

      * redis缓存管理器

      */

     @Bean

     @ConditionalOnBean (RedisConfiguration. class )

     @ConditionalOnProperty (prefix = "spring.cache" , name = "type" , havingValue = "redis" ,

             matchIfMissing = false )

     public CacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) {

         RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()

                 .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer( new StringRedisSerializer()))

                 .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer( new GenericJackson2JsonRedisSerializer()))

                 .entryTtl(Duration.ofMinutes( 10 ));

         return RedisCacheManager

                 .builder(RedisCacheWriter.lockingRedisCacheWriter(redisConnectionFactory))

                 .cacheDefaults(config).build();

     }

/**

  * ehcache缓存管理器(默认)

  * default XML files {@link net.sf.ehcache.config.ConfigurationFactory#parseConfiguration()}

  */

     @Bean

     @ConditionalOnProperty (prefix = "spring.cache" , name = "type" , havingValue = "ehcache" ,

             matchIfMissing = true )

     public CacheManager ehcacheCacheManager() {

         net.sf.ehcache.CacheManager cacheManager = net.sf.ehcache.CacheManager.create();

         /**

          * 包扫描查找指定注解并将cacheNames添加到net.sf.ehcache.CacheManager(单例)

          */

         Reflections reflections = new Reflections( "com.example.demo.service" , new TypeAnnotationsScanner()

                 , new SubTypesScanner(), new MethodAnnotationsScanner());

         Set<Class<?>> classesList = reflections.getTypesAnnotatedWith(CacheConfig. class );

         for (Class<?> aClass : classesList) {

             final CacheConfig config = AnnotationUtils.findAnnotation(aClass, CacheConfig. class );

             if (config.cacheNames() != null && config.cacheNames().length > 0 ) {

                 for (String cacheName : config.cacheNames()) {

                     cacheManager.addCacheIfAbsent(cacheName);

                 }

             }

         }

         /**

          * 方法级别的注解 @Caching、@CacheEvict、@Cacheable、@CachePut,结合实际业务场景仅扫描@Cacheable即可

          */

         final Set<Method> methods = reflections.getMethodsAnnotatedWith(Cacheable. class );

         for (Method method : methods) {

             final Cacheable cacheable = AnnotationUtils.findAnnotation(method, Cacheable. class );

             if (cacheable.cacheNames() != null && cacheable.cacheNames().length > 0 ) {

                 for (String cacheName : cacheable.cacheNames()) {

                     cacheManager.addCacheIfAbsent(cacheName);

                 }

             }

         }

         EhCacheCacheManager ehCacheCacheManager = new EhCacheCacheManager();

         ehCacheCacheManager.setCacheManager(cacheManager);

         return ehCacheCacheManager;

     }

}

4、示例

?

1

2

3

4

5

6

7

8

9

10

11

12

@Component

@CacheConfig (cacheNames = "XXXServiceImpl" , keyGenerator = "customKeyGenerator" )

public class XXXServiceImpl extends ServiceImpl<XXXMapper, XXXEntity> implements XXXService {

     @CacheEvict (allEntries = true )

     public void evictAllEntries() {}

     @Override

     @Cacheable

     public List<XXXEntity> findById(Long id) {

         return this .baseMapper.selectList( new QueryWrapper<XXXEntity>().lambda()

                 .eq(XXXEntity::getId, id));

     }

}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。

原文链接:https://hcshow.blog.csdn.net/article/details/119271227

查看更多关于使用Spring Cache设置缓存条件操作的详细内容...

  阅读:16次