Spring framework(8):Cache 缓存处理

本文详细介绍了Spring Cache缓存处理,包括基本概念如缓存命中率和过期策略,以及Spring Cache的使用示例、缓存注解、缓存管理器和常用缓存技术(如EhCache、Guava、HazelCast)的集成配置。讨论了并发场景下可能存在的问题,并指出Spring Cache在分布式环境下的局限性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

缓存的基本概念

通常在 Web 应用发开中,不同层级对应的缓存策略和要求是不一样的,如下:


以下是缓存中的2个比较重要的基本概念:
  • 缓存命中率
缓存命中率即从缓存中读取数据的次数和总读取次数的比例,一般来说,命中率越高越好:
命中率 = 从缓存中读取的次数 / 总读取次数
Miss 率 = 没有从缓存中读取的次数 / 总读取次数
※总读取次数 = 从缓存中读取的次数 + 从慢速设备中读取的次数
  • 缓存过期策略
如果缓存满了,缓存中移除数据的策略,常见的策略有: LFU、LRU、FIFO 等;
    • FIFO(First In First Out):先进先出策略,先放入缓存的数据先被移除;
    • LRU(Least Recently Used):最久未使用策略,使用距离现在最久的那个数据被移除;
    • LFU(Least Frequently Used):最近最少使用策略,一定时间内使用次数最少的那个数据被移除;
    • TTL(Time To Live):固定存活期策略,从缓存中创建时间点开始直到到期的一个时间段,不管在该时间是否被访问否将过期;
    • TTI(Time To Idle):固定空闲期策略,一个数据在指定的时间内没有被访问,就会从缓存中被移除;

Spring 4.0 开始全面支持 JSR-107(JCache API),提供了一种可以在方法级别进行缓存的缓存抽象,主要的实现过程是在缓存注解类中进行相关方法的标记,Spring 通过 AOP 功能为其生成具有缓存功能的代理类;

使用 Spring Cache 时有以下几点要注意:
  • Spring Cache 提供的仅仅是一种抽象,而没有提供具体实现,所以一般会自己使用 AOP 来进行一定程度的封装实现(或者使用第三方的缓存框架);
  • Spring Cache 并不针对多进程的应用环境进行专门的处理,即当应用程序处于分布式或集群环境下时,需要针对具体的缓存进行相应的配置;
  • Spring Cache 抽象的操作中并没有锁的概念,当多线程并发操作同一个缓存项时,将可能得到过期的数据,此时可以自己实现或借助第三方缓存框架来实现并发锁;




Spring Cache 缓存处理


一个简单的使用示例

Spring Cache 的基本使用主要包括以下2个步骤:
  • 定义缓存:确定需要缓存的方法和缓存策略;
  • 缓存配置:配置缓存信息;
以下示例完整代码地址:https://gitee.com/assad/springframework-test-cache

在业务层对象 UserService 使用注解标注缓存行为:
 
package site.assad.service;
@Service
public class UserService {
    @Autowired
    private UserDao userDao;
    private final static Logger log = LogManager.getLogger();
    //标记方式使用缓存,当调用该方法时,会先从 users 缓存中匹配查询缓存对象,如果匹配则直接返回缓存对象,否则执行方法内的逻辑
    //在这个方法中,对应浑春的 key 为 userId 的值,value 为 userId 对应的 User 对象;
    @Cacheable(cacheNames = "users")
    public User getUser(final int userid){
        log.debug("real query user from DB");
        //不考虑缓存逻辑,直接实现业务
        User user = null;
        user = userDao.getUserById(userid);
        return user;
    }
}
在Spring上下文配置文件 applicationContext.xml 中配置 Spring Cache:
 
<!--Spring Cache 配置-->
<!--启动基于注解的缓存驱动,该驱动会默认使用一个定义为“cacheManager”的缓存管理器-->
<cache:annotation-driven />
<!--指定使用缓存注解的类,多个类可以使用内嵌的list节点包含-->
<bean id="accountServiceBean" class="site.assad.service.UserService" />
<!--配置缓存管理器,使用默认实现SimpleCacheManager-->
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
    <property name="caches">
        <list>
            <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="default" />
            <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="users" />
        </list>
    </property>
</bean>




缓存注解

Spring 4.X 定义了4个可在方法级别、1个类级别上使用的注解,用于定义在某方法上的返回值会被缓存、或者从缓存中移除;

@Cacheable  

这两个注解用于定制被注解的方法的返回值是可以被缓存的,工作原理是 Spring 首先从缓存中查找数据,如果没有则执行方法并缓存结果,再返回数据;
@Cacheable 拥有的属性如下:
属性参数说明使用示例
value
/ cacheNames
缓存的名称,在 Spring 配置文件中定义,必须指定; @Cacheable(cacheNames = "users")
或标明多个参数时 :
@Cacheable(cacheNames = {"users",“members”})
key缓存的 key,可以为空;
如果指定,则按照 SpEL 表达式编写;
如果没有指定,则默认按照所有参数进行组合;
@Cacheable(cacheNames = "users",key="#userId")
condition
/ unless
condition 为缓存的条件,可以为空,使用 SpEL 表达式进行编写;
unless 与 condition 相反,为不为缓存的条件;
@Cacheable(cacheNames = "users",condition="#userAge>20")
使用示例如下:
 
 @Cacheable(cacheNames = "users",key = "#userId")
    public User getUser(final int userid){
        log.debug("real query user from DB");
        User user = null;
        user = userDao.getUserById(userid);
        return user;
    }

