博客地址:http://www.moonxy.com

一、前言

Spring Cache 对 Cahce 进行了抽象,提供了 @Cacheable、@CachePut、@CacheEvict 等注解。Spring Boot 应用基于 Spring Cache,既提供了基于内存实现的缓存管理器,可以用于单体应用系统,也集成了 EhCache、Redis 等缓存服务器,可以用于大型系统或者分布式系统。

二、关于 Cache

应用系统需要通过 Cache 来缓存不经常改变的数据以提高系统性能和增加系统吞吐量,避免直接访问数据库等低速的存储系统。缓存的数据通常存放在访问速度更快的内存中或者是低延迟存取的存储器、服务器上。应用系统缓存通常有以下作用:

缓存 Web 系统的输出,如伪静态页面;

缓存系统中不经常改变的业务数据,如用户权限、字典数据、配置信息等。

三、使用 Spring Cache

Spring Boot 自带了基于 ConcurrentHashMap 的 Simple 缓存管理器,也集成了 EhCache、Redis 等缓存管理器。Spring Boot 应用通过注解的方式使用统一的缓存,只需要在方法上使用缓存注解即可,其缓存的具体实现依赖于选择的目标缓存管理器。本节先介绍 Spring Boot 自带的 Simple 缓存,然后再介绍 EhCahce 和 Redis 缓存。需要注意的是,Simple 只适合单体应用或者开发环境使用,再或者是一个小微系统,通常应用为分布式应用时,则需要集成 EhCache、Redis 等分布式缓存管理器。

3.1 添加 pom 依赖

集成 Spring Cache,只需要在 pom 中添加以下依赖:

<!-- Spring Cache -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>

3.2 配置缓存管理器

在 application.properties 中配置目标缓存管理器:

spring.cache.type=SIMPLE

3.3 开启缓存功能

在启动类上添加 @EnableCaching 注解,开启缓存功能:

package com.light.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching; /**
* 该注解指定项目为springboot,由此类当作程序入口,自动装配web依赖的环境
* @author Administrator
*
*/
@SpringBootApplication
@EnableCaching
public class SpringbootApplication { public static void main(String[] args) {
SpringApplication.run(SpringbootApplication.class, args);
} }

3.4 常用 Spring Cache 缓存注解

一旦配置好 Spring Boot 缓存,就可以在 Spring 管理的 Bean 中使用缓存注解,通常可以直接放在 Service 类上。

@CacheConfig,在类上设置当前缓存的一些公共设置,比如缓存名称;

@Cacheable,作用在方法上,触发缓存读取操作。表明该方法的结果是可以缓存的,如果缓存存在,则目标方法不会被调用,直接取出缓存。可以为方法声明多个缓存,如果至少有一个缓存有缓存项,则其缓存项将被返回;

@CacheEvice,作用在方法上,触发缓存失效操作,删除缓存项或者清空缓存;

@CachePut,作用在方法上,触发缓存更新操作,添加该注解后总是会执行方法体,并且使用返回的结果更新缓存,同 Cacheable 一样,支持 condition、unless、key 选项,也支持 KeyGenerator;

@Caching,作用在方法上,综合上面的各种操作,在有些场景上,调用业务会触发多种缓存操作。

通常清空下,直接使用 SpEL 表达式来指定缓存项的 Key 比自定义一个 KeyGenerator 更为简单。

3.5 定义 Service 接口

package com.light.springboot.service;

import com.light.springboot.entity.User;

public interface UserService {

    /**
* 新增用户
*/
public User insertUser(User user); /**
* 通过id查找单个用户
*/
public User getUserById(Integer id); /**
* 通过id修改单个用户
*/
public User updateUserById(User user); /**
* 通过id删除单个用户
*/
public void deleteUserById(Integer id); }

3.6 编写上面接口的实现类 UserServiceImpl

此处持久层技术使用 Spring Data JPA 实现,调用 UserRepository 接口。

package com.light.springboot.service.impl;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service; import com.light.springboot.dao.UserRepository;
import com.light.springboot.entity.User;
import com.light.springboot.service.UserService; @CacheConfig(cacheNames = "user")
@Service
public class UserServiceImpl implements UserService { @Autowired
private UserRepository userRepository; /**
* 新增用户
*/
@CachePut(key = "#user.id")
public User insertUser(User user) {
user = this.userRepository.save(user);
return user;
} /**
* 通过id查找单个用户
*/
@Cacheable(key = "#id")
public User getUserById(Integer id) {
User user = this.userRepository.findOne(id);
return user;
} /**
* 通过id修改单个用户
*/
@CachePut(key = "#user.id")
public User updateUserById(User user) {
return this.userRepository.save(user);
} /**
* 通过id删除单个用户
*/
@CacheEvict(key = "#id")
public void deleteUserById(Integer id) {
this.userRepository.delete(id);
} }

3.7 编写 UserController 进行验证

package com.light.springboot.controller;

