缓存数据

缓存( Caching )可以存储经常会用到的信息,这样每次需要的时候,这些信息都是立即可用的。我们将会了解到 Spring 的缓存抽象。尽管 Spring 自身并没有实现缓存解决方案,但是它对缓存功能提供了声明式的支持,能够与多种流行的缓存实现进行集成

开启缓存支持

  • 注解开启:@EnableCaching
  • 配置开启: <cache:annotation-driven>

两种开启方法的工作方式是相同的。它们都会创建一个切面( aspect )并触发Spring 缓存注解的切点( pointcut )。根据所使用的注解以及缓存的状态,这个切面会从缓存中获取数据,将数据添加到缓存之中或者从缓存中移除某个值。

import org.springframework.cache.annotation.EnableCaching;
@Configuration
@EnableCaching
public class CacheConfig {
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cache="http://www.springframework.org/schema/cache"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
     <!--启用缓存注解功能,这个是必须的,否则注解不会生效-->
     <!--有一个cache-manager属性用来指定当前所使用的CacheManager对应的bean的名称,默认是cacheManager-->
     <cache:annotation-driven/>
</beans>

配置缓存管理器

缓存管理器:是 Spring 缓存抽象的核心,它能够与多个流行的缓存实现进行集成

Spring内置了多个缓存管理器:

  • SimpleCacheManager
  • NoOpCacheManager
  • ConcurrentMapCacheManager----------默认使用的缓存管理器
  • CompositeCacheManager
  • EhCacheCacheManager-------------------较常用
  • RedisCacheManager (来自于 Spring Data Redis 项目)-----------------较常用
  • GemfireCacheManager (来自于 Spring Data GemFire 项目)

1、配置ConcurrentMapCacheManager

①注解配置

import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
@Configuration
@EnableCaching
public class ConcurrentMapCacheConfig {
    @Bean
    public CacheManager cacheManager1(){
        return new ConcurrentMapCacheManager();
    }
}

②XML配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cache="http://www.springframework.org/schema/cache"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
    <cache:annotation-driven/>
    <!--定义缓存管理器-->
    <bean id="cacheManager" class="org.springframework.cache.concurrent.ConcurrentMapCacheManager">
    </bean>
</beans>

2、配置EhCacheCacheManager

EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider

缓存数据有三级:内存、堆外缓存Off-Heap、Disk缓存,因此无需担心容量问题。还可以通过RMI、可插入API等方式进行分布式缓存。

缓存数据会在虚拟机重启的过程中写入磁盘,持久化。

具有缓存和缓存管理器的侦听接口。

支持多缓存管理器实例,以及一个实例的多个缓存区域。

由于Spring只是提供了基本的管理器接口,还需要依赖于ehcache的第三方cache实现管理

引入依赖:

<!-- 引入Spring对cache的支持 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
    <version>${org.springframework.version}</version>
    <scope>compile</scope>
</dependency>
 
<!-- 引入ecache支持 -->
<!-- https://mvnrepository.com/artifact/net.sf.ehcache/ehcache -->
<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>2.10.3</version>
</dependency>

①注解配置,此处使用的CacheManager是ehcache的缓存管理器

import net.sf.ehcache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.ehcache.EhCacheCacheManager;
import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
 
@Configuration
@EnableCaching
public class EhCacheConfig {
    /**
     * 定义缓存管理器,EhCacheCacheManager是Spring提供的管理器支持,需要将ecache的缓存管理器注入到EhCacheCacheManager中
     **/
    @Bean
    public EhCacheCacheManager cacheManager2(CacheManager cm){
        return new EhCacheCacheManager(cm);
    }
 
    /**
     * 定义缓存管理器工厂,用于生产EhCacheManager
     **/
    @Bean
    public EhCacheManagerFactoryBean ehCacheManagerFactoryBean(){
        EhCacheManagerFactoryBean factoryBean = new EhCacheManagerFactoryBean();
        factoryBean.setConfigLocation(new ClassPathResource("ehcache.xml"));
        return factoryBean;
    }
}

②XML配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cache="http://www.springframework.org/schema/cache"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
    <cache:annotation-driven/>
 
    <!--定义缓存管理器-->
    <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
        <property name="cacheManager" ref="ehcache"></property>
   </bean>
    
