Spring缓存机制实例代码

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

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

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

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

例如

<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
  <diskStore path="java.io.tmpdir" />
  <!-- 配置默认的缓存区 -->
  <defaultCache
    maxElementsInMemory="10000"
    eternal="false"
    timeToIdleSeconds="120"
    timeToLiveSeconds="120"
    maxElementsOnDisk="10000000"
    diskExpiryThreadIntervalSeconds="120"
    memoryStoreEvictionPolicy="LRU"/>
  <!-- 配置名为users的缓存区 -->
  <cache name="users"
    maxElementsInMemory="10000"
    eternal="false"
    overflowToDisk="true"
    timeToIdleSeconds="300"
    timeToLiveSeconds="600" />
</ehcache>

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

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

<cache:annotation-driven cache-manager="cacheManager" />

  <!-- 配置EhCache的CacheManager
  通过configLocation指定ehcache.xml文件的位置 -->
  <bean id="ehCacheManager"
    class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"
    p:configLocation="classpath:ehcache.xml"
    p:shared="false" />
  <!-- 配置基于EhCache的缓存管理器
  并将EhCache的CacheManager注入该缓存管理器Bean -->
  <bean id="cacheManager"
    class="org.springframework.cache.ehcache.EhCacheCacheManager"
    p:cacheManager-ref="ehCacheManager" >
  </bean>

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

<?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:p="http://www.springframework.org/schema/p"
  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-4.0.xsd
  http://www.springframework.org/schema/cache
  http://www.springframework.org/schema/cache/spring-cache-4.0.xsd
  http://www.springframework.org/schema/context
  http://www.springframework.org/schema/context/spring-context-4.0.xsd">

  <context:component-scan
    base-package="com.service"/>

  <cache:annotation-driven cache-manager="cacheManager" />

  <!-- 配置EhCache的CacheManager
  通过configLocation指定ehcache.xml文件的位置 -->
  <bean id="ehCacheManager"
    class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"
    p:configLocation="classpath:ehcache.xml"
    p:shared="false" />
  <!-- 配置基于EhCache的缓存管理器
  并将EhCache的CacheManager注入该缓存管理器Bean -->
  <bean id="cacheManager"
    class="org.springframework.cache.ehcache.EhCacheCacheManager"
    p:cacheManager-ref="ehCacheManager" >
  </bean>

</beans>

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

类级别的缓存

例如有如下Bean类,

@Service("userService")
@Cacheable(value="users")
public class UserServiceImpl implements UserService {

  @Override
  public User getUsersByNameAndAge(String name, int age) {
    System.out.println("正在执行getUsersByNameAndAge()..");
    return new User(name,age);
  }

  @Override
  public User getAnotherUser(String name, int age) {
    System.out.println("正在执行getAnotherUser()..");
    return new User(name,age);
  }
}

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

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

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

输出结果,

正在执行getUsersByNameAndAge()..
true

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

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

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

将上面的Bean类修改如下,

@Service("userService")
@Cacheable(value="users", key="#name")
public class UserServiceImpl implements UserService {

