Spring的缓存机制非常灵活,可以对容器中任意Bean或者Bean的方法进行缓存,因此这种缓存机制可以在JavaEE应用的任何层次上进行缓存。

Spring缓存底层也是需要借助其他缓存工具来实现,例如EhCache(Hibernate缓存工具),上层则以统一API编程。

要使用Spring缓存,需要以下三步

  • 1.向Spring配置文件导入context:命名空间
  • 2.在Spring配置文件启用缓存,具体是添加 <cache:annotation-driven cache-manager="缓存管理器ID" />
  • 3.配置缓存管理器,不同的缓存实现配置不同,如果是EhCache,需要先配置一个ehcache.xml

例如

 1 <?xml version="1.0" encoding="UTF-8"?>
2 <ehcache>
3 <diskStore path="java.io.tmpdir" />
4 <!-- 配置默认的缓存区 -->
5 <defaultCache
6 maxElementsInMemory="10000"
7 eternal="false"
8 timeToIdleSeconds="120"
9 timeToLiveSeconds="120"
10 maxElementsOnDisk="10000000"
11 diskExpiryThreadIntervalSeconds="120"
12 memoryStoreEvictionPolicy="LRU"/>
13 <!-- 配置名为users的缓存区 -->
14 <cache name="users"
15 maxElementsInMemory="10000"
16 eternal="false"
17 overflowToDisk="true"
18 timeToIdleSeconds="300"
19 timeToLiveSeconds="600" />
20 </ehcache>

上面的ehcache.xml配置了两个缓存区,Spring中的Bean将会缓存在这些缓存区中,一般的,Spring容器中有多少个Bean,就会在ehcache中定义多少个缓存区。

接着在Spring配置文件中配置缓存管理器如下,其中第一个Bean是一个工厂Bean,用来配置EhCache的CacheManager, 第二个Bean才是为Spring缓存配置的缓存管理器,所以将第一个Bean注入第二个Bean。

 1     <cache:annotation-driven cache-manager="cacheManager" />
2
3 <!-- 配置EhCache的CacheManager
4 通过configLocation指定ehcache.xml文件的位置 -->
5 <bean id="ehCacheManager"
6 class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"
7 p:configLocation="classpath:ehcache.xml"
8 p:shared="false" />
9 <!-- 配置基于EhCache的缓存管理器
10 并将EhCache的CacheManager注入该缓存管理器Bean -->
11 <bean id="cacheManager"
12 class="org.springframework.cache.ehcache.EhCacheCacheManager"
13 p:cacheManager-ref="ehCacheManager" >
14 </bean>

下面是一个完整的Spring配置,

 1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans xmlns="http://www.springframework.org/schema/beans"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xmlns:p="http://www.springframework.org/schema/p"
5 xmlns:cache="http://www.springframework.org/schema/cache"
6 xmlns:context="http://www.springframework.org/schema/context"
7 xsi:schemaLocation="http://www.springframework.org/schema/beans
8 http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
9 http://www.springframework.org/schema/cache
10 http://www.springframework.org/schema/cache/spring-cache-4.0.xsd
11 http://www.springframework.org/schema/context
12 http://www.springframework.org/schema/context/spring-context-4.0.xsd">
13
14 <context:component-scan
15 base-package="com.service"/>
16
17 <cache:annotation-driven cache-manager="cacheManager" />
18
19 <!-- 配置EhCache的CacheManager
20 通过configLocation指定ehcache.xml文件的位置 -->
21 <bean id="ehCacheManager"
22 class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"
23 p:configLocation="classpath:ehcache.xml"
24 p:shared="false" />
25 <!-- 配置基于EhCache的缓存管理器
26 并将EhCache的CacheManager注入该缓存管理器Bean -->
27 <bean id="cacheManager"
28 class="org.springframework.cache.ehcache.EhCacheCacheManager"
29 p:cacheManager-ref="ehCacheManager" >
30 </bean>
31
32 </beans>

下面将以@Cacheable为例,演示Spring基于EhCache缓存的用法。@Cacheable用于修饰类或者方法,如果修饰类,则类中所有方法都会被缓存。

类级别的缓存

例如有如下Bean类,

 1 @Service("userService")
