博客地址: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. 盘一盘 AQS和ReentrantLock

    AQS是个啥? AQS(AbstractQueuedSynchronizer)是Java并发用来构建锁和其他同步组件的基础框架.许多同步类实现都依赖于它,如常用的ReentrantLock/Reent ...

  2. 使用charls抓包微信小程序的解决方案(终极解决,各种坑不怕,亲测可用,不服来战!)

    第一步:使用charles进行https抓包 https://www.jianshu.com/p/7a88617ce80b   使用charles进行https抓包 使用Charles进行HTTPS抓 ...

  3. 前端小知识-html5

    一.伪类与伪元素 为什么css要引入伪元素和伪类:是为了格式化文档树以外的信息,也就是说,伪类和伪元素是用来修饰不在文档树中的部分 伪类用于当已有元素处于的某个状态时,为其添加对应的样式,这个状态是根 ...

  4. jsp和servlet开发过程中参数传递乱码问题总结

    1.前言 相信很多初学者在学习javaWeb基础知识时,总会遇到各种各样的乱码问题,我也是从那个时候过来的.当时遇到各种乱码问题,只能通过面向百度的方式,解决各种乱码问题,乱码虽然问题能解决,但是总是 ...

  5. SQL Server检索存储过程的结果集

    目的:检索过滤执行存储过程的结果集 如下介绍两个常用的方法,但是都需要申明表结构:不知道是否有更简便的方法,如有更好的方法,请不吝赐教. 以系统存储过程sp_who2为例: 方法1:使用临时表 --1 ...

  6. Hugo

    快速开始 安装Hugo 1.二进制安装(推荐:简单.快速) 到 Hugo Releases 下载对应的操作系统版本的Hugo二进制文件(hugo或者hugo.exe) Mac下直接使用 ==Homeb ...

  7. Java基础之抽象类与接口

    Java基础之抽象类与接口 对于面向对象编程来说,抽象是它的一大特征之一.在Java中,可以通过两种形式来体现OOP的抽象:接口和抽象类.这两者有太多相似的地方,又有太多不同的地方.很多人在初学的时候 ...

  8. 安装Python及各种包/库——没有网络的电脑上

    我们做项目时可能会遇到,一些电脑只能联内网或者无法联网,这种情况怎样在电脑上安装Python及各种第三方包/库呢? 1.首先,在有网络的电脑上在python官网下载好python安装包,地址:http ...

  9. POJ-2406Power Strings-KMP+定理

    Power Strings 题意:给一个字符串S长度不超过10^6,求最大的n使得S由n个相同的字符串a连接而成,如:"ababab"则由n=3个"ab"连接而 ...

  10. POJ 1236 Network of Schools - 缩点

    POJ 1236 :http://poj.org/problem?id=1236 参考:https://www.cnblogs.com/TnT2333333/p/6875680.html 题意: 有好 ...