import java.util.Date;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import com.light.springboot.entity.User;
import com.light.springboot.service.UserService; @RestController
@RequestMapping(value="/userController")
public class UserController { @Autowired
private UserService userService; @RequestMapping("/save")
public User saveUser() {
User user = new User();
user.setName("Adam");
user.setDepartmentId(2);
user.setCreateDate(new Date());
userService.insertUser(user);
return user;
} @RequestMapping("/get/{id}")
public User getUser(@PathVariable(required = false) Integer id) {
User user = userService.getUserById(id);
return user;
} @RequestMapping(value="/update/{id}")
public User updateUser(@PathVariable(required = false) Integer id) {
User user = userService.getUserById(id);
if(user == null) {
return null;
}
user.setName("Deft");
user.setDepartmentId(2);
userService.updateUserById(user);
return user;
} @RequestMapping("/delete/{id}")
public Integer deleteUser(@PathVariable(required = false) Integer id) {
userService.deleteUserById(id);
return id;
}
}

3.8 测试日志说明

由于在系统中 整合了 log4jdbc,所以控制台可以直接显示运行时的 SQL 及其参数。

3.8.1 发起新增请求

http://localhost:8080/userController/save

页面显示:

{"id":14,"name":"Adam","departmentId":2,"createDate":"2018-02-10 18:21:45"}

控制台输出:

Hibernate: insert into user (create_time, department_id, name) values (?, ?, ?)
2018-02-10 18:21:45.187 INFO 3920 --- [nio-8080-exec-9] jdbc.sqlonly : insert into user (create_time, department_id, name) values ('02/10/2018 18:21:45.184', 2, 'Adam') UserEntity [id=14, name=Adam, departmentId=2, createDate=Sat Feb 10 18:21:45 CST 2018]

3.8.2 新增成功之后,发起按照 id 查询请求

http://localhost:8080/userController/get/14

页面显示:

{"id":14,"name":"Adam","departmentId":2,"createDate":"2018-02-10 18:21:45"}

控制台没有日志输出,说明数据是从缓存中获取的。

3.8.3 发起修改请求

http://localhost:8080/userController/update/14

页面显示:

{"id":14,"name":"Deft","departmentId":2,"createDate":"2018-02-10 18:21:45"}

控制台输出:

Hibernate: update user set create_time=?, department_id=?, name=? where id=?
2018-02-10 18:27:36.275 INFO 3920 --- [nio-8080-exec-3] jdbc.sqlonly : update user set create_time='02/10/2018 18:21:45.184', department_id=2, name='Deft' where id=14

3.8.4 修改成功之后,再次发起按照 id 查询请求

http://localhost:8080/userController/get/14

页面显示:

{"id":14,"name":"Deft","departmentId":2,"createDate":"2018-02-10 18:21:45"}

控制台没有日志输出,但是数据已经发生了更新,说明数据是从缓存中获取的。

3.8.5 发起删除请求

http://localhost:8080/userController/delete/14

页面显示:

14

控制台输出:

Hibernate: delete from user where id=?
2018-02-10 18:32:32.541 INFO 3920 --- [nio-8080-exec-7] jdbc.sqlonly : delete from user where id=14

3.8.6 删除成功之后,最后发起按照 id 查询请求

http://localhost:8080/userController/get/14

页面没有显示。

控制台输出:

Hibernate: select user0_.id as id1_0_0_, user0_.create_time as create_t2_0_0_, user0_.department_id as departme3_0_0_, user0_.name as name4_0_0_ from user user0_ where user0_.id=?
2018-02-10 18:36:30.372 INFO 3920 --- [nio-8080-exec-9] jdbc.sqlonly : select user0_.id as id1_0_0_, user0_.create_time as create_t2_0_0_, user0_.department_id as
departme3_0_0_, user0_.name as name4_0_0_ from user user0_ where user0_.id=14

可以看到日志打印出了查询语句,说明该缓存项已经被清除,需要查询数据库。

四、集成 EhCache

4.1 添加 EhCache 依赖

<!-- Spring Cache -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency> <!-- EhCache -->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>

4.2 添加 ehcache.xml 配置文件

在 src/main/resources 目录下创建 ehcache.xml 文件,内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd">
<cache name="user"
eternal="false"
maxEntriesLocalHeap="0"
timeToIdleSeconds="50">
</cache>
</ehcache>

4.3 在 application.properties 中配置目标缓存管理器

spring.cache.type=ehcache
spring.cache.ehcache.config=classpath:ehcache.xml

五、集成 Redis

5.1 添加 Redis 依赖

<!-- Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

5.2 在 application.properties 中配置目标缓存管理器

spring.redis.host=192.168.1.102
spring.redis.port=6379
spring.redis.password=
spring.redis.database=0
spring.redis.pool.max-active=8
spring.redis.pool.max-idle=8
spring.redis.pool.max-wait=-1
spring.redis.pool.min-idle=0
spring.redis.timeout=0 spring.cache.type=redis

其他步骤与使用 Simple 和 Ehcache 时的步骤一致。

