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. docker英语

    demotevt. 使降级:使降职 promotevt. 促进:提升:推销:发扬 swarmn. 一大群:蜂群:人群:一大群小型天体同时在空中出现 worker 工人manager 经理swarm 人 ...

  2. tensorflow 分布式搭建

    https://blog.csdn.net/qq_40652148/article/details/80467131 https://yq.aliyun.com/articles/602111 git ...

  3. MYSQL启用数据库错误:ERROR 2002 (HY000)

    ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2) MYSQL服务 ...

  4. ReportManager

    package com.neusoft.report.engine; import com.neusoft.report.common.Logger; import com.neusoft.repor ...

  5. Java面试题集(116-135)

    Java程序员面试题集(116-135) 摘要:这一部分讲解基于Java的Web开发相关面试题,即便在Java走向没落的当下,基于Java的Web开发因为拥有非常成熟的解决方案,仍然被广泛应用.不管你 ...

  6. 【ABAP系列】SAP ABAP 实现FTP的文件上传与下载

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[ABAP系列]SAP ABAP 实现FTP的文 ...

  7. Cocos2d-X网络编程(1) 网络基本概念

    网络模型 OSI层模型.TCP/IP的层模型如下所示. TCP/IP各层对应的协议如下所示. 通过初步的了解,我知道: IP协议:对应于网络层,是网络层的协议, TCP协议:对应于传输层,是传输层的协 ...

  8. Go语言入门篇-命令 与 语法

    一.命令基础 1. go run : 用于运行命令源码文件(如:go run helloworld.go) 只能接受一个命令源码文件以及若干个库源码文件作为文件参数 其内部操作步骤: (1)先编译源码 ...

  9. Web Services调用存储过程简单实例

    转:http://www.cnblogs.com/jasenkin/archive/2010/03/02/1676634.html Web Services 主要利用 HTTP 和 SOAP 协议使商 ...

  10. Ubuntu 16.04简单配置备忘录

    1.几个安装包的地址 1.Linux QQ:https://im.qq.com/linuxqq/index.html 2.网易云音乐:http://s1.music.126.net/download/ ...