    <!--定义缓存管理器工厂-->
    <bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
        <property name="configLocation" value="classpath*:ehcache.xml"></property>
    </bean>
</beans>

其中,ehcache.xml配置文件简单配置如下:

<ehcache>
    <diskStore path="java.io.tmpdir"/>
    <!--
        maxElementsOnDisk:磁盘缓存中最多可以存放的元素数量,0表示无穷大  
        maxElementsInMemory:内存缓存中最多可以存放的元素数量,若放入Cache中的元素超过这个数值,则有以下两种情况  
                             1)若overflowToDisk=true,则会将Cache中多出的元素放入磁盘文件中  
                             2)若overflowToDisk=false,则根据memoryStoreEvictionPolicy策略替换Cache中原有的元素  
         eternal:缓存中对象是否永久有效,即是否永驻内存,true时将忽略timeToIdleSeconds和timeToLiveSeconds
         timeToIdleSeconds:缓存数据在失效前的允许闲置时间(单位:秒),仅当eternal=false时使用,默认值是0表示可闲置时间无穷大,此为可选属性  
                             即访问这个cache中元素的最大间隔时间,若超过这个时间没有访问此Cache中的某个元素,那么此元素将被从Cache中清除  
        timeToLiveSeconds:缓存数据在失效前的允许存活时间(单位:秒),仅当eternal=false时使用,默认值是0表示可存活时间无穷大  
                             即Cache中的某元素从创建到清楚的生存时间,也就是说从创建开始计时,当超过这个时间时,此元素将从Cache中清除
        overflowToDisk:内存不足时,是否启用磁盘缓存(即内存中对象数量达到maxElementsInMemory时,Ehcache会将对象写到磁盘中)  
                             会根据标签中path值查找对应的属性值,写入磁盘的文件会放在path文件夹下,文件的名称是cache的名称,后缀名是data  
        diskPersistent:是否持久化磁盘缓存,当这个属性的值为true时,系统在初始化时会在磁盘中查找文件名为cache名称,后缀名为index的文件  
                             这个文件中存放了已经持久化在磁盘中的cache的index,找到后会把cache加载到内存  
                             要想把cache真正持久化到磁盘,写程序时注意执行net.sf.ehcache.Cache.put(Element element)后要调用flush()方法
    -->
    <defaultCache
            maxElementsInMemory="1000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="false"/>
    <!--定义一个缓存-->
    <cache name="ehCache"
           maxElementsOnDisk="20000"
           maxElementsInMemory="2000"
           eternal="false"
           overflowToDisk="true"
           diskPersistent="true"/>
    
</ehcache>

3、配置RedisCacheManager

Redis 可以用来为 Spring 缓存抽象机制存储缓存条目, Spring Data Redis 提供了 RedisCacheManager ,这是 CacheManager 的一个实现。

RedisCacheManager 会与一个 Redis 服务器协作,并通过 RedisTemplate 将缓存条目存储到 Redis 中

上一节已经了解过Redis,此处我们需要先引入Redis支持:

<!--引入Spring Data Redis-->
<!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-redis -->
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-redis</artifactId>
    <version>2.1.5.RELEASE</version>
</dependency>
 
<!--引入jedis支持-->
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.1</version>
</dependency>

①混合配置

application-redis.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
       xmlns:cache="http://www.springframework.org/schema/cache"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <cache:annotation-driven cache-manager="redisCacheManager"/>
</beans>

RedisConfig.java

@Configuration
@ComponentScan(basePackages = {"com.my.spring.dao"})
@ImportResource("classpath:application-redis.xml")//引入配置文件
@EnableCaching
public class RedisCacheConfig {
 
    /**
     * 定义缓存管理器,注入redisConnectionFactory
     * @param redisConnectionFactory
     * @return
     */
    @Bean("redisCacheManager")
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory){
        RedisCacheManager.RedisCacheManagerBuilder builder = 
                    RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(redisConnectionFactory);
        Set<String> cacheNames = new HashSet<String>() {{
            add("myCache");
        }};
        //设置多个缓存
        builder.initialCacheNames(cacheNames);
        return builder.build();
    }
 
    /**
     * 定义Redis连接工厂
     * @return
     */
    @Bean
    public JedisConnectionFactory redisConnectionFactory(){
        JedisConnectionFactory factory = new JedisConnectionFactory();
        factory.afterPropertiesSet();
        return factory;
    }
}

4、混合使用多种缓存CompositeCacheManager

我们并不是只能有且仅有一个缓存管理器。如果你很难确定该使用哪个缓存管理器,或者有合法的技术理由使用超过一个缓存管理器的话,那么可以尝试使用 Spring 的 CompositeCacheManager.CompositeCacheManager 要通过一个或更多的缓存管理器来进行配置,它会迭代这些缓存管理器,以查找之前所缓存的值

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Spring缓存分析:

Spring 的缓存抽象在很大程度上是围绕切面构建的。在 Spring 中启用缓存时,会创建一个切面,它触发一个或更多的 Spring 的缓存注解,Spring提供的缓存注解主要有以下几个:

注  解

描  述

@Cacheable

表明 Spring 在调用方法之前,首先应该在缓存中查找方法的返回值。如果这个值能够找到,就会返回缓存的值。否则的话,这个方法就会被调用,返回值会放到缓存之中