Spring Boot 入门之 Cache 篇(四)的更多相关文章

  1. Spring Boot 入门之 Web 篇(二)

    原文地址:Spring Boot 入门之 Web 篇(二) 博客地址:http://www.extlight.com 一.前言 上一篇<Spring Boot 入门之基础篇(一)>介绍了 ...

  2. Spring Boot 入门之基础篇(一)

    原文地址:Spring Boot 入门之基础篇(一) 博客地址:http://www.extlight.com 一.前言 Spring Boot 是由 Pivotal 团队提供的全新框架,其设计目的是 ...

  3. Spring Boot 入门之消息中间件篇(五)

    原文地址:Spring Boot 入门之消息中间件篇(五) 博客地址:http://www.extlight.com 一.前言 在消息中间件中有 2 个重要的概念:消息代理和目的地.当消息发送者发送消 ...

  4. Spring Boot 入门之消息中间件篇(转发)

    一.前言 在消息中间件中有 2 个重要的概念:消息代理和目的地.当消息发送者发送消息后,消息就被消息代理接管,消息代理保证消息传递到指定目的地. 我们常用的消息代理有 JMS 和 AMQP 规范.对应 ...

  5. Spring Boot 入门之单元测试篇(五)

    博客地址:http://www.moonxy.com 一.前言 JUnit 是一个由 Java 语言编写的开源的回归测试(回归测试是指重复以前全部或部分的相同测试)框架,由Erich Gamma 和 ...

  6. Spring Boot入门系列(十四)使用JdbcTemplate操作数据库,配置多数据源!

    前面介绍了Spring Boot 中的整合Mybatis并实现增删改查.如何实现事物控制.不清楚的朋友可以看看之前的文章:https://www.cnblogs.com/zhangweizhong/c ...

  7. Spring Boot 入门之持久层篇(三)

    原文地址:Spring Boot 入门之持久层篇(三) 博客地址:http://www.extlight.com 一.前言 上一篇<Spring Boot 入门之 Web 篇(二)>介绍了 ...

  8. spring boot入门教程——Spring Boot快速入门指南

    Spring Boot已成为当今最流行的微服务开发框架,本文是如何使用Spring Boot快速开始Web微服务开发的指南,我们将使创建一个可运行的包含内嵌Web容器(默认使用的是Tomcat)的可运 ...

  9. Spring Boot 入门之缓存和 NoSQL 篇(四)

    原文地址:Spring Boot 入门之缓存和 NoSQL 篇(四) 博客地址:http://www.extlight.com 一.前言 当系统的访问量增大时,相应的数据库的性能就逐渐下降.但是,大多 ...

随机推荐

  1. 纯前端下载pdf链接文件,而不是打开预览的解决方案

    纯前端下载pdf链接文件,而不是打开预览的解决方案 一,介绍与需求 1.1,介绍 XMLHttpRequest 用于在后台与服务器交换数据.这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行 ...

  2. Entity Framework 6.0 入门系列 第一篇

    Entity Framework 6.0 入门系列 第一篇 好几年前接触过一些ef感觉不是很好用,废弃.但是 Entity Framework 6.0是经过几个版本优化过的产物,性能和功能不断完善,开 ...

  3. rocketMQ部署

    rocketMQ部署(单机) 1.          环境: CentOS7 64  &  JDK1.8+ 64  & 用户:www 2.          下载binary文件包: ...

  4. ajax中的后台返回数据data的意义

  5. PythonWeb框架Django:虚拟环境安装(virtualenv)

    虚拟环境的用处: 当我们有多个项目要使用不同的第三方类库的时候,就会发生冲突,因为Python的环境内只允许一个版本的第三方类库. 比如说 有A,B两个Web项目,但是A项目的Django的环境为2. ...

  6. Unity进阶:PlayMaker

    版权申明: 本文原创首发于以下网站: 博客园『优梦创客』的空间:https://www.cnblogs.com/raymondking123 优梦创客的官方博客:https://91make.top ...

  7. GO.数据库接口

    Go没有内置的驱动支持任何的数据库,但是Go定义了database/sql接口,用户可以基于驱动接口开发相应数据库的驱动. 目前NOSQL已经成为Web开发的一个潮流,很多应用采用了NOSQL作为数据 ...

  8. python内建Exception类型

    1.Exception类型及分层结构如下: BaseException +-- SystemExit +-- KeyboardInterrupt +-- GeneratorExit +-- Excep ...

  9. 2019 ICPC南京网络预选赛 I Washing clothes 李超线段树

    题意:有n个人,每个人有一件衣服需要洗,可以自己手洗花费t时间,也可以用洗衣机洗,但是洗衣机只有一台,即每个时刻最多只能有·一个人用洗衣机洗衣服.现在给你每个人最早可以开始洗衣服的时间,问当洗衣机的洗 ...

  10. POJ 1797-Heavy Transportation-dijkstra小变形和POJ2253类似

    传送门:http://poj.org/problem?id=1797 题意: 在起点和终点间找到一条路,使得经过的边的最小值是最大的: 和POJ2253类似,传送门:http://www.cnblog ...