  @Override
  public User getUsersByNameAndAge(String name, int age) {

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

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

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

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

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

@Service("userService")
@Cacheable(value="users", condition="#age<100")
public class UserServiceImpl implements UserService {

  @Override
  public User getUsersByNameAndAge(String name, int age) {

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

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

执行结果,

正在执行getUsersByNameAndAge()..
正在执行getAnotherUser()..
false

方法级别的缓存

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

@Service("userService")
public class UserServiceImpl implements UserService {

  @Cacheable("users1")
  @Override
  public User getUsersByNameAndAge(String name, int age) {
    System.out.println("正在执行getUsersByNameAndAge()..");
    return new User(name,age);
  }

  @Cacheable("users2")
  @Override
  public User getAnotherUser(String name, int age) {
    System.out.println("正在执行getAnotherUser()..");
    return new User(name,age);
  }
}

使用下面的测试代码,

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

执行结果,

正在执行getUsersByNameAndAge()..
正在执行getAnotherUser()..
false
true

使用@CacheEvict清除缓存

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

allEntries, 是否清空整个缓存区

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

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

下面示范简单用啊,

@Service("userService")
@Cacheable("users")
public class UserServiceImpl implements UserService {
	@Override
	  public User getUsersByNameAndAge(String name, int age) {
		System.out.println("正在执行getUsersByNameAndAge()..");
		return new User(name,age);
	}
	@Override
	  public User getAnotherUser(String name, int age) {
		System.out.println("正在执行getAnotherUser()..");
		return new User(name,age);
	}
	//指定根据name,age参数清楚缓存
	@CacheEvict(value="users")
	  public void evictUser(String name, int age) {
		System.out.println("--正在清空"+name+","+age+"对应的缓存--");
	}
	//指定清除user缓存区所有缓存的数据
	@CacheEvict(value="users", allEntries=true)
	  public void evictAll() {
		System.out.println("--正在清空整个缓存--");
	}
}

下面是测试类,

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

执行结果,

正在执行getUsersByNameAndAge()..
正在执行getAnotherUser()..
--正在清空李四,400对应的缓存--
正在执行getAnotherUser()..
false
true
--正在清空整个缓存--
正在执行getAnotherUser()..
正在执行getAnotherUser()..
false
false

总结

以上就是本文关于Spring缓存机制实例代码的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站其他相关专题,如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!

(0)

相关推荐

  • 实例详解Spring Boot实战之Redis缓存登录验证码

    本章简单介绍redis的配置及使用方法,本文示例代码在前面代码的基础上进行修改添加,实现了使用redis进行缓存验证码,以及校验验证码的过程. 1.添加依赖库(添加redis库,以及第三方的验证码库) <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-redis</artifactId> </dependency&

  • 详解springboot整合ehcache实现缓存机制

    EhCache 是一个纯Java的进程内缓存框架,具有快速.精干等特点,是Hibernate中默认的CacheProvider. ehcache提供了多种缓存策略,主要分为内存和磁盘两级,所以无需担心容量问题. spring-boot是一个快速的集成框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置. 由于spring-boot无需任何样板化的配置文件,所以spring-boot集成一些其他框架时会有略微的

  • 浅谈SpringCache与redis集成实现缓存解决方案

    缓存可以说是加速服务响应速度的一种非常有效并且简单的方式.在缓存领域,有很多知名的框架,如EhCache .Guava.HazelCast等.Redis作为key-value型数据库,由于他的这一特性,Redis也成为一种流行的数据缓存工具. 在传统方式下对于缓存的处理代码是非常臃肿的. 例如:我们要把一个查询函数加入缓存功能,大致需要三步. 一.在函数执行前,我们需要先检查缓存中是否存在数据,如果存在则返回缓存数据 二.如果不存在,就需要在数据库的数据查询出来. 三.最后把数据存放在缓存中,当

  • 浅谈SpringBoot集成Redis实现缓存处理(Spring AOP实现)

    第一章 需求分析 计划在Team的开源项目里加入Redis实现缓存处理,因为业务功能已经实现了一部分,通过写Redis工具类,然后引用,改动量较大,而且不可以实现解耦合,所以想到了Spring框架的AOP(面向切面编程). 开源项目:https://github.com/u014427391/jeeplatform 第二章 SpringBoot简介 Spring框架作为JavaEE框架领域的一款重要的开源框架,在企业应用开发中有着很重要的作用,同时Spring框架及其子框架很多,所以知识量很广.

  • springboot+mybatis+redis 二级缓存问题实例详解

    前言 什么是mybatis二级缓存? 二级缓存是多个sqlsession共享的,其作用域是mapper的同一个namespace. 即,在不同的sqlsession中,相同的namespace下,相同的sql语句,并且sql模板中参数也相同的,会命中缓存. 第一次执行完毕会将数据库中查询的数据写到缓存,第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率. Mybatis默认没有开启二级缓存,需要在全局配置(mybatis-config.xml)中开启二级缓存. 本文讲述的是使用Redi

  • SpringBoot项目中使用redis缓存的方法步骤

    本文介绍了SpringBoot项目中使用redis缓存的方法步骤,分享给大家,具体如下: Spring Data Redis为我们封装了Redis客户端的各种操作,简化使用. - 当Redis当做数据库或者消息队列来操作时,我们一般使用RedisTemplate来操作 - 当Redis作为缓存使用时,我们可以将它作为Spring Cache的实现,直接通过注解使用 1.概述 在应用中有效的利用redis缓存可以很好的提升系统性能,特别是对于查询操作,可以有效的减少数据库压力. 具体的代码参照该

  • Spring Boot 中使用cache缓存的方法

    一.什么是缓存 Cache Cache 一词最早来自于CPU设计 当CPU要读取一个数据时,首先从CPU缓存中查找,找到就立即读取并送给CPU处理:没有找到,就从速率相对较慢的内存中读取并送给CPU处理,同时把这个数据所在的数据块调入缓存中,可以使得以后对整块数据的读取都从缓存中进行,不必再调用内存.正是这样的读取机制使CPU读取缓存的命中率非常高(大多数CPU可达90%左右),也就是说CPU下一次要读取的数据90%都在CPU缓存中,只有大约10%需要从内存读取.这大大节省了CPU直接读取内存的

  • SpringBoot+Mybatis项目使用Redis做Mybatis的二级缓存的方法

    介绍 使用mybatis时可以使用二级缓存提高查询速度,进而改善用户体验. 使用redis做mybatis的二级缓存可是内存可控<如将单独的服务器部署出来用于二级缓存>,管理方便. 1.在pom.xml文件中引入redis依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifac

  • Spring缓存机制实例代码

    Spring的缓存机制非常灵活,可以对容器中任意Bean或者Bean的方法进行缓存,因此这种缓存机制可以在JavaEE应用的任何层次上进行缓存. Spring缓存底层也是需要借助其他缓存工具来实现,例如EhCache(Hibernate缓存工具),上层则以统一API编程. 要使用Spring缓存,需要以下三步 1.向Spring配置文件导入context:命名空间 2.在Spring配置文件启用缓存,具体是添加 <cache:annotation-driven cache-manager="

  • Hibernate缓存机制实例代码解析

    本文研究的主要是Hibernate缓存机制的相关内容,具体如下. 演示项目: Student.java: public class Student { /*学生ID*/ private int id; /*学生姓名*/ private String name; /*学生和班级的关系*/ private Classes classes; //省略setter和getter方法 } Classes.java: public class Classes { /*班级ID*/ private int i

  • Spring的事务机制实例代码

    本文研究的主要是Spring的事务机制的相关内容,具体如下. JAVA EE传统事务机制 通常有两种事务策略:全局事务和局部事务.全局事务可以跨多个事务性资源(即数据源,典型的是数据库和消息队列),通常都需要J2EE应用服务器的管理,其底层需要服务器的JTA支持.而局部事务则与底层采用的持久化技术有关,如果底层直接使用JDBC,需要用Connection对象来操事务.如果采用Hibernate持久化技术,则需要使用session对象来操作事务. 通常的,使用JTA事务,JDBC事务及Hibern

  • spring整合redis实现数据缓存的实例代码

    数据缓存原因:有些数据比较多,如果每次访问都要进行查询,无疑给数据库带来太大的负担,将一些庞大的查询数据并且更新次数较少的数据存入redis,能为系统的性能带来良好的提升. 业务逻辑思路:登入系统,访问数据时,检查redis是否有缓存,有则直接从redis中提取,没有则从数据库查询出,并存入redis中做缓存. 为什么要用redis做缓存: (1)异常快速:Redis的速度非常快,每秒能执行约11万集合,每秒约81000+条记录. (2)支持丰富的数据类型:Redis支持最大多数开发人员已经知道

  • shiro缓存机实例代码

    Shiro提供了类似于Spring的Cache抽象,即Shiro本身不实现Cache,但是对Cache进行了又抽象,方便更换不同的底层Cache实现. Shiro提供的Cache接口: Java代码 public interface Cache<K, V> { //根据Key获取缓存中的值 public V get(K key) throws CacheException; //往缓存中放入key-value,返回缓存中之前的值 public V put(K key, V value) thr

  • MyBatis 动态SQL和缓存机制实例详解

    有的时候需要根据要查询的参数动态的拼接SQL语句 常用标签: - if:字符判断 - choose[when...otherwise]:分支选择 - trim[where,set]:字符串截取,其中where标签封装查询条件,set标签封装修改条件 - foreach: if案例 1)在EmployeeMapper接口文件添加一个方法 public Student getStudent(Student student); 2)如果要写下列的SQL语句,只要是不为空,就作为查询条件,如下所示,这样

  • Android获取应用程序大小和缓存的实例代码

    info package com.qin.appsize; import android.content.Intent; import android.graphics.drawable.Drawable; //Model类 ,用来存储应用程序信息 public class AppInfo { private String appLabel; //应用程序标签 private Drawable appIcon ; //应用程序图像 private Intent intent ; //启动应用程序

  • Java反射机制实例代码分享

    本文旨在对Java反射机制有一个全面的介绍,希望通过本文,大家会对Java反射的相关内容有一个全面的了解. 阅读本文之前,大家可先行参阅<重新理解Java泛型>. 前言 Java反射机制是一个非常强大的功能,在很多大型项目比如Spring, Mybatis都可以看见反射的身影.通过反射机制我们可以在运行期间获取对象的类型信息,利用这一特性我们可以实现工厂模式和代理模式等设计模式,同时也可以解决Java泛型擦除等令人苦恼的问题.本文我们就从实际应用的角度出发,来应用一下Java的反射机制. 反射

  • Android 图片的三级缓存机制实例分析

    Android 图片的三级缓存机制实例分析 当我们获取图片的时候,如果不加以协调好图片的缓存,就会造成大流量,费流量应用,用户体验不好,影响后期发展.为此,我特地分享Android图片的三级缓存机制之从网络中获取图片,来优化应用,具体分三步进行: (1)从缓存中获取图片 (2)从本地的缓存目录中获取图片,并且获取到之后,放到缓存中 (3)从网络去下载图片,下载完成之后,保存到本地和放到缓存中 很好的协调这三层图片缓存就可以大幅度提升应用的性能和用户体验. 快速实现三级缓存的工具类ImageCac

  • iOS清除所有缓存的实例代码

    本文介绍了iOS清除所有缓存的实例代码,分享给大家,具体如下: 计算缓存 NSString *libPath = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES)[0]; CGFloat fileSize=[self folderSizeAtPath:libPath]; - (float ) folderSizeAtPath:(NSString*) folderPath{ NSFileMa

随机推荐