2 @Cacheable(value="users")
3 public class UserServiceImpl implements UserService {
4
5 @Override
6 public User getUsersByNameAndAge(String name, int age) {
7 System.out.println("正在执行getUsersByNameAndAge()..");
8 return new User(name,age);
9 }
10
11 @Override
12 public User getAnotherUser(String name, int age) {
13 System.out.println("正在执行getAnotherUser()..");
14 return new User(name,age);
15 }
16 }

基于类的缓存,将会缓存类中的所有方法,缓存之后,程序调用该类实例的任何方法,只要传入的参数相同,Spring将不会真正执行该方法,而是直接根据传入的参数去查找缓存中的数据!

比如像下面这样使用缓存数据,

1     public static void test2() {
2 ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
3 UserService us = ctx.getBean("userService", UserService.class);
4 User u1 = us.getUsersByNameAndAge("张三", 50);
5 //由于第二次调用userService方法时,使用了相同参数,那么真正的方法将不会执行,
6 //Spring将直接从缓存按参数查找数据
7 User u2 = us.getAnotherUser("张三", 50);
8 System.out.println(u1==u2);
9 }

输出结果,

1 正在执行getUsersByNameAndAge()..
2 true

可以看到,上面的getAnotherUser()并没有真正执行,因为传入的参数与之前的方法传入的参数相同,于是Spring直接从缓存区数据了。

上面的Bean类中的注解@Cacheable除了必选属性value之外,还有key, condition,, unless属性,后面三个都是用来设置Spring存储策略,对于基于类的缓存来说,Spring默认以方法传入的参数作为key去缓存中查找结果。

当然我们也可以修改key的策略,让Spring按照其他标准,比如按照第一个参数是否相同来作为key,在缓存中查找结果。

将上面的Bean类修改如下,