@CachePut

表明 Spring 应该将方法的返回值放到缓存中。在方法的调用前并不会检查缓存,方法始终都会被调用

@CacheEvict

表明 Spring 应该在缓存中清除一个或多个条目

@Caching

这是一个分组的注解,能够同时应用多个其他的缓存注解

填充缓存

由上述注解可知,@Cacheable@CachePut注解可以往缓存填充内容,两者的共有属性有:

属  性

类  型

描  述

value

String[]

要使用的缓存名称

condition

String

SpEL 表达式,如果得到的值是 false 的话,不会将缓存应用到方法调用上

key

String

SpEL 表达式,用来计算自定义的缓存 key

unless

String

SpEL 表达式,如果得到的值是 true 的话,返回值不会放到缓存之中

在最简单的情况下,在 @Cacheable 和 @CachePut 的这些属性中,只需使用 value 属性指定一个或多个缓存即可

依旧使用Redis缓存了解Spring对缓存的抽象,先编写一个BaseDao作为操作基准

@Component
public class BaseDao {
    
    /**
     * 根据id获取信息
     **/
    @Cacheable(value = "myCache")
    public String findOne(Integer id){
        System.out.println("执行findOne方法。。。。");
        return "我是BaseDao"+id;
    }
    
    /**
     * 根据id更改信息
     **/
    @CachePut(value = "myCache")
    public String save(Integer id){
        System.out.println("执行save方法。。。。。");
        return "BaseDao"+id;
    }
    
    /**
     * 根据id移除信息
     **/
    @CacheEvict(value = "myCache")
    public void remove(Integer id){
        System.out.println("执行remove方法。。。。。");
    }
}

编写测试类:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {RedisCacheConfig.class})
public class AppTest {
    @Autowired
    private BaseDao baseDao;
}

测试使用@Cacheable存取数据

我们知道当缓存中没有对应数据的时候,会执行使用了@Cacheable注解的方法,并将结果存入缓存

如果缓存中已经存在对应数据,则直接将缓存数据返回:

@Test
public void testCacheAble(){
    System.out.println(this.baseDao.findOne(0));
    System.out.println(this.baseDao.findOne(0));
}

此处执行两次findOne(0),测试结果:

执行findOne方法。。。。
我是BaseDao0
我是BaseDao0

可以看到,此处只是实际上只有一次真正进入了findOne方法内,第二次从缓存中获取数据,以下是redis-cli中看到的结果:

在缓存中,缓存的key值默认为 缓存名称::传参值

测试使用@CachePut更新数据

由于使用@CachePut注解默认每次都会进入方法并使用返回值更新缓存,所以该注解在实际业务中一般用在更新数据的方法上

@Test
public void testCachePut(){
    this.baseDao.save(0);
    this.baseDao.save(0);
    System.out.println(this.baseDao.findOne(0));
}

测试结果:

执行save方法。。。。。
执行save方法。。。。。
BaseDao0

可以看到,此处两次执行save方法都进入了,执行save之后再调用findOne方法,依旧直接从缓存取值,缓存已更新

自定义缓存的key

@Cacheable 和 @CachePut 都有一个名为 key 属性,这个属性能够替换默认的 key ,它是通过一个 SpEL 表达式计算得到的

表 达 式

描  述

#root.args

传递给缓存方法的参数,形式为数组

#root.caches

该方法执行时所对应的缓存,形式为数组

#root.target

目标对象

#root.targetClass

目标对象的类,是#root.target.class的简写形式

#root.method

缓存方法

#root.methodName

缓存方法的名字,是#root.method.name的简写形式

#result

方法调用的返回值(不能用在@Cacheable注解上)

#Argument

任意的方法参数名(如#argName)或参数索引(如#a0或#p0)

如将上述findOne()和save()方法缓存的key定义为BaseDao的class

@Cacheable(value = "myCache",key = "#root.targetClass")
public String findOne(Integer id){
    System.out.println("执行findOne方法。。。。");
    return "我是BaseDao"+id;
}
 
@CachePut(value = "myCache",key = "#root.targetClass")
public String save(Integer id){
    System.out.println("执行save方法。。。。。");
    return "BaseDao"+id;
}

再次执行测试testCacheAble()方法:

执行findOne方法。。。。
我是BaseDao0
我是BaseDao0

可以看到缓存如下:使用class com.my.spring.dao.BaseDao作为缓存的key

移除缓存

使用@CacheEvict测试移除缓存

@Test
public void testCacheEvict(){
    this.baseDao.remove(0);
}

执行测试结果:

执行remove方法。。。。。

缓存已经清除:

此时再去访问findOne(),结果:

执行findOne方法。。。。
我是BaseDao0
我是BaseDao0

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值