精华内容
下载资源
问答
  • 可缓存的响应 ... 为什么 如果您想拥有一流的用户体验,则服务器端渲染( SSR )是一件豪华但必不可少的事情。 做服务器端事情的主要问题是与动态事情相关的额外成本:服务器将花费CPU周期来...cacheable-response是用于
  • 关于@Cacheable注解在方法上根据请求参数拉起缓存内容,同时@CacheEvict 注解在方法上清空指定key缓存的原理内容
  • 主要介绍了详解Spring缓存注解@Cacheable,@CachePut , @CacheEvict使用,非常具有实用价值,需要的朋友可以参考下
  • EntityFrameworkCore.Cacheable 高性能第二级查询缓存。 什么是EF核心可缓存? 实体框架(EF)Cor​​e Cacheable是用于流行的实体框架数据访问技术的扩展库。 它为所有类型的查询结果提供缓存功能。 基于表达式...
  • const CacheableLookup = require ( 'cacheable-lookup' ) ; const cacheable = new CacheableLookup ( ) ; http . get ( 'http://example.com' , { lookup : cacheable . lookup } , response => { // Handle the ...
  • micro-cacheable是专注于数据缓存实用程序。 该软件包解决的问题是将已请求的数据保存在内存中,以使其在配置的时间内可用,而无需再次处理。 用法 cd my-micro-project/ npm install --save micro-cacheable 并...
  • Laravel开发-cacheable

    2019-08-27 12:35:25
    Laravel开发-cacheable 使任何对象方法返回可缓存。
  • npm install elasticsearch-cacheable 创建一个 elasticsearch 客户端,用这个模块改变它并使用: var esClient = require ( 'elasticsearch' ) . Client ( ) ; require ( './elasticsearch-cacheable' ) ( ...
  • Spring之缓存注解@Cacheable

    千次阅读 2020-12-20 09:40:31
    例: @Cacheable(cacheNames="foos", sync="true") public Foo executeExpensiveOperation(String id) {...} 1.2 @CachePut 在支持Spring Cache的环境下,对于使用@Cacheable标注的方法,Spring在每次执行前都会...

    从3.1开始,Spring引入了对Cache的支持。其使用方法和原理都类似于Spring对事务管理的支持。Spring Cache是作用在方法上的,其核心思想是这样的:当我们在调用一个缓存方法时会把该方法参数和返回结果作为一个键值对存放在缓存中,等到下次利用同样的参数来调用该方法时将不再执行该方法,而是直接从缓存中获取结果进行返回。所以在使用Spring Cache的时候我们要保证我们缓存的方法对于相同的方法参数要有相同的返回结果。

    使用Spring Cache需要我们做两方面的事:

    n  声明某些方法使用缓存

    n  配置Spring对Cache的支持

    和Spring对事务管理的支持一样,Spring对Cache的支持也有基于注解和基于XML配置两种方式。下面我们先来看看基于注解的方式。

    1       基于注解的支持

    Spring为我们提供了几个注解来支持Spring Cache。其核心主要是@Cacheable和@CacheEvict。使用@Cacheable标记的方法在执行后Spring Cache将缓存其返回结果,而使用@CacheEvict标记的方法会在方法执行前或者执行后移除Spring Cache中的某些元素。下面我们将来详细介绍一下Spring基于注解对Cache的支持所提供的几个注解。

    1.1    @Cacheable

    @Cacheable可以标记在一个方法上,也可以标记在一个类上。当标记在一个方法上时表示该方法是支持缓存的,当标记在一个类上时则表示该类所有的方法都是支持缓存的。对于一个支持缓存的方法,Spring会在其被调用后将其返回值缓存起来,以保证下次利用同样的参数来执行该方法时可以直接从缓存中获取结果,而不需要再次执行该方法。Spring在缓存方法的返回值时是以键值对进行缓存的,值就是方法的返回结果,至于键的话,Spring又支持两种策略,默认策略和自定义策略,这个稍后会进行说明。需要注意的是当一个支持缓存的方法在对象内部被调用时是不会触发缓存功能的。@Cacheable可以指定三个属性,value、key和condition。

    1.1.1  value属性指定Cache名称

    value属性是必须指定的,其表示当前方法的返回值是会被缓存在哪个Cache上的,对应Cache的名称。其可以是一个Cache也可以是多个Cache,当需要指定多个Cache时其是一个数组。

    @Cacheable("cache1")//Cache是发生在cache1上的

    publicUser find(Integer id) {

    returnnull;

    }

    @Cacheable({"cache1", "cache2"})//Cache是发生在cache1和cache2上的

    publicUser find(Integer id) {

    returnnull;

    }

    1.1.2  使用key属性自定义key

    key属性是用来指定Spring缓存方法的返回结果时对应的key的。该属性支持SpringEL表达式。当我们没有指定该属性时,Spring将使用默认策略生成key。我们这里先来看看自定义策略,至于默认策略会在后文单独介绍。

    自定义策略是指我们可以通过Spring的EL表达式来指定我们的key。这里的EL表达式可以使用方法参数及它们对应的属性。使用方法参数时我们可以直接使用“#参数名”或者“#p参数index”。下面是几个使用参数作为key的示例。

    @Cacheable(value="users", key="#id")publicUser find(Integer id) {

    returnnull;

    }

    @Cacheable(value="users", key="#p0")publicUser find(Integer id) {

    returnnull;

    }

    @Cacheable(value="users", key="#user.id")publicUser find(User user) {

    returnnull;

    }

    @Cacheable(value="users", key="#p0.id")publicUser find(User user) {

    returnnull;

    }

    除了上述使用方法参数作为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对象的属性作为key时我们也可以将“#root”省略,因为Spring默认使用的就是root对象的属性。如:

    @Cacheable(value={"users", "xxx"}, key="caches[1].name")publicUser find(User user) {

    returnnull;

    }

    @Cacheable(value = "page_user",key ="T(String).valueOf(#page).concat('-').concat(#pageSize)",unless = "#result=null")//由于page是int型,concat要求变量必须为String,所以强转一下

    @Overridepublic List page(int page, intpageSize) {returnuserMapper.page(page,pageSize);

    }

    1.1.3  condition属性指定发生的条件

    有的时候我们可能并不希望缓存一个方法所有的返回结果。通过condition属性可以实现这一功能。condition属性默认为空,表示将缓存所有的调用情形。其值是通过SpringEL表达式来指定的,当为true时表示进行缓存处理;当为false时表示不进行缓存处理,即每次调用该方法时该方法都会执行一次。如下示例表示只有当user的id为偶数时才会进行缓存。

    @Cacheable(value={"users"}, key="#user.id", condition="#user.id%2==0")publicUser find(User user) {

    System.out.println("find user by user " +user);returnuser;

    }

    1.1.4缓存的同步 sync:

    在多线程环境下,某些操作可能使用相同参数同步调用。默认情况下,缓存不锁定任何资源,可能导致多次计算,而违反了缓存的目的。对于这些特定的情况,属性 sync 可以指示底层将缓存锁住,使只有一个线程可以进入计算,而其他线程堵塞,直到返回结果更新到缓存中。例:

    @Cacheable(cacheNames="foos", sync="true")

    public Foo executeExpensiveOperation(String id) {...}

    1.2     @CachePut

    在支持Spring Cache的环境下,对于使用@Cacheable标注的方法,Spring在每次执行前都会检查Cache中是否存在相同key的缓存元素,如果存在就不再执行该方法,而是直接从缓存中获取结果进行返回,否则才会执行并将返回结果存入指定的缓存中。@CachePut也可以声明一个方法支持缓存功能。与@Cacheable不同的是使用@CachePut标注的方法在执行前不会去检查缓存中是否存在之前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中。

    @CachePut也可以标注在类上和方法上。使用@CachePut时我们可以指定的属性跟@Cacheable是一样的。

    @CachePut("users")//每次都会执行方法,并将结果存入指定的缓存中

    publicUser find(Integer id) {

    returnnull;

    }

    1.3     @CacheEvict

    @CacheEvict是用来标注在需要清除缓存元素的方法或类上的。当标记在一个类上时表示其中所有的方法的执行都会触发缓存的清除操作。@CacheEvict可以指定的属性有value、key、condition、allEntries和beforeInvocation。其中value、key和condition的语义与@Cacheable对应的属性类似。即value表示清除操作是发生在哪些Cache上的(对应Cache的名称);key表示需要清除的是哪个key,如未指定则会使用默认策略生成的key;condition表示清除操作发生的条件。下面我们来介绍一下新出现的两个属性allEntries和beforeInvocation。

    1.3.1  allEntries属性

    allEntries是boolean类型,表示是否需要清除缓存中的所有元素。默认为false,表示不需要。当指定了allEntries为true时,Spring Cache将忽略指定的key。有的时候我们需要Cache一下清除所有的元素,这比一个一个清除元素更有效率。

    @CacheEvict(value="users", allEntries=true)public voiddelete(Integer id) {

    System.out.println("delete user by id: " +id);

    }

    1.3.2  beforeInvocation属性

    清除操作默认是在对应方法成功执行之后触发的,即方法如果因为抛出异常而未能成功返回时也不会触发清除操作。使用beforeInvocation可以改变触发清除操作的时间,当我们指定该属性值为true时,Spring会在调用该方法之前清除缓存中的指定元素。

    @CacheEvict(value="users", beforeInvocation=true)public voiddelete(Integer id) {

    System.out.println("delete user by id: " +id);

    }

    其实除了使用@CacheEvict清除缓存元素外,当我们使用Ehcache作为实现时,我们也可以配置Ehcache自身的驱除策略,其是通过Ehcache的配置文件来指定的。由于Ehcache不是本文描述的重点,这里就不多赘述了,想了解更多关于Ehcache的信息,请查看我关于Ehcache的专栏。

    1.4     @Caching

    @Caching注解可以让我们在一个方法或者类上同时指定多个Spring Cache相关的注解。其拥有三个属性:cacheable、put和evict,分别用于指定@Cacheable、@CachePut和@CacheEvict。

    @Caching(cacheable = @Cacheable("users"), evict = { @CacheEvict("cache2"),

    @CacheEvict(value= "cache3", allEntries = true) })publicUser find(Integer id) {

    returnnull;

    }

    如下,Caching可以删除不同的key

    @CacheConfig(cacheNames = { "userFocus"})public interfaceUserFocusMapper {

    @CacheEvict(value="userFocusList",key = "#p0.userCode")intinsert(UserFocus userFocus);

    @Cacheable(key= "#p0.userCode+'_'+#p0.focusName+'_'+#p0.focusType",unless="#result == null")

    UserFocus findFocus(UserFocus userFocus);

    @Cacheable(value="userFocusList",key = "#p0.userCode",unless="#result == null||#result.size() == 0")

    ListfindFocusList(UserFocus userCode);//利用@Caching中evict指定两个value下的不同key删除。

    @Caching(evict = {@CacheEvict(value="userFocusList",key = "#p0.userCode"),

    @CacheEvict(value="userFocus",key = "#p0.userCode+'_'+#p0.focusName+'_'+#p0.focusType")})voidupdateUserFocus(UserFocus userFocus);

    }

    1.5  @CacheConfig

    有时候一个类中可能会有多个缓存操作,而这些缓存操作可能是重复的。这个时候可以使用@CacheConfig

    @CacheConfig("books")

    public class BookRepositoryImpl implements BookRepository {

    @Cacheable

    public Book findBook(ISBN isbn) {...}

    }

    @CacheConfig是一个类级别的注解,允许共享缓存的名称、KeyGenerator、CacheManager 和CacheResolver。该操作会被覆盖。

    1.6     使用自定义注解

    Spring允许我们在配置可缓存的方法时使用自定义的注解,前提是自定义的注解上必须使用对应的注解进行标注。如我们有如下这么一个使用@Cacheable进行标注的自定义注解。

    @Target({ElementType.TYPE, ElementType.METHOD})

    @Retention(RetentionPolicy.RUNTIME)

    @Cacheable(value="users")public @interfaceMyCacheable {

    }

    那么在我们需要缓存的方法上使用@MyCacheable进行标注也可以达到同样的效果。

    @MyCacheablepublicUser findById(Integer id) {

    System.out.println("find user by id: " +id);

    User user= newUser();

    user.setId(id);

    user.setName("Name" +id);returnuser;

    }

    2       配置Spring对Cache的支持

    2.1     声明对Cache的支持

    2.1.1  基于注解

    配置Spring对基于注解的Cache的支持,首先我们需要在Spring的配置文件中引入cache命名空间,其次通过就可以启用Spring对基于注解的Cache的支持。

    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

    http://www.springframework.org/schema/cache

    http://www.springframework.org/schema/cache/spring-cache.xsd">

    有一个cache-manager属性用来指定当前所使用的CacheManager对应的bean的名称,默认是cacheManager,所以当我们的CacheManager的id为cacheManager时我们可以不指定该参数,否则就需要我们指定了。

    还可以指定一个mode属性,可选值有proxy和aspectj。默认是使用proxy。当mode为proxy时,只有缓存方法在外部被调用的时候Spring Cache才会发生作用,这也就意味着如果一个缓存方法在其声明对象内部被调用时Spring Cache是不会发生作用的。而mode为aspectj时就不会有这种问题。另外使用proxy时,只有public方法上的@Cacheable等标注才会起作用,如果需要非public方法上的方法也可以使用Spring Cache时把mode设置为aspectj。

    此外,还可以指定一个proxy-target-class属性,表示是否要代理class,默认为false。我们前面提到的@Cacheable、@cacheEvict等也可以标注在接口上,这对于基于接口的代理来说是没有什么问题的,但是需要注意的是当我们设置proxy-target-class为true或者mode为aspectj时,是直接基于class进行操作的,定义在接口上的@Cacheable等Cache注解不会被识别到,那对应的Spring Cache也不会起作用了。

    需要注意的是只会去寻找定义在同一个ApplicationContext下的@Cacheable等缓存注解。

    2.1.2  基于XML配置

    除了使用注解来声明对Cache的支持外,Spring还支持使用XML来声明对Cache的支持。这主要是通过类似于aop:advice的cache:advice来进行的。在cache命名空间下定义了一个cache:advice元素用来定义一个对于Cache的advice。其需要指定一个cache-manager属性,默认为cacheManager。cache:advice下面可以指定多个cache:caching元素,其有点类似于使用注解时的@Caching注解。cache:caching元素下又可以指定cache:cacheable、cache:cache-put和cache:cache-evict元素,它们类似于使用注解时的@Cacheable、@CachePut和@CacheEvict。下面来看一个示例:

    上面配置定义了一个名为cacheAdvice的cache:advice,其中指定了将缓存findById方法和find方法到名为users的缓存中。这里的方法还可以使用通配符“*”,比如“find*”表示任何以“find”开始的方法。

    有了cache:advice之后,我们还需要引入aop命名空间,然后通过aop:config指定定义好的cacheAdvice要应用在哪些pointcut上。如:

    上面的配置表示在调用com.xxx.UserService中任意公共方法时将使用cacheAdvice对应的cache:advice来进行Spring Cache处理。更多关于Spring Aop的内容不在本文讨论范畴内。

    2.2     配置CacheManager

    CacheManager是Spring定义的一个用来管理Cache的接口。Spring自身已经为我们提供了两种CacheManager的实现,一种是基于Java API的ConcurrentMap,另一种是基于第三方Cache实现——Ehcache,如果我们需要使用其它类型的缓存时,我们可以自己来实现Spring的CacheManager接口或AbstractCacheManager抽象类。下面分别来看看Spring已经为我们实现好了的两种CacheManager的配置示例。

    2.2.1  基于ConcurrentMap的配置

    上面的配置使用的是一个SimpleCacheManager,其中包含一个名为“xxx”的ConcurrentMapCache。

    2.2.2  基于Ehcache的配置

    上面的配置使用了一个Spring提供的EhCacheCacheManager来生成一个Spring的CacheManager,其接收一个Ehcache的CacheManager,因为真正用来存入缓存数据的还是Ehcache。Ehcache的CacheManager是通过Spring提供的EhCacheManagerFactoryBean来生成的,其可以通过指定ehcache的配置文件位置来生成一个Ehcache的CacheManager。若未指定则将按照Ehcache的默认规则取classpath根路径下的ehcache.xml文件,若该文件也不存在,则获取Ehcache对应jar包中的ehcache-failsafe.xml文件作为配置文件。更多关于Ehcache的内容这里就不多说了,它不属于本文讨论的内容,欲了解更多关于Ehcache的内容可以参考我之前发布的Ehcache系列文章,也可以参考官方文档等。

    3       键的生成策略

    键的生成策略有两种,一种是默认策略,一种是自定义策略。

    3.1     默认策略

    默认的key生成策略是通过KeyGenerator生成的,其默认策略如下:

    n  如果方法没有参数,则使用0作为key。

    n  如果只有一个参数的话则使用该参数作为key。

    n  如果参数多余一个的话则使用所有参数的hashCode作为key。

    如果我们需要指定自己的默认策略的话,那么我们可以实现自己的KeyGenerator,然后指定我们的Spring Cache使用的KeyGenerator为我们自己定义的KeyGenerator。

    使用基于注解的配置时是通过cache:annotation-driven指定的.

    而使用基于XML配置时是通过cache:advice来指定的。

    需要注意的是此时我们所有的Cache使用的Key的默认生成策略都是同一个KeyGenerator。

    3.2     自定义策略

    自定义策略是指我们可以通过Spring的EL表达式来指定我们的key。这里的EL表达式可以使用方法参数及它们对应的属性。使用方法参数时我们可以直接使用“#参数名”或者“#p参数index”。下面是几个使用参数作为key的示例。

    @Cacheable(value="users", key="#id")publicUser find(Integer id) {

    returnnull;

    }

    @Cacheable(value="users", key="#p0")publicUser find(Integer id) {

    returnnull;

    }

    @Cacheable(value="users", key="#user.id")publicUser find(User user) {

    returnnull;

    }

    @Cacheable(value="users", key="#p0.id")publicUser find(User user) {

    returnnull;

    }

    除了上述使用方法参数作为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对象的属性作为key时我们也可以将“#root”省略,因为Spring默认使用的就是root对象的属性。如:

    @Cacheable(value={"users", "xxx"}, key="caches[1].name")publicUser find(User user) {

    returnnull;

    }

    4       Spring单独使用Ehcache

    前面介绍的内容是Spring内置的对Cache的支持,其实我们也可以通过Spring自己单独的使用Ehcache的CacheManager或Ehcache对象。通过在Application Context中配置EhCacheManagerFactoryBean和EhCacheFactoryBean,我们就可以把对应的EhCache的CacheManager和Ehcache对象注入到其它的Spring bean对象中进行使用。

    4.1     EhCacheManagerFactoryBean

    EhCacheManagerFactoryBean是Spring内置的一个可以产生Ehcache的CacheManager对象的FactoryBean。其可以通过属性configLocation指定用于创建CacheManager的Ehcache配置文件的路径,通常是ehcache.xml文件的路径。如果没有指定configLocation,则将使用默认位置的配置文件创建CacheManager,这是属于Ehcache自身的逻辑,即如果在classpath根路径下存在ehcache.xml文件,则直接使用该文件作为Ehcache的配置文件,否则将使用ehcache-xxx.jar中的ehcache-failsafe.xml文件作为配置文件来创建Ehcache的CacheManager。此外,如果不希望创建的CacheManager使用默认的名称(在ehcache.xml文件中定义的,或者是由CacheManager内部定义的),则可以通过cacheManagerName属性进行指定。下面是一个配置EhCacheManagerFactoryBean的示例。

    4.2     EhCacheFactoryBean

    EhCacheFactoryBean是用来产生Ehcache的Ehcache对象的FactoryBean。定义EhcacheFactoryBean时有两个很重要的属性我们可以来指定。一个是cacheManager属性,其可以指定将用来获取或创建Ehcache的CacheManager对象,若未指定则将通过CacheManager.create()获取或创建默认的CacheManager。另一个重要属性是cacheName,其表示当前EhCacheFactoryBean对应的是CacheManager中的哪一个Ehcache对象,若未指定默认使用beanName作为cacheName。若CacheManager中不存在对应cacheName的Ehcache对象,则将使用CacheManager创建一个名为cacheName的Cache对象。此外我们还可以通过EhCacheFactoryBean的timeToIdle、timeToLive等属性指定要创建的Cache的对应属性,注意这些属性只对CacheManager中不存在对应Cache时新建的Cache才起作用,对已经存在的Cache将不起作用,更多属性设置请参考Spring的API文档。此外还有几个属性是对不管是已经存在还是新创建的Cache都起作用的属性:statisticsEnabled、sampledStatisticsEnabled、disabled、blocking和cacheEventListeners,其中前四个默认都是false,最后一个表示为当前Cache指定CacheEventListener。下面是一个定义EhCacheFactoryBean的示例。

    自定义Key生成器

    https://blog.csdn.net/syani/article/details/52239967

    有一个尤其需要注意的坑:Spring默认的SimpleKeyGenerator是不会将函数名组合进key中的

    举个栗子:

    @Componentpublic class CacheTestImpl implementsCacheTest {

    @Cacheable("databaseCache")publicLong test1()

    {return 1L; }

    @Cacheable("databaseCache")publicLong test2()

    {return 2L; }

    @Cacheable("databaseCache")publicLong test3()

    {return 3L; }

    @Cacheable("databaseCache")publicString test4()

    {return "4"; }

    }

    我们期望的输出是:

    1

    2

    3

    4

    而实际上的输出是:

    1

    1

    1

    ClassCastException: java.lang.Long cannot be cast to java.lang.String

    此外,原子类型的数组,直接作为key使用也是不会生效的

    为了解决上述2个问题,自定义了一个KeyGenerator如下:

    @Componentpublic class CacheKeyGenerator implementsKeyGenerator {//custom cache key

    public static final int NO_PARAM_KEY = 0;public static final int NULL_PARAM_KEY = 53;private final Logger LOG = LoggerFactory.getLogger(CacheKeyGenerator.class);

    @OverridepublicObject generate(Object target, Method method, Object... params) {

    StringBuilder key= newStringBuilder();

    key.append(target.getClass().getSimpleName()).append(".").append(method.getName()).append(":");if (params.length == 0) {returnkey.append(NO_PARAM_KEY).toString();

    }for(Object param : params) {if (param == null) {

    LOG.warn("input null param for Spring cache, use default key={}", NULL_PARAM_KEY);

    key.append(NULL_PARAM_KEY);

    }else if(ClassUtils.isPrimitiveArray(param.getClass())) {int length =Array.getLength(param);for (int i = 0; i < length; i++) {

    key.append(Array.get(param, i));

    key.append(',');

    }

    }else if (ClassUtils.isPrimitiveOrWrapper(param.getClass()) || param instanceofString) {

    key.append(param);

    }else{

    LOG.warn("Using an object as a cache key may lead to unexpected results. " +

    "Either use @Cacheable(key=..) or implement CacheKey. Method is " + target.getClass() + "#" +method.getName());

    key.append(param.hashCode());

    }

    key.append('-');

    }

    String finalKey=key.toString();long cacheKeyHash =Hashing.murmur3_128().hashString(finalKey, Charset.defaultCharset()).asLong();

    LOG.debug("using cache key={} hashCode={}", finalKey, cacheKeyHash);returnkey.toString();

    }

    }

    采用此方式后可以解决:多参数、原子类型数组、方法名识别 等问题

    踩坑二,spring中service自己调用自己方法设置缓存是不起作用的。原因是缓存注解借用的是aop面向切面编程实现

    解决方法有二:一在mapper或dao中实现缓存,用service去调用dao;

    @CacheConfig(cacheNames = { "userFocus"})public interfaceUserFocusMapper {

    @CacheEvict(value="userFocusList",key = "#p0.userCode")intinsert(UserFocus userFocus);

    @Cacheable(key= "#p0.userCode+'_'+#p0.focusName+'_'+#p0.focusType",unless="#result == null")

    UserFocus findFocus(UserFocus userFocus);

    @Cacheable(value="userFocusList",key = "#p0.userCode",unless="#result == null||#result.size() == 0")

    ListfindFocusList(UserFocus userCode);//利用@Caching中evict指定两个value下的不同key删除。

    @Caching(evict = {@CacheEvict(value="userFocusList",key = "#p0.userCode"),

    @CacheEvict(value="userFocus",key = "#p0.userCode+'_'+#p0.focusName+'_'+#p0.focusType")})voidupdateUserFocus(UserFocus userFocus);

    }

    二是service自己注入自己

    @Servicepublic class UserFocusServiceImpl implementsUserFocusService {

    @AutowiredprivateUserFocusService userFocusService;

    5.springboot 开启缓存注解

    java类配置:

    @Configuration

    @EnableCaching

    public class AppConfig {

    }

    展开全文
  • Rails的可缓存CSRF令牌 ... CacheableCSRFToken使您可以轻松地缓存Ruby on Rails页面或包含CSRF...将cacheable-csrf-token-rails到您的Gemfile 在ApplicationController中添加以下行: include CacheableCSRFTokenRails
  • cacheable-flash, 获取处理页cacheing的Rails Flash 对象 是一个新的希望这里 gem 不再被维护。 请考虑切换到 github.com/leonid-shevtsov/unobtrusive_flash 。这么长的时间,感谢你 ! CacheableFlash 描述这个...
  • 本篇文章主要介绍了spring整合redis缓存并以注解(@Cacheable、@CachePut、@CacheEvict)形式使用,具有一定的参考价值,有兴趣的可以了解一下。
  • Laravel开发-cacheable-auth-user 为Laravel 5.3缓存auth::user()。这个包为Laravel添加了新的驱动程序。
  • 启动类在加上注解@EnableCaching...@Cacheable的cacheNames可以理解为缓存key的前缀或者一级目录(redis可视化工具下)。 引入依赖: <dependency> <groupId>org.springframework.boot</groupId&g...

    启动类在加上注解 @EnableCaching 开启缓存注解,主要适用于整个接口返回结果需要缓存的场景,其他情况,由于业务场景比较复杂,也就是单独某段代码需要缓存,使用redis的客户端会更加灵活。@Cacheable 的cacheNames可以理解为缓存key的前缀或者一级目录(redis可视化工具下)。

    引入依赖:

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-cache</artifactId>
            </dependency>
            
            <!--这边用的是Redis缓存,所以加上这个依赖-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
            </dependency>
    
    
    @Configuration
    @Slf4j
    public class ActivityRedisConfig extends CachingConfigurerSupport {
    
        @Bean
        @SuppressWarnings(value = {"unchecked", "rawtypes"})
        public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
            RedisTemplate<Object, Object> template = new RedisTemplate<>();
            template.setConnectionFactory(connectionFactory);
    
            FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);
    
            ObjectMapper mapper = new ObjectMapper();
            mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
            serializer.setObjectMapper(mapper);
    
            template.setValueSerializer(serializer);
            // 使用StringRedisSerializer来序列化和反序列化redis的key值
            template.setKeySerializer(new StringRedisSerializer());
            template.afterPropertiesSet();
            return template;
        }
    
    
        /**
         * 使用入参的所有参数作为缓存的key
         * @return
         */
        @Bean
        public KeyGenerator activityKeyGenerator() {
            return (target, method, params) -> {
                System.out.println(params);
                String key = "";
                try {
    // params[0] 为@RequestBody接收的对象,这里只针对这种情况做处理,
    // 如果接口还有其他接收参数的形式,比如@pathvariable,
    //@PathVariable结合@RequestParam等请自行测试再添加相关逻辑代码,这里就没去验证了
                    key = getKey(params[0]);
                } catch (IllegalAccessException e) {
                    log.info("缓存key生成失败", e);
                }
                return key;
            };
        }
    
        /**
         * 自定义缓存管理器
         */
        @Bean
        public RedisCacheManager cacheManager(RedisConnectionFactory factory) {
            RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
            FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);
            // 设置@cacheable 序列化方式,方便可视化工具查看
            config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(serializer)).entryTtl(Duration.ofMinutes(5));
            Set<String> cacheNames = new HashSet<>();
            cacheNames.add("测试");
            cacheNames.add("distributor");
            ConcurrentHashMap<String, RedisCacheConfiguration> configMap = new ConcurrentHashMap<>();
            configMap.put("测试", config.entryTtl(Duration.ofMinutes(1L)));
            configMap.put("distributor", config.entryTtl(Duration.ofMinutes(50L)));
            //需要先初始化缓存名称,再初始化其它的配置。
            RedisCacheManager cacheManager = RedisCacheManager.builder(factory).cacheDefaults(config)
                    .initialCacheNames(cacheNames).withInitialCacheConfigurations(configMap).build();
            return cacheManager;
        }
    
        public String getKey(Object object) throws IllegalAccessException {
            String s = null;
            Field[] fields = object.getClass().getDeclaredFields();
            s = getField(fields, object, s);
            Field[] fieldsSuper = object.getClass().getSuperclass().getDeclaredFields();
            s = getField(fieldsSuper, object, s);
            return s;
        }
    
        public String getField(Field[] fields, Object object, String s) throws IllegalAccessException {
            for (int i = 0; i < fields.length; i++) {
                Field field = fields[i];
                field.setAccessible(true);
                if (field.getName().equals("serialVersionUID")) continue;
                String ss = field.getName() + "_" + String.valueOf(field.get(object));
                s = s == null ? ss : s + "_" + ss;
            }
            return s;
        }
    
    
    }

    方式一:

    
        @PostMapping("users/info")
        @Cacheable(cacheNames = "测试", key = "#user.id", unless = "!#result?.result")
        @CapCode
        public ResultMessage info(@RequestBody User user) {
            System.out.println("controller[url=users/login]");
            return ResultMessage.success(user);
        }

    方式二:自定义keyGenerator

        @PostMapping("users/info")
        @Cacheable(cacheNames = "测试", keyGenerator = "activityKeyGenerator", unless = "!#result?.result")
        @CapCode
        public ResultMessage info(@RequestBody User user) {
            System.out.println("controller[url=users/login]");
            return ResultMessage.success(user);
        }

    其中unless的使用非常重要,决定什么情况下使用缓存,一般是接口查询成功才使用缓存,失败了就不需要缓存,具体使用看文章底部参考文章,另外说一点unless处理boolean类型时,表达式返回结果是false才缓存,上文接口处理成功,unless = "!#result?.result"。

    返回封装如下:

    public class ResultMessage<T> implements Serializable {
        /**
         *
         */
        private static final long serialVersionUID = 3443815263986524969L;
    
        public ResultMessage() {
        }
    
        /**
         * @param result
         * @param code
         * @param msg
         * @param data
         */
        public ResultMessage(Boolean result, int code, String msg, T data) {
            this.result = result;
            this.code = code;
            this.msg = msg;
            this.data = data;
        }
    
        /**
         * 结果,true 成功,false 失败
         */
        private Boolean result = true;
    
        /**
         * 编码
         */
        private int code;
    
        /**
         * 提示信息
         */
        private String msg;
    
        /**
         * 业务数据
         */
        private T data;
    
        /**
         * 成功
         *
         * @param msg
         * @param data
         * @return
         */
        public static <TT> ResultMessage<TT> success(String msg, TT data) {
            return new ResultMessage<TT>(Boolean.TRUE, Constants.SUCCESS_CODE, msg, data);
        }
    
        /**
         * 成功
         *
         * @param data
         * @return
         */
        public static <TT> ResultMessage<TT> success(TT data) {
            return new ResultMessage<TT>(Boolean.TRUE, Constants.SUCCESS_CODE, Constants.RET_SUCCESS_MESSAGE, data);
        }
    
        /**
         * 成功
         *
         * @param msg
         * @return
         */
        public static <TT> ResultMessage<TT> success(String msg) {
            return new ResultMessage<TT>(Boolean.TRUE, Constants.SUCCESS_CODE, msg, null);
        }
    
        /**
         * 失败
         *
         * @param msg
         * @param data
         * @return
         */
        public static <TT> ResultMessage<TT> fail(String msg, TT data) {
            return new ResultMessage<TT>(Boolean.FALSE, Constants.FAIL_CODE, msg, data);
        }
    
        /**
         * 失败
         *
         * @param data
         * @return
         */
        public static <TT> ResultMessage<TT> fail(TT data) {
            return new ResultMessage<TT>(Boolean.FALSE, Constants.FAIL_CODE, Constants.RET_FAIL_MESSAGE, data);
        }
    
        /**
         * 失败
         *
         * @param msg
         * @return
         */
        public static <TT> ResultMessage<TT> fail(String msg) {
            return new ResultMessage<TT>(Boolean.FALSE, Constants.FAIL_CODE, msg, null);
        }
    
        /**
         * 异常
         *
         * @param msg
         * @param data
         * @return
         */
        public static <TT> ResultMessage<TT> error(String msg, TT data) {
            return new ResultMessage<TT>(Boolean.FALSE, Constants.ERROR_CODE, msg, data);
        }
    
        /**
         * 异常
         *
         * @param data
         * @return
         */
        public static <TT> ResultMessage<TT> error(TT data) {
            return new ResultMessage<TT>(Boolean.FALSE, Constants.ERROR_CODE, Constants.RET_ERROR_MESSAGE, data);
        }
    
        /**
         * 异常
         *
         * @param msg
         * @return
         */
        public static <TT> ResultMessage<TT> error(String msg) {
            return new ResultMessage<TT>(Boolean.FALSE, Constants.ERROR_CODE, msg, null);
        }
    
        /**
         * 返回结果
         *
         * @param result
         * @param code
         * @param msg
         * @param data
         * @return
         */
        public static <TT> ResultMessage<TT> result(Boolean result, int code, String msg, TT data) {
            return new ResultMessage<TT>(result, code, msg, data);
        }
    
        public Boolean getResult() {
            return result;
        }
    
        public void setResult(Boolean result) {
            this.result = result;
        }
    
        public int getCode() {
            return code;
        }
    
        public void setCode(int code) {
            this.code = code;
        }
    
        public String getMsg() {
            return msg;
        }
    
        public void setMsg(String msg) {
            this.msg = msg;
        }
    
        public T getData() {
            return data;
        }
    
        public void setData(T data) {
            this.data = data;
        }
    
        @Override
        public String toString() {
            return "ResultMessage{" +
                    "result=" + result +
                    ", code=" + code +
                    ", msg='" + msg + '\'' +
                    ", data=" + data +
                    '}';
        }
    }
    
    public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T> {
        @SuppressWarnings("unused")
        private ObjectMapper objectMapper = new ObjectMapper();
    
        public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
    
        private Class<T> clazz;
    
        static {
            ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
        }
    
        public FastJson2JsonRedisSerializer(Class<T> 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 JSON.parseObject(str, clazz);
        }
    
        public void setObjectMapper(ObjectMapper objectMapper) {
            Assert.notNull(objectMapper, "'objectMapper' must not be null");
            this.objectMapper = objectMapper;
        }
    
        protected JavaType getJavaType(Class<?> clazz) {
            return TypeFactory.defaultInstance().constructType(clazz);
        }
    }
    

    参考:

    redis@Cacheable注解unless用法

    Springboot 之 @Cacheable 各种序列化及有效时间设置

    展开全文
  • 可缓存请求 ... 缓存可以直接在内存中使用,也可以轻松地与各种存储适配器插入。 ... 产品特点 仅存储RFC 7234定义的可缓存响应 新鲜的缓存条目直接从缓存中提供 使用If-None-Match / If-Modified-Since标头重新验证陈旧的...
  • @Cacheable使用详解

    2021-11-22 17:53:25
     @Cacheable 注解在方法上,表示该方法的返回结果是可以缓存的。也就是说,该方法的返回结果会放在缓存中,以便于以后使用相同的参数调用该方法时,会返回缓存中的值,而不会实际执行该方法。  注意,这里强调了...

    1. 功能说明

      @Cacheable 注解在方法上,表示该方法的返回结果是可以缓存的。也就是说,该方法的返回结果会放在缓存中,以便于以后使用相同的参数调用该方法时,会返回缓存中的值,而不会实际执行该方法。

      注意,这里强调了一点:参数相同。这一点应该是很容易理解的,因为缓存不关心方法的执行逻辑,它能确定的是:对于同一个方法,如果参数相同,那么返回结果也是相同的。但是如果参数不同,缓存只能假设结果是不同的,所以对于同一个方法,你的程序运行过程中,使用了多少种参数组合调用过该方法,理论上就会生成多少个缓存的 key(当然,这些组合的参数指的是与生成 key 相关的)。下面来了解一下 @Cacheable 的一些参数:

    2. cacheNames & value

      @Cacheable 提供两个参数来指定缓存名:value、cacheNames,二者选其一即可。这是 @Cacheable 最简单的用法示例:

    @Override
    @Cacheable("menu")
    public Menu findById(String id) {
        Menu menu = this.getById(id);
        if (menu != null){
            System.out.println("menu.name = " + menu.getName());
        }
        return menu;
    }

      在这个例子中,findById 方法与一个名为 menu 的缓存关联起来了。调用该方法时,会检查 menu 缓存,如果缓存中有结果,就不会去执行方法了。

    3. 关联多个缓存名

      其实,按照官方文档,@Cacheable 支持同一个方法关联多个缓存。这种情况下,当执行方法之前,这些关联的每一个缓存都会被检查,而且只要至少其中一个缓存命中了,那么这个缓存中的值就会被返回。示例:

    @Override
        @Cacheable({"menu", "menuById"})
        public Menu findById(String id) {
            Menu menu = this.getById(id);
            if (menu != null){
                System.out.println("menu.name = " + menu.getName());
            }
            return menu;
        }
    
    ---------
    @GetMapping("/findById/{id}")
    public Menu findById(@PathVariable("id")String id){
        Menu menu0 = menuService.findById("fe278df654adf23cf6687f64d1549c0a");
        Menu menu2 = menuService.findById("fb6106721f289ebf0969565fa8361c75");
        return menu0;
    }

    4. key & keyGenerator

      一个缓存名对应一个被注解的方法,但是一个方法可能传入不同的参数,那么结果也就会不同,这应该如何区分呢?这就需要用到 key 。在 spring 中,key 的生成有两种方式:显式指定和使用 keyGenerator 自动生成。

      4.1. KeyGenerator 自动生成

        当我们在声明 @Cacheable 时不指定 key 参数,则该缓存名下的所有 key 会使用 KeyGenerator 根据参数 自动生成。spring 有一个默认的 SimpleKeyGenerator ,在 spring boot 自动化配置中,这个会被默认注入。生成规则如下:

          a. 如果该缓存方法没有参数,返回 SimpleKey.EMPTY ;

          b. 如果该缓存方法有一个参数,返回该参数的实例 ;

          c. 如果该缓存方法有多个参数,返回一个包含所有参数的 SimpleKey ;

        默认的 key 生成器要求参数具有有效的 hashCode() 和 equals() 方法实现。另外,keyGenerator 也支持自定义, 并通过 keyGenerator 来指定。关于 KeyGenerator 这里不做详细介绍,有兴趣的话可以去看看源码,其实就是使用 hashCode 进行加乘运算。跟 String 和 ArrayList 的 hash 计算类似。

      4.2. 显式指定 key

        相较于使用 KeyGenerator 生成,spring 官方更推荐显式指定 key 的方式,即指定 @Cacheable 的 key 参数。

        即便是显式指定,但是 key 的值还是需要根据参数的不同来生成,那么如何实现动态拼接呢?SpEL(Spring Expression Language,Spring 表达式语言) 能做到这一点。下面是一些使用 SpEL 生成 key 的例子。

    @Override
        @Cacheable(value = {"menuById"}, key = "#id")
        public Menu findById(String id) {
            Menu menu = this.getById(id);
            if (menu != null){
                System.out.println("menu.name = " + menu.getName());
            }
            return menu;
        }
    
        @Override
        @Cacheable(value = {"menuById"}, key = "'id-' + #menu.id")
        public Menu findById(Menu menu) {
            return menu;
        }
    
        @Override
        @Cacheable(value = {"menuById"}, key = "'hash' + #menu.hashCode()")
        public Menu findByHash(Menu menu) {
            return menu;
        }

    注意:官方说 key 和 keyGenerator 参数是互斥的,同时指定两个会导致异常

    5. cacheManager & cacheResolver

      CacheManager,缓存管理器是用来管理(检索)一类缓存的。通常来讲,缓存管理器是与缓存组件类型相关联的。我们知道,spring 缓存抽象的目的是为使用不同缓存组件类型提供统一的访问接口,以向开发者屏蔽各种缓存组件的差异性。那么  CacheManager 就是承担了这种屏蔽的功能。spring 为其支持的每一种缓存的组件类型提供了一个默认的 manager,如:RedisCacheManager 管理 redis 相关的缓存的检索、EhCacheManager 管理 ehCache 相关的缓等。

      CacheResolver,缓存解析器是用来管理缓存管理器的,CacheResolver 保持一个 cacheManager 的引用,并通过它来检索缓存。CacheResolver 与 CacheManager 的关系有点类似于 KeyGenerator 跟 key。spring 默认提供了一个 SimpleCacheResolver,开发者可以自定义并通过 @Bean 来注入自定义的解析器,以实现更灵活的检索。

      大多数情况下,我们的系统只会配置一种缓存,所以我们并不需要显式指定 cacheManager 或者 cacheResolver。但是 spring 允许我们的系统同时配置多种缓存组件,这种情况下,我们需要指定。指定的方式是使用 @Cacheable 的 cacheManager 或者 cacheResolver 参数。

      注意:按照官方文档,cacheManager 和 cacheResolver 是互斥参数,同时指定两个可能会导致异常。

    6. sync

      是否同步,true/false。在一个多线程的环境中,某些操作可能被相同的参数并发地调用,这样同一个 value 值可能被多次计算(或多次访问 db),这样就达不到缓存的目的。针对这些可能高并发的操作,我们可以使用 sync 参数来告诉底层的缓存提供者将缓存的入口锁住,这样就只能有一个线程计算操作的结果值,而其它线程需要等待,这样就避免了 n-1 次数据库访问。

      sync = true 可以有效的避免缓存击穿的问题。

    7. condition

      调用前判断,缓存的条件。有时候,我们可能并不想对一个方法的所有调用情况进行缓存,我们可能想要根据调用方法时候的某些参数值,来确定是否需要将结果进行缓存或者从缓存中取结果。比如当我根据年龄查询用户的时候,我只想要缓存年龄大于 35 的查询结果。那么 condition 能实现这种效果。

      condition 接收一个结果为 true 或 false 的表达式,表达式同样支持 SpEL 。如果表达式结果为 true,则调用方法时会执行正常的缓存逻辑(查缓存-有就返回-没有就执行方法-方法返回不空就添加缓存);否则,调用方法时就好像该方法没有声明缓存一样(即无论传入了什么参数或者缓存中有些什么值,都会执行方法,并且结果不放入缓存)

    8. unless

      执行后判断,不缓存的条件。unless 接收一个结果为 true 或 false 的表达式,表达式支持 SpEL。当结果为 true 时,不缓存

    可以看到,两次调用的结果都没有缓存。说明在这种情况下,condition 比 unless 的优先级高。总结起来就是:

        condition 不指定相当于 true,unless 不指定相当于 false

        当 condition = false,一定不会缓存;

        当 condition = true,且 unless = true,不缓存;

        当 condition = true,且 unless = false,缓存;

    展开全文
  • 这是已弃用的在此处使用该库: : 见
  • Cacheable注解使用详解

    千次阅读 2021-01-17 16:07:32
    最简单的方式就是使用springframe为我们提供的@Cacheable注解,以下是@Cacheable注解的具体使用方式。@Cacheable注解参数解读示例@Cacheable(value = CommonRedisKey.IndexRedisKey.INDEX_FOCUS_LIST, key = "'" + ...

    完成Redis基础配置之后,就可以使用Redis对数据进行缓存了。

    最简单的方式就是使用springframe为我们提供的@Cacheable注解,以下是@Cacheable注解的具体使用方式。

    @Cacheable注解参数解读

    示例

    @Cacheable(value = CommonRedisKey.IndexRedisKey.INDEX_FOCUS_LIST, key = "'" + CommonRedisKey.IndexRedisKey.INDEX_FOCUS_LIST + "_' + #channel")

    value

    用来存放我们要保存的key的集合。类似我们之前定义的"uiset",类型为标准的String

    key

    我们实际要保存到redis的key,可以增加参数,以方法的参数或者属性。类型为String,但是需要做处理。

    需要将我们自定义的字符串以"'"括起来再与参数进行拼接。如果需要用到方法中的参数,可以用 #+参数名直接获

    取。如果需要用到方法中参数的属性,可以向Java对象一样,用 . 获取。如 #channel.name。

    condition

    触发条件。这个参数是规定这个缓存触发的条件拼接。如 condition="#channel != null",就是在channel不

    为null的时候触发。

    unless

    排除条件。这个参数是规定这个缓存在什么时候不处罚。如 unless="#result == null",就是在结果为null的

    时候触发。

    缓存设置失效时间

    在之前Redis基础配置中,有一个地方是配置缓存默认失效时间的。

    //设置缓存过期时间

    cacheManager.setDefaultExpiration(30);

    这里是将缓存默认的失效设置为30秒,但是我们有的数据需要单独配置,配置方法如下:

    //针对key单独设置过期时间

    Map expireMap = new HashMap();

    expireMap.put(CommonRedisKey.IndexRedisKey.INDEX_AD_LIST, 5 * 60L);

    cacheManager.setExpires(expireMap);

    在RedisConfig.java类,cacheManager方法中增加如下配置。增加后的RedisConfig.java如下:

    package com.shanyuan.platform.ms.base.cache.config;

    import java.util.HashMap;

    import java.util.Map;

    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.data.redis.cache.RedisCacheManager;

    import org.springframework.data.redis.connection.RedisConnectionFactory;

    import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;

    import org.springframework.data.redis.core.RedisTemplate;

    import org.springframework.data.redis.core.StringRedisTemplate;

    import org.springframework.data.redis.serializer.StringRedisSerializer;

    import com.shanyuan.platform.ms.base.cache.serializer.FastJson2JsonRedisSerializer;

    import com.shanyuan.platform.ms.base.common.CommonRedisKey;

    import redis.clients.jedis.JedisPoolConfig;

    @Configuration

    @EnableCaching

    public class RedisConfig extends CachingConfigurerSupport{

    //缓存管理器

    @Bean

    public CacheManager cacheManager(@SuppressWarnings("rawtypes") RedisTemplate redisTemplate) {

    RedisConnectionFactory factory = redisTemplate.getConnectionFactory();

    RedisCacheManager cacheManager = null;

    if(factory instanceof JedisConnectionFactory) {

    JedisConnectionFactory jcf = (JedisConnectionFactory) factory;

    JedisPoolConfig npc = (JedisPoolConfig) jcf.getPoolConfig().clone();

    JedisConnectionFactory njcf= new JedisConnectionFactory(npc);

    njcf.setHostName(jcf.getHostName());

    njcf.setPort(jcf.getPort());

    njcf.setPassword(jcf.getPassword());

    njcf.setTimeout(jcf.getTimeout());

    njcf.setDatabase(0);

    njcf.setUsePool(true);

    njcf.afterPropertiesSet();

    @SuppressWarnings("rawtypes")

    RedisTemplate ntemplate = new StringRedisTemplate(njcf);

    setSerializer(ntemplate);//设置序列化工具

    ntemplate.afterPropertiesSet();

    cacheManager = new RedisCacheManager(ntemplate);

    }

    if(cacheManager==null) {

    cacheManager = new RedisCacheManager(redisTemplate);

    }

    //设置缓存过期时间

    cacheManager.setDefaultExpiration(30);

    //针对key单独设置过期时间

    Map expireMap = new HashMap();

    expireMap.put(CommonRedisKey.IndexRedisKey.INDEX_AD_LIST, 5 * 60L);

    expireMap.put(CommonRedisKey.IndexRedisKey.INDEX_FOCUS_LIST, 5 * 60L);

    expireMap.put(CommonRedisKey.GoodsFilterRedisKey.DACS_SUPPORT_AREA_LIST, 24 * 60 * 60L);

    expireMap.put(CommonRedisKey.IndexRedisKey.INDEX_HELP_GOODS, 6 * 60 * 60L);

    expireMap.put(CommonRedisKey.IndexRedisKey.INDEX_SPECIAL_GOODS, 30 * 60L);

    expireMap.put(CommonRedisKey.IndexRedisKey.INDEX_UNIONITEM_GOODS, 6 * 60 * 60L);

    expireMap.put(CommonRedisKey.BizGoodsClass.BIZ_GOODS_CLASS_SET, 6 * 60 * 60L);

    expireMap.put(CommonRedisKey.GoodsFilterRedisKey.DACS_GOODS_CLASS + "_set", 6 * 60 * 60L);

    expireMap.put(CommonRedisKey.GoodsFilterRedisKey.DACS_SUPPORT_AREA_LIST, 6 * 60 * 60L);

    cacheManager.setExpires(expireMap);

    return cacheManager;

    }

    public RedisTemplate redisTemplate(RedisConnectionFactory factory){

    if(factory instanceof JedisConnectionFactory) {

    JedisConnectionFactory jcf = (JedisConnectionFactory) factory;

    jcf.setDatabase(3);

    }

    StringRedisTemplate template = new StringRedisTemplate(factory);

    setSerializer(template);//设置序列化工具

    template.afterPropertiesSet();

    return template;

    }

    private void setSerializer(RedisTemplate template){

    FastJson2JsonRedisSerializer fastJsonRedisSerializer = new FastJson2JsonRedisSerializer(Object.class);template.setValueSerializer(fastJsonRedisSerializer);template.setKeySerializer(new StringRedisSerializer());}}```* 这里用Map<String, Long>,其key是@Cacheable注解中的 value 属性, value是要是设置的有效期,单位为秒。* 配置完之后,需要将配置应用到项目中,必须执行这行代码,否则配置是不生效的。```cacheManager.setExpires(expireMap);```----------------------------------------#### 注意!!!* 在使用这个方式对数据进行缓存的时候,还需要注意一下几点。||注意事项||-----|:-----:|-----:|| 1. | 如果缓存的类的构造器为有参构造时,必须保证该类有无参构造 || 2. | key与value属性为必填属性,且值不能相同 || 3. | key拼接的时候注意使用 ' ,否则无法解析 || 4. | 尽量使用unless或者condition去限制缓存触发机制,防止缓存中进入无效数据 || 5. | 尽量对自定义的缓存进行expire配置,即过期时间,每种数据需要的缓存时间可能是不一样的,尽量单独配置 || 6. | 配置类中expireMap的key,是@Cacheable注解中 value 属性,不需要对key设置时效,这么做就够了 |----------------------------------------

    展开全文
  • } @Caching @Caching 用于定义复杂的缓存规则,可以集成@Cacheable和 @CachePut // @Caching 定义复杂的缓存规则 @Caching( cacheable = { @Cacheable(/*value={"emp"},*/key = "#lastName") }, put = { @CachePut...
  • cacheable注解

    2021-01-12 19:24:45
    {"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],"search_count":[{"count_phone":4,"count":4}]},"card":[{"des":"阿里云数据库专家保驾护航,为用户的数据库应用系统进行性能和风险评估,参与配合进行...
  • } @Caching# @Caching 用于定义复杂的缓存规则,可以集成@Cacheable和 @CachePut // @Caching 定义复杂的缓存规则 @Caching( cacheable = { @Cacheable(/*value={"emp"},*/key = "#lastName") }, put = { @CachePut...
  • Spring中的@Cacheable开销

    2020-05-10 09:15:39
    Spring 3.1引入了很棒的缓存抽象层 。 最后,我们可以放弃所有本地化的方面,装饰器和污染我们...@Cacheable("books") public Book findBook(ISBN isbn) {...} "books"是一个缓存名称, isbn参数成为缓存键,返回的...
  • 1、背景咱们在项目当中... 注解方式spring cache系列注解经过切面自定义注解实现因为spring @Cacheable提供了多种缓存实现,有的缓存是不支持ttl的,所以@Cacheable注解当中是没有设置ttl参数的,Redis实现的Cach...
  • springboot-Cacheable

    千次阅读 2019-04-10 16:05:17
    其核心主要是@Cacheable和@CacheEvict。使用@Cacheable标记的方法在执行后Spring Cache将缓存其返回结果,而使用@CacheEvict标记的方法会在方法执行前或者执行后移除Spring Cache中的某些元素。下面我们将来详细介绍...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 24,185
精华内容 9,674
关键字:

cacheable

友情链接: stm8l151c8t6+atom.rar