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. 【C++ STL 优先队列priority_queue】

    https://www.cnblogs.com/fzuljz/p/6171963.html

  2. 关于ajax请求controller返回中文乱码的解决方法!

    问题描述:前台ajax请求 Spring框架的 controller 返回的本应该是一段中文字符串 ,结果返回了一串 ?????? 解决办法:在RequestMapping的属性中添加返回数据类型属性 ...

  3. 阶段3 1.Mybatis_01.Mybatis课程介绍及环境搭建_07.环境搭建的注意事项

    2 resources下面创建目录要一级一级的创建,下面这个创建的就是一级目录而不是三级 在文件夹下看到的目录也是一级的 因此这里创建目录需要一个个的去创建 配置文件和dao类这两个目录要保持一致,这 ...

  4. PARAMETERS对象

    1. PARAMETERS的基本语法及定义 PARAMETERS可以参照数据字典字段或自定义数据类型创建文本输入域及单选框/复选框等, PARAMETERS只能创建一个单一的输入域且最多只能输入行,其 ...

  5. es笔记---新建es索引

    es对索引的一堆操作都是用restful api去进行的,参数时一堆json,一年前边查边写搞过一次,这回搞迁移,发现es都到6.0版本了,也变化了很多,写个小笔记记录一下. 创建一个es索引很简单, ...

  6. centos7 主从dns配置 bind服务

    一,配置前请先关闭防火墙selinux 防火墙关闭方法,参见上一篇文章. setenforce 0    #临时关闭 修改/etc/selinux/config 文件  将SELINUX=enforc ...

  7. 应用安全 - 无文件式攻击 - 潜伏型攻击 - MBR - 汇总 (2019-11-29 15:57)

    Petya勒索病毒 Date

  8. 应用安全 - 软件漏洞 - sudo漏洞汇总

    sudo Potential bypass of Runas user restrictions(CVE-2019-14287 ) Date:2019.10.14 类型: sudo提权漏洞 影响版本: ...

  9. Java基础语法--IDEA快捷键

    Ctrl+Shift + Enter,语句完成“!”,否定完成,输入表达式时按 “!”键Ctrl+E,最近的文件Ctrl+Shift+E,最近更改的文件Shift+Click,可以关闭文件Ctrl+[ ...

  10. 2019 CSP-S初赛游记

    2019-10-19 ——这个注定要被载入史册的日子 作为一名初中生,和lpy大佬一同参加提高组的比赛,而今年普及组和提高组的时间竟然不一样,于是——凌晨六点半,来到了pdyz和高中生一起坐车去. 高 ...