缓存的本质是 key/value 集合,默认情况下,缓存抽象使用方法签名和参数值作为一个键值,并将该键与方法调用的结果组成 key/value,如果在 Cache 注解上没有指定 key,Spring 会使用默认的 KeyGenerator 来生成一个key,在 Spring 4.X ,该默认的构造器为 SimpleKeyGenerator ,如果需要使用到自定义的键构造器,可以实现 KeyGenerator 接口来实现一个键构造器,然后在 keyGenerator 中指定该构造器,如下:
@Cacheable(cacheNames = "users",keyGenertor="myKeyGenerator")

@CachePut

和 @Cacheable 类似,用于指定被注解的方法的返回值可以被缓存,但是 这两个注解不能同时用在用一个方法上,这2个注解的区别是:@Cacheable 可以跳过方法直接获取缓存,@CahePut 则会强制执行方法以更新注解;
除此之外,@CachePut 的参数和 @Cacheable 一样,都拥有 value/cacheNames、key、condition 等属性;

@CacheEvict

@CacheEvict 是 @Cacheable 的方向操作,即从给定的缓存中移除一个值,该注解通常用在更新或删除用户的操作;
大多数缓存框架都提供了缓存数据的有效期,使用该注解可以显式从缓存中删除失效的缓存数据;
@CacheEvict 除了拥有 @Chacheable 的属性 value/chaceNames、key、condition 之外,还拥有以下属性:
属性参数说明
allEntries标记是否需要清空缓存中的所有元素,默认为 false;
如果为true,Spring 将忽略指定的 key,清除缓存中的所有内容;
beforeInvocation标记清除缓存操作的执行时间点,默认为 false;
一般清除操作默认实在执行方法成功后触发的,该注解可以重新指定清除操作的执行时间点;
当为 true 时,Spring 会在调用方法之前清除缓存中的指定元素;
一个示例的使用如下:
 
 @CacheEvict(cacheNames = "users",key="#userId")
 public void removeUserFromCache(int userId){
    userDao.removeUserById(userId);
    log.debug("remove user "+ userId + " from cache");
 }

@Caching

@Caching 是一个组注解,可以为一个方法提供定义基于 @Cacheable/@CachePut、@CacheEvict 注解的数组,如下使用示例:
 
@Service
public class UserService {
    @Caching(cacheable = {
            @Cacheable(value = "members", condition = "#obj instanceof T(site.assad.domain.Member)"),
            @Cacheable(value = "visitors", condition = "#obj instanceof T(site.assad.domain.Visitor)")
            })
    public User getUser(User obj) {
        return userDao.getUser(obj);
    }
}
以上示例中,Member 和 Visitor 都是 User 的子类, 对于 getUser 方法的返回结果,根据返回类型的判定,将缓存到 memebers,visitors 两个不同的缓存中;

@CacheConfig

@CacheConfig 是 Spring 4.0 新增加的缓存注解,用于定义类级别的全局缓存,用于定义在类中重复使用的默认Cache注解,如下:
 
@CacheConfig(cacheNames = "users",key="#userId")
public class UserService {
        @Cacheable
        public User findA(User user) {
            .....
        }
        @Cacheable
        public User findB(User user) {
            .....
        }
}
在以上的示例代码中 ,@CacheConfig 在类级别定义了缓存 users 、key,在 findA、findB 方法中使用@Cacheable中不指定参数,会默认使用 @CahceConfig 中的参数;

缓存注解中 SpEL 表达式的使用

Spring Cache 缓存注解属性 key、condition、unless 中,指定值是使用 SpEL 表达式的,SpEL 表达式可以基于上下文并通过使用缓存抽象,提供与 root 对象相关联的缓存特定参数,如下:
名称位置说明示例
methodName
root 对象
当前被调用的方法名
#root.methodName
methodroot 对象
当前被调用的方法#root.method.name
targetroot 对象
当前被调用的目标对象实例#root.target
targetClassroot 对象
当前被调用的目标对象的Class对象
#root.targetClass
argsroot 对象
当前被调用的方法的参数列表
#root.args[0]
cachesroot 对象
当前方法调用使用的缓存列表#root.caches[0].name
Argument Name执行上下文当前被调用的方法的参数@Cacheable(value = "users" , key = "#userId")
public User getUser(final int userId){  
.......
}
@Cacheable(value = "users" , key = "#user.id")
public User getUser(final User user){  
.......
}
result执行上下文方法执行后的返回值(仅仅当方法执行后的判断有效,如 boforeInvocation=false)#result



缓存管理器

CacheManager 是提供了访问缓存名称和缓存对象方法、提供管理缓存、操作缓存、移除缓存方法具体实现的 SPI(Service Provider Interafce,服务提供程序接口);
Spring Cache 框架对于各种不同的需求,提供了不同的缓存管理器实现;