1 @Service("userService")
2 @Cacheable(value="users", key="#name")
3 public class UserServiceImpl implements UserService {
4
5 @Override
6 public User getUsersByNameAndAge(String name, int age) {

意味着我们传入相同的name,Spring就不会真正执行方法。只有name不同的时候,方法才会真正执行,例如下面,

1     public static void test2() {
2 ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
3 UserService us = ctx.getBean("userService", UserService.class);
4 User u1 = us.getUsersByNameAndAge("张三", 50);
5 //将@Cacheable的key参数改为key="#name"之后,下面的方法将可以执行。
6 User u2 = us.getAnotherUser("李四", 50);
7 System.out.println(u1==u2);
8 }

可以看到这回getAnotherUser()方法得到执行了,

1 正在执行getUsersByNameAndAge()..
2 正在执行getAnotherUser()..
3 false

我们也可以设置condition属性,例如,

1 @Service("userService")
2 @Cacheable(value="users", condition="#age<100")
3 public class UserServiceImpl implements UserService {
4
5 @Override
6 public User getUsersByNameAndAge(String name, int age) {

那么对于下面的代码来说,两个方法都不会被缓存,Spring每次都是执行真正的方法取结果,

1     public static void test2() {
2 ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
3 UserService us = ctx.getBean("userService", UserService.class);
4 User u1 = us.getUsersByNameAndAge("张三", 500);
5 User u2 = us.getAnotherUser("李四", 500);
6 System.out.println(u1==u2);
7 }

执行结果,

1 正在执行getUsersByNameAndAge()..
2 正在执行getAnotherUser()..
3 false

方法级别的缓存

方法级别的缓存则只会对方法起作用了,不同的方法可以设置不用的缓存区,例如下面这样,

 1 @Service("userService")
2 public class UserServiceImpl implements UserService {
3
4 @Cacheable("users1")
5 @Override
6 public User getUsersByNameAndAge(String name, int age) {
7 System.out.println("正在执行getUsersByNameAndAge()..");
8 return new User(name,age);
9 }
10
11 @Cacheable("users2")
12 @Override
13 public User getAnotherUser(String name, int age) {
14 System.out.println("正在执行getAnotherUser()..");
15 return new User(name,age);
16 }
17 }

使用下面的测试代码,

 1     public static void test2() {
2 ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
3 UserService us = ctx.getBean("userService", UserService.class);
4 //第一次执行方法,方法将会真正执行并缓存
5 User u1 = us.getUsersByNameAndAge("张三", 500);
6 //虽然下面方法传入相同参数,但是因为这两个方法在不同的缓存区,所以无法使用缓存数据
7 User u2 = us.getAnotherUser("张三", 500);
8 System.out.println(u1==u2);
9 //上面已经缓存过,这里不会真正执行,直接使用缓存
10 User u3 = us.getAnotherUser("张三", 500);
11 System.out.println(u3==u2);
12 }

执行结果,

1 正在执行getUsersByNameAndAge()..
2 正在执行getAnotherUser()..
3 false
4 true

使用@CacheEvict清除缓存

被@CacheEvict修饰的方法可以用来清除缓存,使用@CacheEvict可以指定如下属性。

allEntries, 是否清空整个缓存区

beforeInvocation: 是否在执行方法之前清除缓存。默认是方法执行成功之后才清除。

condiition以及key, 与@Cacheable中一样的含义。

下面示范简单用啊,

 1 @Service("userService")
2 @Cacheable("users")
3 public class UserServiceImpl implements UserService {
4
5 @Override
6 public User getUsersByNameAndAge(String name, int age) {
7 System.out.println("正在执行getUsersByNameAndAge()..");
8 return new User(name,age);
9 }
10
11 @Override
12 public User getAnotherUser(String name, int age) {
13 System.out.println("正在执行getAnotherUser()..");
14 return new User(name,age);
15 }
16 //指定根据name,age参数清楚缓存
17 @CacheEvict(value="users")
18 public void evictUser(String name, int age) {
19 System.out.println("--正在清空"+name+","+age+"对应的缓存--");
20 }
21
22 //指定清除user缓存区所有缓存的数据
23 @CacheEvict(value="users", allEntries=true)
24 public void evictAll() {
25 System.out.println("--正在清空整个缓存--");
26 }
27 }

下面是测试类,

 1     public static void test2() {
2 ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
3 UserService us = ctx.getBean("userService", UserService.class);
4 //系统会缓存两个方法
5 User u1 = us.getUsersByNameAndAge("张三", 500);
6 User u2 = us.getAnotherUser("李四",400);
7 //调用evictUser()方法清除缓冲区指定的数据
8 us.evictUser("李四", 400);
9 //前面清除了 李四, 400 的缓存,下面的方法返回的数据将会再次被缓存
10 User u3 = us.getAnotherUser("李四", 400);
11 System.out.println(us == u3); //false
12 //前面已经缓存了 张三, 500的数据,下面方法不会重新执行,直接取缓存中的数据
13 User u4 = us.getAnotherUser("张三", 500);
14 System.out.println(u1==u4); //输出true
15 //清空整个缓存
16 us.evictAll();
17 //由于整个缓存都已经被清空,下面的代码都会被重新执行
18 User u5 = us.getAnotherUser("张三", 500);
19 User u6 = us.getAnotherUser("李四", 400);
20 System.out.println(u1==u5); //输出false
21 System.out.println(u3==u6); //输出false
22 }

执行结果,

 1 正在执行getUsersByNameAndAge()..
2 正在执行getAnotherUser()..
3 --正在清空李四,400对应的缓存--
4 正在执行getAnotherUser()..
5 false
6 true
7 --正在清空整个缓存--
8 正在执行getAnotherUser()..
9 正在执行getAnotherUser()..
10 false
11 false

Spring缓存机制(转)的更多相关文章

  1. Spring(五)Spring缓存机制与Redis的结合

    一.Redis和数据库的结合 使用Redis可以优化性能,但是存在Redis的数据和数据库同步的问题. 例如,T1时刻以将 key1 保存数据到 Redis,T2时刻刷新进入数据库,但是T3时刻发生了 ...

  2. Spring缓存机制的理解

    在spring缓存机制中,包括了两个方面的缓存操作:1.缓存某个方法返回的结果:2.在某个方法执行前或后清空缓存. 下面写两个类来模拟Spring的缓存机制: package com.sin90lzc ...

  3. spring 缓存机制

    简介 Spring3.1开始引入了基于注释的缓存,其使用方法和原理类似于Spring对事务管理的支持.可以对容器中的任意的bean或bean的方法添加缓存.   配置Spring缓存 Spring缓存 ...

  4. 8 -- 深入使用Spring -- 5... Spring 3.1 新增的缓存机制

    8.5 Spring 3.1 新增的缓存机制 Spring 3.1 新增了一种全新的缓存机制,这种缓存机制与Spring容器无缝地整合在一起,可以对容器中的任意Bean或Bean的方法增加缓存.Spr ...

  5. 【spring-boot】spring-boot整合ehcache实现缓存机制

    EhCache 是一个纯Java的进程内缓存框架,具有快速.精干等特点,是Hibernate中默认的CacheProvider. ehcache提供了多种缓存策略,主要分为内存和磁盘两级,所以无需担心 ...

  6. spring-boot整合ehcache实现缓存机制

    EhCache 是一个纯Java的进程内缓存框架,具有快速.精干等特点,是Hibernate中默认的CacheProvider. ehcache提供了多种缓存策略,主要分为内存和磁盘两级,所以无需担心 ...

  7. 以Spring整合EhCache为例从根本上了解Spring缓存这件事(转)

    前两节"Spring缓存抽象"和"基于注解驱动的缓存"是为了更加清晰的了解Spring缓存机制,整合任何一个缓存实现或者叫缓存供应商都应该了解并清楚前两节,如果 ...

  8. Spring控制Hibernate的缓存机制ehcache

    首先在spring.xml中进入bean <prop key="hibernate.cache.use_second_level_cache">true</pro ...

  9. (37)Spring Boot集成EHCache实现缓存机制【从零开始学Spring Boot】

    [本文章是否对你有用以及是否有好的建议,请留言] 写后感:博主写这么一系列文章也不容易啊,请评论支持下. 如果看过我之前(35)的文章这一篇的文章就会很简单,没有什么挑战性了. 那么我们先说说这一篇文 ...

随机推荐

  1. 详解设备PID和VID

    根据USB规范的规定,所有的USB设备都有供应商ID(VID)和产品识别码(PID),主机通过不同的VID和PID来区别不同的设备. VID和PID都是两个字节长,其中,供应商ID(VID)由供应商向 ...

  2. GTX 1060 3GB 能否使用DeepFaceLab ?

    大部分人都知道跑换脸软件对电脑配置的要求比较高.所以当你想要开始玩之前都会有一个疑问:我的电脑能跑起来了么?或者我的电脑能跑那个模型? 之前写过一篇750 1G显卡如何玩deepfakes的文章.今天 ...

  3. ThreadLocal 源码分析

    线程局部变量 ThreadLocal 用于实现线程隔离和类间变量共享. 创建实例 /** * 当前 ThreadLocal 实例的哈希值 */ private final int threadLoca ...

  4. Quartz安装包中的15个example

    Welcome======= Welcome to the Quartz Examples directory. This directory contains 15 examples that sh ...

  5. C# .Net动态调用webService实现思路及代码

    加载: using System; using System.Collections.Generic; using System.Linq; using System.Web; using Syste ...

  6. 关于maven项目 启动页面报错 The type java.io.ObjectInputStream cannot be resolved.

    这种情况,要修改jdk版本,默认jdk选择 jdk不选jre windows---->perference---->java----->installes jres-----> ...

  7. Octavia 创建 Listener、Pool、Member、L7policy、L7 rule 与 Health Manager 的实现与分析

    目录 文章目录 目录 创建 Listener 创建 Pool 创建 Member CalculateDelta HandleNetworkDeltas AmphoraePostNetworkPlug ...

  8. sql进阶练习题

    student SNO    SNAME    SAGE    SSEX01    赵雷    1990-01-01 00:00:00    男02    钱电    1990-12-21 00:00 ...

  9. Linux监控命令之==>vmstat

    一.使用说明 vmstat 可以对操作系统的内存信息.进程状态.CPU 活动.磁盘等信息进行监控,不足之处是无法对某个进程进行深入分析. 二.用法及参数说明 -a:显示活跃和非活跃内存 -f:显示从系 ...

  10. go bigfile (文件传输管理系统)前端分片上传demo

    BIGFILE Github地址: https://github.com/bigfile/bigfile 欢迎大家前来issue & star BIGFILE 中文文档地址:https://l ...