我们知道一个程序的瓶颈在于数据库,我们也知道内存的速度是大大快于硬盘的速度的。当我们需要重复地获取相同的数据的时候,我们一次又一次的请求数据库或者远程服务,导致大量的时间耗费在数据库查询或者远程方法调用上,导致程序性能的恶化,这便是数据缓存要解决的问题。

一,Spring 缓存支持

Spring定义了org.springframework.cache.CacheManager和org.springframework.cache.Cache接口用来统一不同的缓存技术。其中CacheManager是Spring提供的各种缓存技术抽象接口,Cache接口包含缓存的各种操作(增加,删除,获得缓存,我们一般不会直接和此接口打交道)

1,Spring支持的CacheManager

针对不同的缓存技术,需要实现不同的CacheManager,Spring定义了如下所示的CacheManager实现:

SimpleCacheManager:使用简单的Collection来存储缓存,主要用来测试用途。

ConcurrentMapCacheManager:使用ConcurrentMap来存储缓存。

NoOpCacheManager:仅测试用途,不会实际存储缓存。

EhCacheCacheManager:使用EhCache作为缓存技术。

GuavaCacheManager:使用Google Guava的GuavaCache作为缓存技术。

HazelcastCacheManager:使用Hazelcast作为缓存技术。

JCacheCacheManager:支持JCache标准的实现作为缓存技术,如Apache Commons JCS

RedisCacheManager:使用Redis作为缓存技术。

在我们使用任意一个实现的CacheManager的时候,需要注册实现CacheManager的Bean,例如:

@Beanpublic EhCacheCacheManager cacheManager(CacheManager ehCacheCacheManager){return new EhCacheCacheManager(ehCacheCacheManager);}

当然,每种缓存技术都有很多的额外配置,但配置cacheManager是必不可少的

2,声名式缓存注解

Spring提供了4个注解来声明缓存规则(又是使用注解式的AOP的一个生动例子)。这四个注解如下:

@Cacheable:在方法执行前spring先查看缓存中是否有数据,如果有数据,则直接返回缓存数据;若没有数据,调用方法并将方法返回值放进缓存。

@CachePut:无论怎样,都会将方法的返回值放到缓存中。@CachePut的属性与@Cacheable保持一致。

@CacheEvict:将一条或多条数据从缓存中删除。

@Caching:可以通过@Caching注解组合多个注解策略在一个方法上。

@Cacheable,@Cacheable,@CacheEvict都有value属性,指定的是要使用的缓存名称;key属性指定的是数据在缓存中的存储的键。

3,开启声名式缓存支持

开启声名式缓存支持十分简单,只需在配置类上使用@EnableCaching注解即可,如下:

package com.jack.springboot8cache.config;import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Configuration;/*** create by jack 2017/10/9*/
@Configuration
@EnableCaching
public class AppConfig {
}

二,Spring Boot的支持

在spring中使用缓存技术的关键是配置CacheManager,而Spring Boot为我们自动配置了多个CacheManager的实现。

Spring Boot的CacheManager的自动配置放置在org.springframework.boot.autoconfigure.cache包中,如下所示:

通过上图我们可以看出,Spring Boot为我们自动配置了EhCacheCacheConfiguration(使用EhCache),GenericCacheConfiguration(使用Collection),GuavaCacheConfiguration(使用Guava),HazelcastCacheConfiguration(使用Hazelcast),InfinispanCacheConfiguration(使用Infinispan),JCacheCacheConfiguration(使用JCache),NoOpCacheConfiguration(不使用存储),RedisCacheConfiguration(使用Redis),SimpleCacheConfiguration(使用ConcurrentMap)。在不做任何额外配置的情况下,默认使用的是SimpleCacheConfiguration,即使用ConcurrentMapCacheManager。Spring boot支持以“spring.cache”为前缀的属性来配置缓存:

spring.cache.type=#可选generic,ehcache,hazelcast,infinispan,jcache,redis,guava,simple,none

spring.cache.cache-names=#程序启动时创建缓存名称

spring.cache.ehcache.config=#ehcache配置文件地址

spring.cache.hazelcast.config=#hazelcast配置文件地址

spring.cache.infinispan.config=#infinispan配置文件地址

spring.cache.jcache.config=#jcache配置文件地址

spring.cache.jcache.provider=#当多个jcache实现在类路径中的时候,指定jcache实现

spring.cache.guava.spec=#guava specs