SimpleCacheManager

SimpleCacheManager 是缓存管理器的简化版本,可以配置缓存列表,并利用这些缓存列表进行相关的操作;
以下实例配置中利用 ConcurrentMapCacheFactoryBean 类来对 ConcurrentMapCache 进行实例化;
<cache:annotation-driven />
<bean id="accountServiceBean" class="site.assad.service.UserService" />
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
    <property name="caches">
        <list>
            <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="default" />
            <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="users" />
        </list>
    </property>
</bean>

NoOpCacheManager

NoOpCacheManager 主要用于测试目的,该缓存管理器实际上不缓存任何数据,示例配置如下:
 
<bean id="cahceManager" class="org.springframework.cache.support.NoOpCacheManager" />

ConcurrentMapCacheManager

ConcurrentMapCacheManager 使用了 JDK 的 ConcurrentMap 管理缓存队列,于 SimpleCacheManager 类似,当不需要定义缓存,如下:
<bean id="cahceManager" class="org.springframework.cache.concurrent.ConcurrentMapCacheManager" />

CompositeCacheManager

当应用程序上下文中定义 <cache:annotation-driven /> 时,只能提供一个缓存管理器,使用 CompositeCacheManager 能够定义多个缓存管理器,同时 CompositeCacheManager  还可以通过 fallbackToNoOpCache 属性放hi NoOpCacheManager ;
以下示例中通过 CompositeCacheManager ,及拿过一个 SimleCacheManager 和 一个 HazelCast 缓存管理器宽版在一起,SimleCacheManager  管理 members 缓存,HazelCast 管理 visitors 缓存;
 
<cache:annotation-driven />
<bean id="accountServiceBean" class="site.assad.service.UserService" />
<bean id="cacheManager" class="org.springframework.cache.support.CompositeCacheManager">
    <property name="cacheManagers">
        <list>
            <!--定义SimpleCacheManager管理器-->
            <bean class="org.springframework.cache.support.SimpleCacheManager">
                <property name="caches">
                    <list>
                      <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="members" />
                    </list>
                </property>
            </bean>
            <!--定义HazelCast管理器-->
            <bean class="com.hazelcast.spring.cache.HazelcastCacheManager">
                <constructor-arg ref="hazelcast" />
            </bean>
        </list>
    </property>
</bean>



使用基于 XML 的 Cache 声明

在某些情况下,由于某些原因无法获取项目的源码,可以使用 XML 的方式配置 Spring Cache,配置方式于 transaction 管理器的 advice 类似,如下:
 
<!--需要定义缓存的类-->
<bean id="userService" class="site.assad.service.UserService" />
<!--缓存增强定义-->
<cache:advice id="cacheAdvice" cache-manager="cacheManager">
    <cache:caching cache="users">
        <cache:cacheable method="getUser" key="#userId" />
        <cache:cache-evict method="removeUser" key="#userId"/>
    </cache:caching>    
</cache:advice>
<!--缓存切面定义-->
<aop:config>
    <aop:advisor advice-ref="cacheAdvice" pointcut="execution(*site.assad.service.UserService.*(..))" />
</aop:config>






常用Cache技术的集成配置

对于常用的第三方缓存框架,Spring 提供了对他们的集成机制;

EhCache

EhCache 是广泛使用的 Java 开源缓存框架之一,使用 EhCache ,需要在项目中导入以下依赖:
net.sf.ehcach:ehcahe
在 Spring 上下文配置文件 applicationContext 文件中的配置如下:
<cache:annotation-driven/>
<context:component-scan base-package="site.assad.service"/>
<!--配置EhCacheCacheManager缓存管理器-->
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager"
       p:cacheManager="ehcache"/>
<bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"
          p:configLocation="classpath:ehcache.xml"/>
之后在 EhCache 配置文件中中配置缓存bean,ehcache.xml
 
<ehcache>
    <cache name="users" maxElementsInMemory="1000" />
</ehcache>

Guava

Guava 是 Google 的一个开源公共库,他提供了缓存的功能,需要导入以下依赖:
com.google.guava:guava
一个简单的配置如下,并不需要定义缓存,因为它们将在被需要时被创建;
 
<bean id="cacheManager" class="org.springframework.cache.guava.GuavaCacheManager" />

HazelCast

HazelCast 是流行的 Java 分布式内存网格框架之一,使用 HazelCast 需要导入以下依赖:
com.hazelcast:hazelcast-all
在 Spring 上下文定义 HazelCast 缓存管理器如下:
 
<cache:annotation-driven/>
<context:component-scan base-package="com.smart.cache.hazelcast"/>
<!--定义HazelCast Bean-->
<hz:hazelcast id="hazelcast">
   <hz:config>
       <hz:map name="users">
           <hz:map-store enabled="true" class-name="site.assad.domain.User" write-delay-seconds="0"/>
       </hz:map>
   </hz:config>
</hz:hazelcast>
<!--定义HazelCast缓存管理器-->
<bean id="cacheManager" class="com.hazelcast.spring.cache.HazelcastCacheManager">
    <constructor-arg ref="hazelcast" />
</bean>








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值