SpringBoot25-spingboot数据访问-数据缓存Cache
我们知道一个程序的瓶颈在于数据库,我们也知道内存的速度是大大快于硬盘的速度的。当我们需要重复地获取相同的数据的时候,我们一次又一次的请求数据库或者远程服务,导致大量的时间耗费在数据库查询或者远程方法调用上,导致程序性能的恶化,这便是数据缓存要解决的问题。
一,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相关推荐
- 项目架构开发:数据访问层之Cache
数据访问层简单介绍 数据访问层,提供整个项目的数据访问与持久化功能.在分层系统中所有有关数据访问.检索.持久化的任务,最终都将在这一层完成. 来看一个比较经典的数据访问层结构图 大概可以看出如下信息 ...
- asp.net 应用数据缓存 -- Cache对象使用
ASP.NET 应用数据缓存 -- Cache对象使用 [原文:http://msdn.microsoft.com/zh-cn/library/18c1wd61%28v=vs.100%29.aspx] ...
- Spring Boot 实践折腾记(12):支持数据缓存Cache
不管是什么类型的应用程序,都离不开数据,即便如现在的手机APP,我们依然需要使用数数据库,对于不懂的人,当然,我们可以告诉他们一些高大上的概念,但是作为专业人士,就一定要明白背后的真实原理到底是什么. ...
- DNN 数据访问策略 (转)
经过几天断断续续的努力,这篇文章终于翻译结束,文章主要讲了DNN的数据访问策略,对于了解系统整体上是如何工作的有一定的帮助,希望能给dnn的初学者一些有用的信息.由于翻译的匆忙+水平有限,错误或不当之 ...
- 多样化实现Windows phone 7本地数据访问5——深入Rapid Repository
上一篇多样化实现Windows Phone 7本地数据访问<4>--Rapid Repository 中初步的介绍Repid Repository作为Windows phone 7数据库 ...
- CYQ.Data 轻量数据访问层(一) 概述
在很久很久以前.2007年底,我曾发布过CYQ.Data.DLL,那时的学术氛围很浓,评论的也比较重 在那里,我曾做过一些简介与使用方法的帮助 在这个系列中,我将一步一步开源并讲解实现的过程,由于文章 ...
- dnn学习:数据访问(1)
最近粗略的研究了dnn的数据访问机制,记录一下学习结果. dnn的页面.模块可按照组进行授权,本来想先研究dnn是如何设计权限控制模块的,把权限相关的表结构及关系搞清楚后开始看代码,这一看就钻到 ...
- web页面上数据是否进行缓存要怎么判断_前端要知道的网络知识五:详细的介绍web缓存...
Web缓存是可以自动保存常见文档副本的HTTP设备.当Web请求抵达缓存时,如果本地有"已缓存的"副本,就可以从本地存储设备而不是原始服务器中提取这个文档.本文将详细介绍缓存的相关 ...
- 如何写出高性能代码(四)优化数据访问
同一份逻辑,不同人的实现的代码性能会出现数量级的差异: 同一份代码,你可能微调几个字符或者某行代码的顺序,就会有数倍的性能提升:同一份代码,也可能在不同处理器上运行也会有几倍的性能差异:十倍程序员 ...
最新文章
- .NET 自定义Json序列化时间格式
- 杜克大学计算机科学专业,杜克大学计算机科学专业研究生留学申请条件高不高?...
- python代码物理_利用python求解物理学中的双弹簧质能系统详解
- ubuntu中安装wmware-tools
- linux服务器nvidia驱动的安装与卸载
- getSlotFromBufferLocked: unknown buffer: 0xf3d94ca0
- Atcoder 084D - Small Multiple(最短路径+思维)
- python的convert_python编程开发之类型转换convert实例分析
- Java中9种IO的读取方式
- Android Fragment (一)
- powershell自动化操作AD域、Exchange邮箱系列(1)——powershell 简介
- IE下调试CSS与JS
- solaris 10 oracle 11g r2安装教程,Oracle 11gR2 on Solaris 10安装技术文档(原版英文)
- Hive 3.1.2 国内镜像下载地址
- UWB定位系统与蓝牙技术的对比
- 计算机图片怎么截图快捷键,电脑系统截图快捷键(电脑怎么截图)
- 【操作系统】动态分区分配算法
- 为什么保险公司一直不停地招人?
- 2019(第八届)国际桥梁与隧道技术大会-会议议程
- 数据库恢复时出现诸如“设备激活错误