课时五十二、Spring Boot Spring Cache理论篇Spring Boot Spring Cache理论篇
Spring 3.1 引入了激动人心的基于注释(annotation)的缓存(cache)技术,它本质上不是一个具体的缓存实现方案,而是一个对缓存使用的抽象,通过在既有代码中添加少量它定义的各种 annotation,就能够达到缓存方法返回对象的效果。(具体实现:例如EHCache、 OSCache、Redis等)
灵活性
Spring 的缓存技术具备相当的灵活性,不仅能够使用 SpEL(Spring Expression Language)来定义缓存的 key 和各种 condition,还提供开箱即用的缓存临时存储方案,也支持和主流的专业缓存例如 EHCache 集成。
特点
通过少量的配置 annotation 注释即可使得既有代码支持缓存
支持开箱即用 Out-Of-The-Box,即不用安装和部署额外第三方组件即可使用缓存
支持 Spring Express Language,能使用对象的任何属性或者方法来定义缓存的 key 和 condition
支持 AspectJ,并通过其实现任何方法的缓存支持
支持自定义 key 和自定义缓存管理者,具有相当的灵活性和扩展性
我们以前如何自己实现缓存的呢?
以前我们是在service层通过先判断缓存中是否存在缓存数据,如果存在直接返回数据,如果不存在从数据库中进行查询数据然后进行缓存。
那么我们会发现这种方式的代码耦合度太高。如果你之前的代码就是这么设计的,那么你可以考虑使用Spring Cache优化你的代码,你的代码将会变得优雅很多。
注解类Cacheable
Cacheable 支持如下几个参数: value:缓存位置名称,不能为空,如果使用EHCache,就是ehcache.xml中声明的cache的name |
//将缓存保存进andCache,并使用参数中的userId加上一个字符串作为缓存的key
Cacheable(value="andCache", Key="#userId+'findById'")
public SystemUser findById(String userId){
SystemUser user =(SystemUser)dao.findById(SystemUser.Class,userId);
return user;
}
例子
//将缓存保存进andCache,并使用参数中的userId的长度保存到32是才保存进缓存,默认使用参数值及类型作为缓存的key
Cacheable(value="andCache", Key="#userId.length<32")
public boolean isResserved(String userId){
System.out.println("hello andCache"+userId);
return false;
}
注解类@CacheEvict
@CacheEvict 支持如下几个参数: value:缓存位置名称,不能为空 key:缓存的key,默认为空 condition:触发条件,只有满足条件的情况才会清除缓存,默认为空,支持SpEL allEntries:true表示清除value中的全部缓存,默认为false |
例子
// 清楚掉制定key的缓存
@CacheEvict(value = "andCache",key = "#user.userId+'findById'")
public void modifyUserRole(SystemUser user){
System.out.println("hello andCache delete"+user.getUserId());
}//全部清除缓存
@CacheEvict(value = "andCache",allEntries = true)
public void setReservedUsers(){
System.out.println("hello andCache deleteall");
}
注解类@CachePut
@CachePut 注释,这个注释可以确保方法被执行, 同时方法的返回值也被记录到缓存中,实现缓存与数据库的同步更新,理解为update语句。
课时五十三、Spring Boot集成Redis实现缓存机制
前言
Spring Boot集成Redis实现缓存机制
本节课牵涉到的技术点比较多:Spring Data JPA、Redis、Spring MVC,Spirng Cache,所以在本视频的时候,需要对以上这些技术点有一定的了解或者也可以先看看本视频,针对本视频实际的技术点在进一步了解
注意:需要下载Redis Server到本地,所以确保本地的Redis可用。这里还使用了MySql数据库,当然你也可以使用内存数据库进行测试
课程大纲
(1)新建Java Maven Project
新建一个项目:spring-boot-redis
(2)在pom.xml中添加相应的依赖包
<!-- spring boot web支持:mvc,aop... -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency><!--
包含支持UI模版(Velocity,FreeMarker,JasperReports),
邮件服务,
脚本服务(JRuby),
缓存Cache(EHCache),
任务计划Scheduling(uartz)。
-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<!-- 添加redis支持-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
</dependency>
<!-- JPA操作数据库. -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- mysql 数据库驱动. -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
(3)编写Spring Boot启动类
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
(4)配置application.properties
########################################################
###datasource 配置MySQL数据源;
########################################################
spring.datasource.url = jdbc:mysql://localhost:3306/test
spring.datasource.username = root
spring.datasource.password = root
spring.datasource.driverClassName = com.mysql.jdbc.Driver
spring.datasource.max-active=20
spring.datasource.max-idle=8
spring.datasource.min-idle=8
spring.datasource.initial-size=10
########################################################
###REDIS (RedisProperties) redis基本配置;
########################################################
# database name
spring.redis.database=0
# server host1
spring.redis.host=127.0.0.1
# server password
#spring.redis.password=
#connection port
spring.redis.port=6379
# pool settings ...
spring.redis.pool.max-idle=8
spring.redis.pool.min-idle=0
spring.redis.pool.max-active=8
spring.redis.pool.max-wait=-1
# name of Redis server
#spring.redis.sentinel.master=
# comma-separated list of host:port pairs
#spring.redis.sentinel.nodes=
########################################################
### Java Persistence Api 自动进行建表
########################################################
# Specify the DBMS
spring.jpa.database = MYSQL
# Show or not log for each sql query
spring.jpa.show-sql = true
# Hibernate ddl auto (create, create-drop, update)
spring.jpa.hibernate.ddl-auto = update
# Naming strategy
spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy
# stripped before adding them to the entity manager)
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
(5)编写RedisCacheConfig配置类
/**
* redis :
---------------------
1/RedisFactory : redis工厂. --- 通过application.properties配置文件,spring boot会自动创建,我们引入使用即可。
2/RedisTempalte: redis操作类.
3/ReidisManager:redis的管理类.
*
* @author Angel -- 守护天使
* @version v.0.1
* @date 2017年9月21日
*/
@Configuration//这是配置类.@EnableCaching //开启缓存.
public class RedisCacheConfig {
@Bean
public RedisTemplate<String,String> redisTempalte(RedisConnectionFactory redisConnectionFactory){
RedisTemplate<String, String> redisTemplate = new RedisTemplate<String, String>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
return redisTemplate;
}
/**
* 将我们定义的redis 的 redisTempalte(缓存操作类),设置到spring cahcheManager对象中,
* 这样的话,spring 就可以使用cacaheManager对我们的对象进行缓存控制。
* @param redisTempalte
* @return
*/
@Bean
public CacheManager cacheManager(RedisTemplate redisTempalte){
CacheManager cacheManager = new RedisCacheManager(redisTempalte);
return cacheManager;
}
}
(6)编写Student测试实体类
@Entity
public class Student implements Serializable{
private static final long serialVersionUID = 1L;
@Id @GeneratedValue(strategy=GenerationType.AUTO)
private int id; //主键.
private String name;
private int gender;
private Date registerTime;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getGender() {
return gender;
}
public void setGender(int gender) {
this.gender = gender;
}
public Date getRegisterTime() {
return registerTime;
}
public void setRegisterTime(Date registerTime) {
this.registerTime = registerTime;
}
}
(7)编写StudentInfoRepository持久化类
public interface StudentRepository extends CrudRepository<Student, Integer>{}
(8)编写StudentService类
@Service
public class StudentService {
@Autowired
private StudentRepository studentRepository;
//开启事务.
@Transactional
public void save(Student student){
student.setRegisterTime(new Date());
studentRepository.save(student);
}@Cacheable(value="student")
public Student findById(int id) {
System.out.println("(未使用缓存)-->进入到了findById,id="+id);
return studentRepository.findOne(id);
}
@CacheEvict(value="student")
public void deleteCache(int id) {
//这里只清空缓存,实际数据库没有删除。
// studentRepository.delete(id);
}
@Cacheable(value="student1")
public List<Student> findByAll() {
return (List<Student>) studentRepository.findAll();
}
//@CachePut
//public void update();
}
(9)编写StudentController类
@RestController
@RequestMapping("/student")
public class StudentController {
@Autowired
private StudentService studentService;
//?name=张三&gender=1
@RequestMapping("/save")
public Student save(Student student){
studentService.save(student);
return student;
}
@RequestMapping("/findById")
public Student findById(int id){
System.out.println("StudentController.findById----id="+id+",开始获取数据");
return studentService.findById(id);
}
@RequestMapping("/findByAll")
public List<Student> findByAll(){return studentService.findByAll();
}
@RequestMapping("/deleteCache")
public String deleteCache(int id){
System.out.println("StudentController.deleteCache----id="+id+",开始请缓存");
studentService.deleteCache(id);
return "ok";
}
}
(10)测试代码是否正常运行了
编外话:全部数据缓存
课时五十四、Spring Boot Redis版本问题
使用1.5版本引入redis就不能成功,自动下载到本地maven库里面的也是一个”unknown“的文件夹,使用1.3、1.4的就OK...,如下的配置方式:
<dependency>
<groupId> org.springframework.boot</groupId>
<artifactId> spring-boot-starter-redis </artifactId>
</dependency>
<!-- 添加redis支持
不同spring-boot的版本是不一样的,
1.4+ 之前的是:spring-boot-starter-redis
1.4+ spring-boot-starter-redis修改为了:spring-boot-starter-data-redis
1.5+ 直接废弃了. 需要使用:spring-boot-starter-data-redis
-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
</dependency>
课时五十五、Spring Boot集成EHCache实现缓存机制
(1)新建Maven Java Project
新建一个工程名为spring-boot-ehcache的maven java project
(2)在pom.xml中加入依赖包
<!-- spring boot web支持:mvc,aop... -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--
包含支持UI模版(Velocity,FreeMarker,JasperReports),
邮件服务,
脚本服务(JRuby),
缓存Cache(EHCache),
任务计划Scheduling(uartz)。
-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<!-- 集成ehcache需要的依赖-->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
<!-- JPA操作数据库. -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- mysql 数据库驱动. -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
(3)编写Spring Boot启动类;
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
(4)配置application.properties;
########################################################
###datasource ,mysql\u6570\u636e\u5e93\u8fde\u63a5\u914d\u7f6e
########################################################
spring.datasource.url = jdbc:mysql://59818a0fa1d8a.bj.cdb.myqcloud.com:5169/test?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
spring.datasource.username = root
spring.datasource.password = sag18747231218
spring.datasource.driverClassName = com.mysql.jdbc.Driver
spring.datasource.max-active=20
spring.datasource.max-idle=8
spring.datasource.min-idle=8
spring.datasource.initial-size=10
########################################################
### Java Persistence Api \uff0cJPA\u81ea\u52a8\u5efa\u8868\u64cd\u4f5c\u914d\u7f6e
########################################################
# Specify the DBMS
spring.jpa.database = MYSQL
# Show or not log for each sql query
spring.jpa.show-sql = true
# Hibernate ddl auto (create, create-drop, update)
spring.jpa.hibernate.ddl-auto = update
# Naming strategy
spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy
# stripped before adding them to the entity manager)
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
(5)编写缓存配置类以及ehcache.xml配置文件;
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<!--
diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释如下:
user.home – 用户主目录
user.dir – 用户当前工作目录
java.io.tmpdir – 默认临时文件路径
-->
<diskStore path="java.io.tmpdir/Tmp_EhCache" />
<!--
defaultCache: 默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。
-->
<!--
name:缓存名称。
maxElementsInMemory:缓存最大数目
maxElementsOnDisk:硬盘最大缓存个数。
eternal:对象是否永久有效,一但设置了,timeout将不起作用。
overflowToDisk:是否保存到磁盘,当系统当机时
timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
clearOnFlush:内存数量最大时是否清除。
memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。
FIFO,first in first out,这个是大家最熟的,先进先出。
LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
-->
<defaultCache
eternal="false"
maxElementsInMemory="1000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="0"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LRU" /><cache
name="student"
eternal="false"
maxElementsInMemory="100"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="0"
timeToLiveSeconds="300"
memoryStoreEvictionPolicy="LRU" />
</ehcache><?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<!--
diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释如下:
user.home – 用户主目录
user.dir – 用户当前工作目录
java.io.tmpdir – 默认临时文件路径
-->
<diskStore path="java.io.tmpdir/Tmp_EhCache" />
<!--
defaultCache: 默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。
-->
<!--
name:缓存名称。
maxElementsInMemory:缓存最大数目
maxElementsOnDisk:硬盘最大缓存个数。
eternal:对象是否永久有效,一但设置了,timeout将不起作用。
overflowToDisk:是否保存到磁盘,当系统当机时
timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
clearOnFlush:内存数量最大时是否清除。
memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。
FIFO,first in first out,这个是大家最熟的,先进先出。
LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
-->
<defaultCache
eternal="false"
maxElementsInMemory="1000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="0"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LRU" /><cache
name="student"
eternal="false"
maxElementsInMemory="100"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="0"
timeToLiveSeconds="300"
memoryStoreEvictionPolicy="LRU" />
</ehcache>
(6)编写Student实体类进行测试;
@Entity
public class Student implements Serializable{
private static final long serialVersionUID = 1L;
@Id @GeneratedValue(strategy=GenerationType.AUTO)
private int id; //主键.
private String name;
private int gender;
private Date registerTime;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getGender() {
return gender;
}
public void setGender(int gender) {
this.gender = gender;
}
public Date getRegisterTime() {
return registerTime;
}
public void setRegisterTime(Date registerTime) {
this.registerTime = registerTime;
}
}
(7)编写持久类StudentRepository;
public interface StudentRepository extends CrudRepository<Student, Integer> {
}
(8)编写处理类StudentService
@Service
public class StudentService {
@Autowired
private StudentRepository studentRepository;
@Transactional //开启事务.
@Cacheable(value="student",key="'student'+#student.id")
public void save(Student student){
studentRepository.save(student);
}
@Cacheable(value="student",key="'student'+#id")
public Student findById(int id){
System.out.println("不使用缓存,查询数据,并且返回。");
return studentRepository.findOne(id);
}
@CacheEvict(value="student",key="'student'+#id")
public void deleteCache(int id){
//这里不做具体的数据库删除,只清空缓存.
}
}
(9)编写StudentController测试类
@RestController
@RequestMapping("/student")
public class StudentController {
@Autowired
private StudentService studentService;
@RequestMapping("/save")
public Student save(Student student){
studentService.save(student);
return student;
}
@RequestMapping("/findById")
public Student findById(int id){
System.out.println("进入到StudentController的findById");
return studentService.findById(id);
}
@RequestMapping("/deleteCache")
public String deleteCache(int id){
System.out.println("进入到StudentController的deleteCache");
studentService.deleteCache(id);
return "ok";
}
}
@RestController
@RequestMapping("/student")
public class StudentController {
@Autowired
private StudentService studentService;
@RequestMapping("/save")
public Student save(Student student){
studentService.save(student);
return student;
}
@RequestMapping("/findById")
public Student findById(int id){
System.out.println("进入到StudentController的findById");
return studentService.findById(id);
}
@RequestMapping("/deleteCache")
public String deleteCache(int id){
System.out.println("进入到StudentController的deleteCache");
studentService.deleteCache(id);
return "ok";
}
}