在Spring Boot环境下,使用缓存技术只需在项目中导入相关缓存技术的依赖包,并在配置类上使用@EnableCaching开启缓存即可。

三,实战

下面将以Spring Boot默认的ConcurrentMapCacheManager作为缓存技术,演示@Cacheable,@CachePut,@CacheEvit,最后使用EhCache,Guava来替换缓存技术。

1,新建Spring Boot项目

新建Spring Boot项目依赖为Cache,JPA,WEB,添加mysql驱动,pom.xml的配置文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.jack</groupId><artifactId>springboot8cache</artifactId><version>0.0.1-SNAPSHOT</version><packaging>jar</packaging><name>springboot8cache</name><description>Demo project for Spring Boot</description><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>1.5.7.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--mysql连接驱动--><!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!-- https://mvnrepository.com/artifact/com.alibaba/fastjson --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.39</version></dependency><!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-cache --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

2,实体类

package com.jack.springboot8cache.entity;import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;/*** create by jack 2017/10/3*/
//@Entity注解指明这是一个和数据库表映射的实体类
@Entity
public class Person {/*** 主键id* @Id注解指明这个属性映射为数据库的主键* @GeneratedValue定义主键生成的方式,下面采用的是mysql的自增属性*/@Id@GeneratedValue(strategy= GenerationType.AUTO)private Integer id;/*** 姓名*/private String name;/*** 年龄*/private Integer age;/*** 地址*/private String address;public Person() {super();}public Person(Integer id, String name, Integer age, String address) {super();this.id = id;this.name = name;this.age = age;this.address = address;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}
}

3,实体类Repository

package com.jack.springboot8cache.dao;import com.jack.springboot8cache.entity.Person;
import org.springframework.data.jpa.repository.JpaRepository;/*** create by jack 2017/10/8* 实体类*/
public interface PersonRepository extends JpaRepository<Person,Integer> {
}

4,业务服务

1)接口:

package com.jack.springboot8cache.service;import com.jack.springboot8cache.entity.Person;/*** create by jack 2017/10/9*/
public interface DemoService {Person save(Person person);void remove(Integer id);Person findOne(Person person);
}

2)实现类

package com.jack.springboot8cache.impl;import com.jack.springboot8cache.dao.PersonRepository;
import com.jack.springboot8cache.entity.Person;
import com.jack.springboot8cache.service.DemoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;/*** create by jack 2017/10/9*/
@Service
public class DemoServiceImpl implements DemoService {@Autowiredprivate PersonRepository personRepository;/*** 注意:如果没有指定key,则方法参数作为key保存到缓存中*//***@CachePut缓存新增的或更新的数据到缓存,其中缓存的名称为people,数据的key是person的id* @param person* @return*/@CachePut(value = "people",key="person.id")@Overridepublic Person save(Person person) {Person p = personRepository.save(person);System.out.println("为id,key为:"+p.getId()+"数据做了缓存");return p;}/*** @CacheEvict从缓存people中删除key为id的数据* @param id*/@CacheEvict(value = "people")@Overridepublic void remove(Integer id) {System.out.println("删除了id,key为"+id+"的数据缓存");personRepository.delete(id);}/*** @Cacheable缓存key为person的id数据到缓存people中* @param person* @return*/@Cacheable(value = "people",key = "person.id")@Overridepublic Person findOne(Person person) {Person p = personRepository.findOne(person.getId());System.out.println("为id,key为:"+p.getId()+"数据做了缓存");return p;}
}

注意:如果没有指定key,则方法参数作为key保存到缓存中

5,控制器

package com.jack.springboot8cache.controller;import com.jack.springboot8cache.entity.Person;
import com.jack.springboot8cache.service.DemoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** create by jack 2017/10/9*/
@RestController
@RequestMapping("cache")
public class CacheController {@Autowiredprivate DemoService demoService;@RequestMapping("/put")public Person put(Person person){return demoService.save(person);}@RequestMapping("/evit")public String evit(Integer id){demoService.remove(id);return "ok";}@RequestMapping("/cacheable")public Person cacheable(Person person){return demoService.findOne(person);}}

6,开启缓存支持

package com.jack.springboot8cache;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;@SpringBootApplication
@EnableCaching
public class Springboot8cacheApplication {public static void main(String[] args) {SpringApplication.run(Springboot8cacheApplication.class, args);}
}

7,运行

当我们对数据库做了缓存以后,数据的获得将从缓存中得到,而不是从数据库中得到。当前的数据库的数据情况如下所示:

我们在每次运行测试情况下,都重启了应用程序。

1)测试@Cacheable

第一次访问http://localhost:9090/cache/cacheable?id=85,第一次将调用方法查询数据库,并将数据放到缓存people中。

此时控制台输出如下:

2017-10-09 23:29:05.877  INFO 2468 --- [nio-9090-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring FrameworkServlet 'dispatcherServlet'
2017-10-09 23:29:05.878  INFO 2468 --- [nio-9090-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization started
2017-10-09 23:29:05.939  INFO 2468 --- [nio-9090-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization completed in 61 ms
Hibernate: select person0_.id as id1_0_0_, person0_.address as address2_0_0_, person0_.age as age3_0_0_, person0_.name as name4_0_0_ from person person0_ where person0_.id=?
为id,key为:85数据做了缓存

页面输出如下:

再次访问http://localhost:9090/cache/cacheable?id=85,此时控制台没有再次输出hibernate的查询语句,以及“为id,key为:85数据做了缓存”字样,表示没有调用这个方法,页面直接从数据缓存中获得数据,页面输出结果如下:

2)测试@CachePut

访问http://localhost:9090/cache/put?name=jack8&age=99&address=深圳深圳,此时控制台输出如下:

2017-10-09 23:37:10.823  INFO 1212 --- [nio-9090-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring FrameworkServlet 'dispatcherServlet'
2017-10-09 23:37:10.824  INFO 1212 --- [nio-9090-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization started
2017-10-09 23:37:10.893  INFO 1212 --- [nio-9090-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization completed in 69 ms
Hibernate: insert into person (address, age, name) values (?, ?, ?)
为id,key为:86数据做了缓存

页面输出如下:

我们再次访问:http://localhost:9090/cache/cacheable?id=87,控制台无输出,从缓存直接获得数据,页面显示如上。

3)测试@CacheEvit

访问:http://localhost:9090/cache/cacheable?id=87,为id为87的数据做缓存,再次访问http://localhost:9090/cache/cacheable?id=87,确认数据已经从缓存中获取。

访问:http://localhost:9090/cache/evit?id=87,从缓存中删除key为87的缓存数据:

删除了id,key为87的数据缓存
Hibernate: select person0_.id as id1_0_0_, person0_.address as address2_0_0_, person0_.age as age3_0_0_, person0_.name as name4_0_0_ from person person0_ where person0_.id=?
Hibernate: delete from person where id=?

再次访问:http://localhost:9090/cache/cacheable?id=87,观察控制台,数据已经从数据库中删除了,查询缓存,发现空指针异常了,查询失败

四,切换缓存技术

切换缓存技术除了移入相关依赖包或者配置以外,使用方式和上面的实战例子保存一致。下面讲解Spring Boot下EhCache和Guava作为缓存技术的方式,其余缓存技术也是类似的方式。

1,EhCahce

当我们需要使用EhCache作为缓存技术的时候,我们只需要在pom.xml中添加EhCache的依赖即可:

<!-- https://mvnrepository.com/artifact/net.sf.ehcache/ehcache -->
<dependency><groupId>net.sf.ehcache</groupId><artifactId>ehcache</artifactId><version>2.10.4</version>
</dependency>

EhCache所需的配置文件ehcache.xml只需放在类路径下,Spring Boot会自动扫描,例如:

<?xml version="1.0" encoding="UTF-8">

<ehcache>

<cache name="people" maxElementsInMemory="1000"/>

</ehcache>

Spring Boot会给我们自动配置EhcacheCacheManager的Bean。

2,Guava

当我们需要使用Guava作为缓存技术的时候,我们只需要在pom.xml中增加Guava的依赖即可:

<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>23.0</version>
</dependency>

Spring Boot会给我们自动配置GuavaCacheManager这个Bean。

3,Redis

使用Redis,只需添加下面的依赖即可:

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><version>1.5.7.RELEASE</version>
</dependency>

Spring Boot将会为我们自动配置RedisCacheManager以及RedisTemplate的Bean。

源码地址:https://github.com/wj903829182/SpringCloudTwo/tree/master/springboot8cache

SpringBoot25-spingboot数据访问-数据缓存Cache相关推荐

  1. 项目架构开发:数据访问层之Cache

    数据访问层简单介绍 数据访问层,提供整个项目的数据访问与持久化功能.在分层系统中所有有关数据访问.检索.持久化的任务,最终都将在这一层完成. 来看一个比较经典的数据访问层结构图 大概可以看出如下信息 ...

  2. asp.net 应用数据缓存 -- Cache对象使用

    ASP.NET 应用数据缓存 -- Cache对象使用 [原文:http://msdn.microsoft.com/zh-cn/library/18c1wd61%28v=vs.100%29.aspx] ...

  3. Spring Boot 实践折腾记(12):支持数据缓存Cache

    不管是什么类型的应用程序,都离不开数据,即便如现在的手机APP,我们依然需要使用数数据库,对于不懂的人,当然,我们可以告诉他们一些高大上的概念,但是作为专业人士,就一定要明白背后的真实原理到底是什么. ...

  4. DNN 数据访问策略 (转)

    经过几天断断续续的努力,这篇文章终于翻译结束,文章主要讲了DNN的数据访问策略,对于了解系统整体上是如何工作的有一定的帮助,希望能给dnn的初学者一些有用的信息.由于翻译的匆忙+水平有限,错误或不当之 ...

  5. 多样化实现Windows phone 7本地数据访问5——深入Rapid Repository

    上一篇多样化实现Windows Phone 7本地数据访问<4>--Rapid Repository  中初步的介绍Repid Repository作为Windows phone 7数据库 ...

  6. CYQ.Data 轻量数据访问层(一) 概述

    在很久很久以前.2007年底,我曾发布过CYQ.Data.DLL,那时的学术氛围很浓,评论的也比较重 在那里,我曾做过一些简介与使用方法的帮助 在这个系列中,我将一步一步开源并讲解实现的过程,由于文章 ...

  7. dnn学习:数据访问(1)

    最近粗略的研究了dnn的数据访问机制,记录一下学习结果.    dnn的页面.模块可按照组进行授权,本来想先研究dnn是如何设计权限控制模块的,把权限相关的表结构及关系搞清楚后开始看代码,这一看就钻到 ...

  8. web页面上数据是否进行缓存要怎么判断_前端要知道的网络知识五:详细的介绍web缓存...

    Web缓存是可以自动保存常见文档副本的HTTP设备.当Web请求抵达缓存时,如果本地有"已缓存的"副本,就可以从本地存储设备而不是原始服务器中提取这个文档.本文将详细介绍缓存的相关 ...

  9. 如何写出高性能代码(四)优化数据访问

      同一份逻辑,不同人的实现的代码性能会出现数量级的差异: 同一份代码,你可能微调几个字符或者某行代码的顺序,就会有数倍的性能提升:同一份代码,也可能在不同处理器上运行也会有几倍的性能差异:十倍程序员 ...

最新文章

  1. .NET 自定义Json序列化时间格式
  2. 杜克大学计算机科学专业,杜克大学计算机科学专业研究生留学申请条件高不高?...
  3. python代码物理_利用python求解物理学中的双弹簧质能系统详解
  4. ubuntu中安装wmware-tools
  5. linux服务器nvidia驱动的安装与卸载
  6. getSlotFromBufferLocked: unknown buffer: 0xf3d94ca0
  7. Atcoder 084D - Small Multiple(最短路径+思维)
  8. python的convert_python编程开发之类型转换convert实例分析
  9. Java中9种IO的读取方式
  10. Android Fragment (一)
  11. powershell自动化操作AD域、Exchange邮箱系列(1)——powershell 简介
  12. IE下调试CSS与JS
  13. solaris 10 oracle 11g r2安装教程,Oracle 11gR2 on Solaris 10安装技术文档(原版英文)
  14. Hive 3.1.2 国内镜像下载地址
  15. UWB定位系统与蓝牙技术的对比
  16. 计算机图片怎么截图快捷键,电脑系统截图快捷键(电脑怎么截图)
  17. 【操作系统】动态分区分配算法
  18. 为什么保险公司一直不停地招人?
  19. 2019(第八届)国际桥梁与隧道技术大会-会议议程
  20. 数据库恢复时出现诸如“设备激活错误

热门文章

  1. [Python] 第三方库安装包包名解释
  2. Inception模块
  3. 爬取百思不得姐的段子
  4. Android碎碎念4:避免Dialog抢Activity焦点
  5. 如何让百度搜索网站带图片公司logo
  6. 计算机英语 病毒 教案,计算机病毒及其防治教案.doc
  7. emqx 修改网页客户端dashboard 密码
  8. ICIP2021:VVC RPR参考结构改进
  9. unicode汉字内码表(转)
  10. 【OpenCV】2020年关于SIFT算法专利版权问题的解决办法