spring的缓存(cache)-本地
注:本文篇幅有点长,所以建议各位下载源码学习。(如需要请收藏!转载请声明来源,谢谢!)
代码下载:https://gitee.com/hong99/spring/issues/I1N1DF
什么是缓存(cache)?
缓存简称cache,全称:即高速缓冲存储器(Cache Memory)。
缓存分为:硬件缓存和软件缓存。
硬件缓存有CPU、内存条、硬盘等...以内存条为主,可以参照:
https://baike.baidu.com/item/%E7%BC%93%E5%AD%98
软件缓存:分为客户端缓存和服务端缓存。
客户端缓存:就类似我们常用的浏览器,QQ、微信、手机的应用账号密码等或者,方便不用请求服务端直接展示出来,提高效率。
服务端缓存:某些服务的应用,自身的缓存,当客户端请求的时候直接从缓存中快速获取,而减少直接请求数据库,提升服务端性能和效率。
不管是硬件还是软件缓存最终的目的都是为了提升系统性能和效率,减少不必要的开销。
缓存解决了什么问题?
当用户量小的时候,QPS可能就几个或者几十个,这是没什么问题的,没觉得需要什么缓存,服务、db都坑得住,对响应时间的要求不高。但是当用户量QPS是百万甚至千万的时候,如果这时候服务没有缓存,可以说几乎是服务是秒崩溃的...如下图,若服务直接都去调用db,那db会导致db连接超时,甚至崩溃,接着导致服务异常,最后服务崩溃....
所以缓存主要是解决用户快速访问/请求,以达到更高的用户体验,间接使服务以免由于用户量过大而导致系统奔溃或者出现各种异常,并且提升系统的鲁棒性。
相关概念:
PV(Page View):页面访问量,每次用户访问或者刷新页面都会被计算在内。
QPS(Query Per Second):每秒查询数,每秒系统能够处理的查询请求次数。
TPS(Transactions Per Second):每秒事务数,每秒系统能够处理的事务次数。
缓存的使用场景?缓存有哪些策略?
数据使用的次数非常频繁导致每次都查库或者必须建立缓存以备不时之需。当然缓存也有基于业务场景建立,也有基础安全场景,以及用户体验等。但不管基础何种场景建立缓存的目的是需要非常明确,并且是必要解决因为没有缓存导致的一系列问题。否则乱建缓存可能引发缓存数据不一致,死数据等问题。
缓存创建的场景有两种:被动创建、初始化创建;
被动创建:需要用到的时候才创建;
场景:用户登陆,当第一次登陆的时候需要获取用户信息,先查数据库然后同步到缓存中,当第二次获取用户信息的时候直接从缓存中获取。
初始化创建:随着服务的启动就存在了;
场景:创建新用户的时候就该用户放到缓存中,当用户每次获取的时候直接从缓存中获取,若获取不到再到数据库中获取,减少查询db的次数。
如何避免缓存与数据库数据不一致?
1.当原始数据(数据库)被更新时,同步先删除缓存数据再更新。
2.需要为缓存数据加过期时间;
缓存有哪些策略?
先进先出策略 FIFO(First In,First Out)
最少使用策略 LFU(Least Frequently Used)
最近最少使用策略 LRU(Least Recently Used)
缓存有哪些?有哪些框架?
缓存大致分为两在种,本地缓存和分布式缓存,当然如果考虑分级的话还有一级、二级、三级等缓存,但这里以前种来区别。
本地缓存
hashmap,Guava ,ehcache,spring cache
分布式缓存
redis,memcached
代码下载:https://gitee.com/hong99/spring/issues/I1N1DF
源码实现
项目结构
注:项目大部分代码与ORM中的spring_mybatis_plus一致,请参考!
本地缓存实现
hashmap实现
com.hong.spring.service.IUserService 新增方法
/**
*
* 功能描述:通过缓存查询
*
* @param:
* @return:
* @auther: csh
* @date: 2020/8/27 16:56
*/
User findLocalCacheById(Integer id);
com.hong.spring.service.impl.UserServiceImpl 新增实现
@Override
public User findLocalCacheById(Integer id) {
if(null==id){
log.info("id为空!");
return null;
}
boolean userExist = map.containsKey(id);
if(userExist){
log.info("从缓存获取用户信息:{}", JSONObject.toJSONString(map.get(id)));
return map.get(id);
}else{
User user = userMapper.findById(id);
log.info("从数据库中获取用户信息{}",JSONObject.toJSONString(user));
if(null!=user){
map.put(id,user);
}
return user;
}
}
com.hong.spring.service.UserServiceTest 新增测试
@Test
public void findCacheById(){
for(int i=0;i<10;i++){
log.info("第{}次,获取结果{}",i,JSONObject.toJSONString(userService.findLocalCacheById(1)));
}
}
结果
17:03:10.737 [main] INFO com.hong.spring.service.impl.UserServiceImpl - 从数据库中获取用户信息{"age":100,"id":1,"username":"333"}
17:03:10.738 [main] INFO com.hong.spring.service.UserServiceTest - 第0次,获取结果{"age":100,"id":1,"username":"333"}
17:03:10.738 [main] INFO com.hong.spring.service.impl.UserServiceImpl - 从缓存获取用户信息:{"age":100,"id":1,"username":"333"}
17:03:10.738 [main] INFO com.hong.spring.service.UserServiceTest - 第1次,获取结果{"age":100,"id":1,"username":"333"}
17:03:10.738 [main] INFO com.hong.spring.service.impl.UserServiceImpl - 从缓存获取用户信息:{"age":100,"id":1,"username":"333"}
17:03:10.739 [main] INFO com.hong.spring.service.UserServiceTest - 第2次,获取结果{"age":100,"id":1,"username":"333"}
17:03:10.739 [main] INFO com.hong.spring.service.impl.UserServiceImpl - 从缓存获取用户信息:{"age":100,"id":1,"username":"333"}
17:03:10.739 [main] INFO com.hong.spring.service.UserServiceTest - 第3次,获取结果{"age":100,"id":1,"username":"333"}
17:03:10.739 [main] INFO com.hong.spring.service.impl.UserServiceImpl - 从缓存获取用户信息:{"age":100,"id":1,"username":"333"}
17:03:10.739 [main] INFO com.hong.spring.service.UserServiceTest - 第4次,获取结果{"age":100,"id":1,"username":"333"}
17:03:10.740 [main] INFO com.hong.spring.service.impl.UserServiceImpl - 从缓存获取用户信息:{"age":100,"id":1,"username":"333"}
17:03:10.740 [main] INFO com.hong.spring.service.UserServiceTest - 第5次,获取结果{"age":100,"id":1,"username":"333"}
17:03:10.740 [main] INFO com.hong.spring.service.impl.UserServiceImpl - 从缓存获取用户信息:{"age":100,"id":1,"username":"333"}
17:03:10.740 [main] INFO com.hong.spring.service.UserServiceTest - 第6次,获取结果{"age":100,"id":1,"username":"333"}
17:03:10.740 [main] INFO com.hong.spring.service.impl.UserServiceImpl - 从缓存获取用户信息:{"age":100,"id":1,"username":"333"}
17:03:10.740 [main] INFO com.hong.spring.service.UserServiceTest - 第7次,获取结果{"age":100,"id":1,"username":"333"}
17:03:10.741 [main] INFO com.hong.spring.service.impl.UserServiceImpl - 从缓存获取用户信息:{"age":100,"id":1,"username":"333"}
17:03:10.741 [main] INFO com.hong.spring.service.UserServiceTest - 第8次,获取结果{"age":100,"id":1,"username":"333"}
17:03:10.741 [main] INFO com.hong.spring.service.impl.UserServiceImpl - 从缓存获取用户信息:{"age":100,"id":1,"username":"333"}
17:03:10.741 [main] INFO com.hong.spring.service.UserServiceTest - 第9次,获取结果{"age":100,"id":1,"username":"333"}
总结:hashmap缓存作为缓存有一个缺点就是若没有设置过期时间会导致缓存越堆越多,导致内存被占用过多,而且永远没有失效,数据更新或修改的时候也没有更新到会导致不一致问题,当然也可以在修改的时候将map中的数据移除,但是还是存在没有过期导致死数据问题。
Guava 的实现
简介:Guava是Google的一组核心Java库,其中包括新的集合类型(例如多图和多集),不可变的集合,图形库以及用于并发,I / O,哈希,缓存,基元,字符串等的实用程序!它广泛用于Google的大多数Java项目中,也被许多其他公司广泛使用。
本地实现
com.hong.spring.service.IUserService#findGuavaCacheById
/**
*
* 功能描述:通过guava缓存获取
*
* @param:
* @return:
* @auther: csh
* @date: 2020/8/27 17:53
*/
User findGuavaCacheById(Integer id);
com.hong.spring.service.impl.UserServiceImpl#findGuavaCacheById
@Override
public User findGuavaCacheById(Integer id) {
if(null==id){
log.info("获取guava缓存未传id!");
return null;
}
User user = guavaCache.getIfPresent(id);
if(user!=null){
log.info("从guava缓存获取用户信息:{}", JSONObject.toJSONString(map.get(id)));
return user;
}else{
user = userMapper.findById(id);
log.info("从数据库中获取用户信息{}",JSONObject.toJSONString(user));
if(null!=user){
guavaCache.put(id,user);
}
return user;
}
}
com.hong.spring.service.UserServiceTest#findGuavaCacheById
@Test
public void findGuavaCacheById() throws InterruptedException {
for(int i=0;i<10;i++){
log.info("第{}次,获取结果{}",i,JSONObject.toJSONString(userService.findGuavaCacheById(1)));
}
log.info("睡上10秒再拿!");
Thread.sleep(11000);
log.info("重新获取结果{}",JSONObject.toJSONString(userService.findGuavaCacheById(1)));
}
结果
18:02:20.861 [main] DEBUG org.mybatis.spring.SqlSessionUtils - Creating a new SqlSession
18:02:20.868 [main] DEBUG org.mybatis.spring.SqlSessionUtils - SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@5c748168] was not registered for synchronization because synchronization is not active
18:02:20.878 [main] DEBUG org.mybatis.spring.transaction.SpringManagedTransaction - JDBC Connection [com.mysql.jdbc.JDBC4Connection@47547132] will not be managed by Spring
18:02:20.882 [main] DEBUG com.hong.spring.dao.UserMapper.findById - ==> Preparing: SELECT * FROM user WHERE id = ?
18:02:20.908 [main] DEBUG com.hong.spring.dao.UserMapper.findById - ==> Parameters: 1(Integer)
18:02:20.928 [main] DEBUG com.hong.spring.dao.UserMapper.findById - <== Total: 1
18:02:20.932 [main] DEBUG com.alibaba.druid.pool.PreparedStatementPool - stmt enter cache
18:02:20.933 [main] DEBUG org.mybatis.spring.SqlSessionUtils - Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@5c748168]
18:02:20.950 [main] INFO com.hong.spring.service.impl.UserServiceImpl - 从数据库中获取用户信息{"age":100,"id":1,"username":"333"}
18:02:20.954 [main] INFO com.hong.spring.service.UserServiceTest - 第0次,获取结果{"age":100,"id":1,"username":"333"}
18:02:20.954 [main] INFO com.hong.spring.service.impl.UserServiceImpl - 从guava缓存获取用户信息:null
18:02:20.955 [main] INFO com.hong.spring.service.UserServiceTest - 第1次,获取结果{"age":100,"id":1,"username":"333"}
18:02:20.955 [main] INFO com.hong.spring.service.impl.UserServiceImpl - 从guava缓存获取用户信息:null
18:02:20.955 [main] INFO com.hong.spring.service.UserServiceTest - 第2次,获取结果{"age":100,"id":1,"username":"333"}
18:02:20.955 [main] INFO com.hong.spring.service.impl.UserServiceImpl - 从guava缓存获取用户信息:null
18:02:20.956 [main] INFO com.hong.spring.service.UserServiceTest - 第3次,获取结果{"age":100,"id":1,"username":"333"}
18:02:20.956 [main] INFO com.hong.spring.service.impl.UserServiceImpl - 从guava缓存获取用户信息:null
18:02:20.956 [main] INFO com.hong.spring.service.UserServiceTest - 第4次,获取结果{"age":100,"id":1,"username":"333"}
18:02:20.956 [main] INFO com.hong.spring.service.impl.UserServiceImpl - 从guava缓存获取用户信息:null
18:02:20.956 [main] INFO com.hong.spring.service.UserServiceTest - 第5次,获取结果{"age":100,"id":1,"username":"333"}
18:02:20.957 [main] INFO com.hong.spring.service.impl.UserServiceImpl - 从guava缓存获取用户信息:null
18:02:20.957 [main] INFO com.hong.spring.service.UserServiceTest - 第6次,获取结果{"age":100,"id":1,"username":"333"}
18:02:20.957 [main] INFO com.hong.spring.service.impl.UserServiceImpl - 从guava缓存获取用户信息:null
18:02:20.957 [main] INFO com.hong.spring.service.UserServiceTest - 第7次,获取结果{"age":100,"id":1,"username":"333"}
18:02:20.957 [main] INFO com.hong.spring.service.impl.UserServiceImpl - 从guava缓存获取用户信息:null
18:02:20.958 [main] INFO com.hong.spring.service.UserServiceTest - 第8次,获取结果{"age":100,"id":1,"username":"333"}
18:02:20.958 [main] INFO com.hong.spring.service.impl.UserServiceImpl - 从guava缓存获取用户信息:null
18:02:20.958 [main] INFO com.hong.spring.service.UserServiceTest - 第9次,获取结果{"age":100,"id":1,"username":"333"}
18:02:20.958 [main] INFO com.hong.spring.service.UserServiceTest - 睡上10秒再拿!
18:02:31.968 [main] DEBUG org.mybatis.spring.SqlSessionUtils - Creating a new SqlSession
18:02:31.968 [main] DEBUG org.mybatis.spring.SqlSessionUtils - SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7cf283e1] was not registered for synchronization because synchronization is not active
18:02:31.969 [main] DEBUG org.mybatis.spring.transaction.SpringManagedTransaction - JDBC Connection [com.mysql.jdbc.JDBC4Connection@47547132] will not be managed by Spring
18:02:31.969 [main] DEBUG com.hong.spring.dao.UserMapper.findById - ==> Preparing: SELECT * FROM user WHERE id = ?
18:02:31.969 [main] DEBUG com.hong.spring.dao.UserMapper.findById - ==> Parameters: 1(Integer)
18:02:31.970 [main] DEBUG com.hong.spring.dao.UserMapper.findById - <== Total: 1
18:02:31.970 [main] DEBUG org.mybatis.spring.SqlSessionUtils - Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7cf283e1]
18:02:31.971 [main] INFO com.hong.spring.service.impl.UserServiceImpl - 从数据库中获取用户信息{"age":100,"id":1,"username":"333"}
18:02:31.971 [main] INFO com.hong.spring.service.UserServiceTest - 重新获取结果{"age":100,"id":1,"username":"333"}
总结:可以看出guava的性能非常优秀,而且可以设置过期时间,当然该实现只是guava功能的冰山一角,guava还有很多其他优秀的功能,可以参照以下文章。
参考文章:
api:https://guava.dev/releases/snapshot-jre/api/docs/
github:https://github.com/google/guava
https://blog.csdn.net/pzjtian/article/details/106910046
https://cloud.tencent.com/developer/article/1074135
https://www.cnblogs.com/rickiyang/p/11074159.html
spring集成guava 实现注解方法
applicationContext-mybatis_plus.xml添加如下
<!--开启缓存注解-->
<cache:annotation-driven />
<bean id="cacheManager" class="org.springframework.cache.guava.GuavaCacheManager">
<!--初始化 500个 最大5000个 过期时间10s-->
<property name="cacheSpecification" value="initialCapacity=500,maximumSize=5000,expireAfterAccess=10s,softValues" />
<property name="cacheNames">
<list>
<value>questionCreatedTrack</value>
</list>
</property>
</bean>
com.hong.spring.service.IUserService#findSpringGuavaCacheById
/**
*
* 功能描述:通过spring缓存获取
*
* @param:
* @return:
* @auther: csh
* @date: 2020/8/27 18:54
*/
User findSpringGuavaCacheById(Integer id);
com.hong.spring.service.impl.UserServiceImpl#findSpringGuavaCacheById
@Cacheable(value = "questionCreatedTrack",key="#id",condition = "#id>0")
@Override
public User findSpringGuavaCacheById(Integer id) {
if(null==id){
return null;
}
User user = userMapper.findById(id);
log.info("从数据库中获取用户信息{}",JSONObject.toJSONString(user));
return user;
}
com.hong.spring.service.UserServiceTest#findSpringGuavaCacheById
/**
*
* 功能描述:通过spring获取缓存
*
* @param:
* @return:
* @auther: csh
* @date: 2020/8/27 19:04
*/
@Test
public void findSpringGuavaCacheById() throws InterruptedException {
for(int i=0;i<10;i++){
log.info("第{}次,获取结果{}",i,JSONObject.toJSONString(userService.findSpringGuavaCacheById(1)));
}
log.info("睡上10秒再拿!");
Thread.sleep(11000);
log.info("重新获取结果{}",JSONObject.toJSONString(userService.findGuavaCacheById(1)));
}
结果
19:01:57.450 [main] DEBUG org.mybatis.spring.SqlSessionUtils - Creating a new SqlSession
19:01:57.455 [main] DEBUG org.mybatis.spring.SqlSessionUtils - SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1706a5c9] was not registered for synchronization because synchronization is not active
19:01:57.463 [main] DEBUG org.mybatis.spring.transaction.SpringManagedTransaction - JDBC Connection [com.mysql.jdbc.JDBC4Connection@3971f0fe] will not be managed by Spring
19:01:57.465 [main] DEBUG com.hong.spring.dao.UserMapper.findById - ==> Preparing: SELECT * FROM user WHERE id = ?
19:01:57.483 [main] DEBUG com.hong.spring.dao.UserMapper.findById - ==> Parameters: 1(Integer)
19:01:57.498 [main] DEBUG com.hong.spring.dao.UserMapper.findById - <== Total: 1
19:01:57.500 [main] DEBUG com.alibaba.druid.pool.PreparedStatementPool - stmt enter cache
19:01:57.501 [main] DEBUG org.mybatis.spring.SqlSessionUtils - Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1706a5c9]
19:01:57.517 [main] INFO com.hong.spring.service.impl.UserServiceImpl - 从数据库中获取用户信息{"age":100,"id":1,"username":"333"}
19:01:57.521 [main] INFO com.hong.spring.service.UserServiceTest - 第0次,获取结果{"age":100,"id":1,"username":"333"}
19:01:57.522 [main] INFO com.hong.spring.service.UserServiceTest - 第1次,获取结果{"age":100,"id":1,"username":"333"}
19:01:57.522 [main] INFO com.hong.spring.service.UserServiceTest - 第2次,获取结果{"age":100,"id":1,"username":"333"}
19:01:57.523 [main] INFO com.hong.spring.service.UserServiceTest - 第3次,获取结果{"age":100,"id":1,"username":"333"}
19:01:57.524 [main] INFO com.hong.spring.service.UserServiceTest - 第4次,获取结果{"age":100,"id":1,"username":"333"}
19:01:57.524 [main] INFO com.hong.spring.service.UserServiceTest - 第5次,获取结果{"age":100,"id":1,"username":"333"}
19:01:57.525 [main] INFO com.hong.spring.service.UserServiceTest - 第6次,获取结果{"age":100,"id":1,"username":"333"}
19:01:57.525 [main] INFO com.hong.spring.service.UserServiceTest - 第7次,获取结果{"age":100,"id":1,"username":"333"}
19:01:57.526 [main] INFO com.hong.spring.service.UserServiceTest - 第8次,获取结果{"age":100,"id":1,"username":"333"}
19:01:57.526 [main] INFO com.hong.spring.service.UserServiceTest - 第9次,获取结果{"age":100,"id":1,"username":"333"}
19:01:57.526 [main] INFO com.hong.spring.service.UserServiceTest - 睡上10秒再拿!
19:02:08.527 [main] DEBUG org.mybatis.spring.SqlSessionUtils - Creating a new SqlSession
19:02:08.527 [main] DEBUG org.mybatis.spring.SqlSessionUtils - SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6d2dc9d2] was not registered for synchronization because synchronization is not active
19:02:08.528 [main] DEBUG org.mybatis.spring.transaction.SpringManagedTransaction - JDBC Connection [com.mysql.jdbc.JDBC4Connection@3971f0fe] will not be managed by Spring
19:02:08.528 [main] DEBUG com.hong.spring.dao.UserMapper.findById - ==> Preparing: SELECT * FROM user WHERE id = ?
19:02:08.528 [main] DEBUG com.hong.spring.dao.UserMapper.findById - ==> Parameters: 1(Integer)
19:02:08.551 [main] DEBUG com.hong.spring.dao.UserMapper.findById - <== Total: 1
19:02:08.552 [main] DEBUG org.mybatis.spring.SqlSessionUtils - Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6d2dc9d2]
19:02:08.552 [main] INFO com.hong.spring.service.impl.UserServiceImpl - 从数据库中获取用户信息{"age":100,"id":1,"username":"333"}
19:02:08.553 [main] INFO com.hong.spring.service.UserServiceTest - 重新获取结果{"age":100,"id":1,"username":"333"}
19:02:08.557 [Thread-1] INFO com.alibaba.druid.pool.DruidDataSource - {dataSource-1} closing ...
19:02:08.559 [Thread-1] DEBUG com.alibaba.druid.pool.PreparedStatementPool - stmt exit cache
19:02:08.559 [Thread-1] INFO com.alibaba.druid.pool.DruidDataSource - {dataSource-1} closed
总结:通过spring的cache以guava实现缓存,是spring cache的一种实现,可以完成缓存效果,但是该情况存在一个问题就是增加和修改,比较麻烦需要特定方法实现,比较麻烦。所以通过缓存过期时间来实现更好。关于api请看下文。
参考文章:
https://www.cnblogs.com/fingerboy/p/9549937.html
spring集成spring cache
类图
思维导图
spring 3.1开始支持声名式缓存到4.0后就全面支持JSR-107。其实spring cahce本质不是一个具体的缓存实现方案,只是作为门面来使用类似于事务的注解,具体实现的还是其他缓存框架比如:ehcache、guava、redis等。该功能十分强大,并且非常方便的支持了缓存的使用。相关功能可以参照上图。
相关文章:
官方:https://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/cache.html
官方:https://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/aop.html#aop-aj-ltw-spring
简单的spring cache配置
applicationContext-mybatis_plus_cache.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:cache="http://www.springframework.org/schema/cache"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
<!-- 配置组件扫描 -->
<context:component-scan base-package="com.hong.spring"></context:component-scan>
<!--加载配置文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 开启注解 -->
<context:annotation-config />
<!--开启注解事务-->
<tx:annotation-driven transaction-manager="transactionManager" />
<!--放行静态资源-->
<mvc:default-servlet-handler />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="internalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/pages/" />
<!-- 后缀 -->
<property name="suffix" value=".html" />
<property name="contentType" value="text/html"/>
</bean>
<!--开启mvc注解事务-->
<!-- 定义注解驱动 -->
<mvc:annotation-driven>
<mvc:message-converters>
<!-- 设置支持中文 -->
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/plain;charset=UTF-8</value>
<value>text/html;charset=UTF-8</value>
</list>
</property>
</bean>
<bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<!-- 基础配置 -->
<property name="url" value="${jdbc.url}"></property>
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="username" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
<!-- 关键配置 -->
<!-- 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时 -->
<property name="initialSize" value="3" />
<!-- 最小连接池数量 -->
<property name="minIdle" value="2" />
<!-- 最大连接池数量 -->
<property name="maxActive" value="15" />
<!-- 配置获取连接等待超时的时间 -->
<property name="maxWait" value="10000" />
<!-- 性能配置 -->
<!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->
<property name="poolPreparedStatements" value="true" />
<property name="maxPoolPreparedStatementPerConnectionSize" value="20" />
<!-- 其他配置 -->
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="300000" />
<!-- 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,
执行validationQuery检测连接是否有效。-->
<property name="testWhileIdle" value="true" />
<!-- 这里建议配置为TRUE,防止取到的连接不可用 ,申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。-->
<property name="testOnBorrow" value="true" />
<!-- 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能 -->
<property name="testOnReturn" value="false" />
</bean>
<!--事务管理器-->
<!-- sqlSessionFactory -->
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<!-- 加载 MyBatis 的配置文件 -->
<property name="configLocation" value="classpath:mybatis-plus.xml"/>
<!-- 数据源 -->
<property name="dataSource" ref="dataSource"/>
<!-- 所有配置的mapper文件 -->
<property name="mapperLocations" value="classpath*:com/hong/spring/mapper/*.xml" />
</bean>
<!-- Mapper 扫描器 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 扫描 包下的组件 -->
<property name="basePackage" value="com.hong.spring.dao" />
<!-- 关联mapper扫描器 与 sqlsession管理器 -->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
</bean>
<!--事务配置-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!--开启缓存注解-->
<cache:annotation-driven />
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches">
<set>
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean"
p:name="default" />
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean"
p:name="questionCreatedTrack" />
</set>
</property>
</bean>
</beans>
com.hong.spring.service.UserServiceCaceTest#findSpringGuavaCacheById
package com.hong.spring.service;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hong.spring.entity.User;
import com.hong.spring.utils.DataResponse;
import lombok.extern.log4j.Log4j2;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
/**
* @Auther: csh
* @Date: 2020/8/24 11:17
* @Description:
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext-mybatis_plus_cache.xml")
@Log4j2
public class UserServiceCaceTest {
@Autowired
private IUserService userService;
/**
*
* 功能描述:通过spring获取缓存
*
* @param:
* @return:
* @auther: csh
* @date: 2020/8/27 19:04
*/
@Test
public void findSpringGuavaCacheById() throws InterruptedException {
for(int i=0;i<10;i++){
log.info("第{}次,获取结果{}",i,JSONObject.toJSONString(userService.findSpringGuavaCacheById(1)));
}
log.info("睡上10秒再拿!");
Thread.sleep(11000);
log.info("重新获取结果{}",JSONObject.toJSONString(userService.findSpringGuavaCacheById(1)));
}
@Test
public void deleteCacheTest(){
log.info("获取结果{}",JSONObject.toJSONString(userService.findSpringGuavaCacheById(1)));
userService.deleteCache(1);
log.info("重新获取结果{}",JSONObject.toJSONString(userService.findSpringGuavaCacheById(1)));
}
}
结果
11:39:11.866 [main] INFO com.hong.spring.service.impl.UserServiceImpl - 从数据库中获取用户信息{"age":100,"id":1,"username":"333"}
11:39:11.868 [main] INFO com.hong.spring.service.UserServiceCaceTest - 第0次,获取结果{"age":100,"id":1,"username":"333"}
11:39:11.869 [main] INFO com.hong.spring.service.UserServiceCaceTest - 第1次,获取结果{"age":100,"id":1,"username":"333"}
11:39:11.870 [main] INFO com.hong.spring.service.UserServiceCaceTest - 第2次,获取结果{"age":100,"id":1,"username":"333"}
11:39:11.870 [main] INFO com.hong.spring.service.UserServiceCaceTest - 第3次,获取结果{"age":100,"id":1,"username":"333"}
11:39:11.870 [main] INFO com.hong.spring.service.UserServiceCaceTest - 第4次,获取结果{"age":100,"id":1,"username":"333"}
11:39:11.871 [main] INFO com.hong.spring.service.UserServiceCaceTest - 第5次,获取结果{"age":100,"id":1,"username":"333"}
11:39:11.871 [main] INFO com.hong.spring.service.UserServiceCaceTest - 第6次,获取结果{"age":100,"id":1,"username":"333"}
11:39:11.871 [main] INFO com.hong.spring.service.UserServiceCaceTest - 第7次,获取结果{"age":100,"id":1,"username":"333"}
11:39:11.871 [main] INFO com.hong.spring.service.UserServiceCaceTest - 第8次,获取结果{"age":100,"id":1,"username":"333"}
11:39:11.872 [main] INFO com.hong.spring.service.UserServiceCaceTest - 第9次,获取结果{"age":100,"id":1,"username":"333"}
11:39:11.872 [main] INFO com.hong.spring.service.UserServiceCaceTest - 睡上10秒再拿!
八月 28, 2020 11:39:22 上午 org.springframework.context.support.GenericApplicationContext doClose
信息: Closing org.springframework.context.support.GenericApplicationContext@759d26fb: startup date [Fri Aug 28 11:39:09 CST 2020]; root of context hierarchy
11:39:22.872 [main] INFO com.hong.spring.service.UserServiceCaceTest - 重新获取结果{"age":100,"id":1,"username":"333"}
com.hong.spring.service.UserServiceCaceTest#deleteCacheTest清除缓存:结果
11:53:15.903 [main] INFO com.hong.spring.service.impl.UserServiceImpl - 从数据库中获取用户信息{"age":100,"id":1,"username":"333"}
11:53:15.905 [main] INFO com.hong.spring.service.UserServiceCaceTest - 获取结果{"age":100,"id":1,"username":"333"}
11:53:15.907 [main] INFO com.hong.spring.service.impl.UserServiceImpl - 调用了清除缓存:1
11:53:15.908 [main] DEBUG org.mybatis.spring.SqlSessionUtils - Creating a new SqlSession
11:53:15.909 [main] DEBUG org.mybatis.spring.SqlSessionUtils - SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6fd5717c] was not registered for synchronization because synchronization is not active
11:53:15.910 [main] DEBUG org.mybatis.spring.transaction.SpringManagedTransaction - JDBC Connection [com.mysql.jdbc.JDBC4Connection@5e1218b4] will not be managed by Spring
11:53:15.910 [main] DEBUG com.hong.spring.dao.UserMapper.findById - ==> Preparing: SELECT * FROM user WHERE id = ?
11:53:15.911 [main] DEBUG com.hong.spring.dao.UserMapper.findById - ==> Parameters: 1(Integer)
11:53:15.912 [main] DEBUG com.hong.spring.dao.UserMapper.findById - <== Total: 1
11:53:15.912 [main] DEBUG org.mybatis.spring.SqlSessionUtils - Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6fd5717c]
11:53:15.913 [main] INFO com.hong.spring.service.impl.UserServiceImpl - 从数据库中获取用户信息{"age":100,"id":1,"username":"333"}
11:53:15.913 [main] INFO com.hong.spring.service.UserServiceCaceTest - 重新获取结果{"age":100,"id":1,"username":"333"}
验证:若没有开启 mode="aspectj" 同一类下的方法互调是没有缓存效果,因为aop管理不到。
com.hong.spring.service.IUserService#findByUserId
/**
*
* 功能描述:通过userId
*
* @param:
* @return:
* @auther: csh
* @date: 2020/8/28 14:18
*/
User findByUserId(Integer id);
com.hong.spring.service.impl.UserServiceImpl#findByUserId
@Override
public User findByUserId(Integer id) {
return this.findSpringGuavaCacheById(id);
}
结果如下:
14:20:21.264 [main] DEBUG com.hong.spring.dao.UserMapper.findById - ==> Preparing: SELECT * FROM user WHERE id = ?
14:20:21.290 [main] DEBUG com.hong.spring.dao.UserMapper.findById - ==> Parameters: 1(Integer)
14:20:21.305 [main] DEBUG com.hong.spring.dao.UserMapper.findById - <== Total: 1
14:20:21.307 [main] DEBUG com.alibaba.druid.pool.PreparedStatementPool - stmt enter cache
14:20:21.308 [main] DEBUG org.mybatis.spring.SqlSessionUtils - Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@73044cdf]
14:20:21.323 [main] INFO com.hong.spring.service.impl.UserServiceImpl - 从数据库中获取用户信息{"age":100,"id":1,"username":"333"}
14:20:21.324 [main] INFO com.hong.spring.service.UserServiceCaceTest - 第一次拿:{"age":100,"id":1,"username":"333"}
14:20:21.325 [main] DEBUG org.mybatis.spring.SqlSessionUtils - Creating a new SqlSession
14:20:21.325 [main] DEBUG org.mybatis.spring.SqlSessionUtils - SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1d96d872] was not registered for synchronization because synchronization is not active
14:20:21.326 [main] DEBUG org.mybatis.spring.transaction.SpringManagedTransaction - JDBC Connection [com.mysql.jdbc.JDBC4Connection@58294867] will not be managed by Spring
14:20:21.327 [main] DEBUG com.hong.spring.dao.UserMapper.findById - ==> Preparing: SELECT * FROM user WHERE id = ?
14:20:21.327 [main] DEBUG com.hong.spring.dao.UserMapper.findById - ==> Parameters: 1(Integer)
14:20:21.328 [main] DEBUG com.hong.spring.dao.UserMapper.findById - <== Total: 1
14:20:21.329 [main] DEBUG org.mybatis.spring.SqlSessionUtils - Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1d96d872]
14:20:21.329 [main] INFO com.hong.spring.service.impl.UserServiceImpl - 从数据库中获取用户信息{"age":100,"id":1,"username":"333"}
14:20:21.329 [main] INFO com.hong.spring.service.UserServiceCaceTest - 第二次拿:{"age":100,"id":1,"username":"333"}
可以发现:第二次查询还是从库里面查询的导致缓存失效,所以默认的proxy是同类下面aop管理不到。
applicationContext-mybatis_plus_cache.xml添加mode="aspectj"尝试
<!--开启缓存注解-->
<cache:annotation-driven cache-manager="cacheManager" mode="aspectj" />
新增
<cache:advice id="cacheAdvice" cache-manager="cacheManager">
<cache:caching cache="questionCreatedTrack">
<cache:cacheable method="findById" key="#p0"/>
<cache:cacheable method="find*" key="#p0"/>
<cache:cache-evict method="deleteAll" all-entries="true"/>
</cache:caching>
</cache:advice>
<!--proxy-target-class="false" false基础jdk方法代理 true基于类代理-->
<aop:config>
<aop:advisor advice-ref="cacheAdvice" pointcut="execution(* com.hong.spring.service.*.*(..))" />
</aop:config>
结果
16:38:45.912 [main] INFO com.hong.spring.service.impl.UserServiceImpl - 从数据库中获取用户信息{"age":100,"id":1,"username":"333"}
16:38:45.913 [main] INFO com.hong.spring.service.UserServiceCaceTest - 第一次拿:{"age":100,"id":1,"username":"333"}
16:38:45.914 [main] INFO com.hong.spring.service.UserServiceCaceTest - 第二次拿:{"age":100,"id":1,"username":"333"}
参考文章:
中文翻译:https://www.docs4dev.com/docs/zh/spring-framework/4.3.21.RELEASE/reference/cache.html
官方:https://docs.spring.io/spring/docs/4.3.11.RELEASE/spring-framework-reference/html/cache.html
缺点:
- 若没有开启 mode="aspectj" 默认是proxy @Cacheable必须是public才能生效,会导致如果私有方法想有缓存,只能手动实现;
- 没有锁的概念,会导致脏读;
- 不针对多进程应用环境进行管理;
- 无法支持多级缓存;
- 若没有开启 mode="aspectj" 同一类下的方法互调是没有缓存效果,因为aop管理不到。
spring集成ehcache
EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点。
在pom中引入jar包
<!--引入ehcache-->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.10.2</version>
</dependency>
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">
<!-- 磁盘缓存位置 -->
<diskStore path="java.io.tmpdir/ehcache"/>
<!-- 默认缓存 -->
<defaultCache
maxEntriesLocalHeap="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
maxEntriesLocalDisk="10000000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"/>
<!-- questionCreatedTrack缓存 -->
<cache name="questionCreatedTrack"
maxElementsInMemory="1000"
eternal="false"
timeToIdleSeconds="5"
timeToLiveSeconds="5"
overflowToDisk="false"
memoryStoreEvictionPolicy="LRU"/>
</ehcache>
配置说明:
name |
cache的唯一标识 |
---|---|
maxElementsInMemory |
最大创建条数 |
eternal |
缓存是否是永久的,默认false |
timeToLiveSeconds |
缓存的存活时间 |
timeToIdleSeconds |
多长时间不访问就清楚该缓存 |
overflowToDisk |
内存不足是否写入磁盘,默认False |
maxElementsOnDisk |
持久化到硬盘最大条数 |
memoryStoreEvictionPolicy |
缓存满了后,清除缓存的规则,自带三种:FIFO(先进先出),LFU(最少使用),LRU(最近最少使用) |
diskSpoolBufferSizeMB |
磁盘缓存的缓存区大小,默认30M |
diskExpiryThreadIntervalSeconds |
磁盘失效线程时间间隔 |
applicationContext-mybatis_plus_ehcache.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:cache="http://www.springframework.org/schema/cache"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 配置组件扫描 -->
<context:component-scan base-package="com.hong.spring"></context:component-scan>
<!--加载配置文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 开启注解 -->
<context:annotation-config />
<!--开启注解事务-->
<tx:annotation-driven mode="aspectj" />
<!--放行静态资源-->
<mvc:default-servlet-handler />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="internalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/pages/" />
<!-- 后缀 -->
<property name="suffix" value=".html" />
<property name="contentType" value="text/html"/>
</bean>
<!--开启mvc注解事务-->
<!-- 定义注解驱动 -->
<mvc:annotation-driven>
<mvc:message-converters>
<!-- 设置支持中文 -->
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/plain;charset=UTF-8</value>
<value>text/html;charset=UTF-8</value>
</list>
</property>
</bean>
<bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<!-- 基础配置 -->
<property name="url" value="${jdbc.url}"></property>
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="username" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
<!-- 关键配置 -->
<!-- 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时 -->
<property name="initialSize" value="3" />
<!-- 最小连接池数量 -->
<property name="minIdle" value="2" />
<!-- 最大连接池数量 -->
<property name="maxActive" value="15" />
<!-- 配置获取连接等待超时的时间 -->
<property name="maxWait" value="10000" />
<!-- 性能配置 -->
<!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->
<property name="poolPreparedStatements" value="true" />
<property name="maxPoolPreparedStatementPerConnectionSize" value="20" />
<!-- 其他配置 -->
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="300000" />
<!-- 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,
执行validationQuery检测连接是否有效。-->
<property name="testWhileIdle" value="true" />
<!-- 这里建议配置为TRUE,防止取到的连接不可用 ,申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。-->
<property name="testOnBorrow" value="true" />
<!-- 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能 -->
<property name="testOnReturn" value="false" />
</bean>
<!--事务管理器-->
<!-- sqlSessionFactory -->
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<!-- 加载 MyBatis 的配置文件 -->
<property name="configLocation" value="classpath:mybatis-plus.xml"/>
<!-- 数据源 -->
<property name="dataSource" ref="dataSource"/>
<!-- 所有配置的mapper文件 -->
<property name="mapperLocations" value="classpath*:com/hong/spring/mapper/*.xml" />
</bean>
<!-- Mapper 扫描器 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 扫描 包下的组件 -->
<property name="basePackage" value="com.hong.spring.dao" />
<!-- 关联mapper扫描器 与 sqlsession管理器 -->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
</bean>
<!--事务配置-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!--开启缓存注解-->
<cache:annotation-driven mode="aspectj" />
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager" p:cache-manager-ref="ehcache"/>
<!-- EhCache library setup -->
<bean id="ehcache"
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:config-location="classpath:ehcache.xml"/>
<cache:advice id="cacheAdvice" cache-manager="cacheManager">
<cache:caching cache="questionCreatedTrack">
<cache:cacheable method="findById" key="#p0"/>
<cache:cacheable method="find*" key="#p0"/>
<cache:cache-evict method="deleteAll" all-entries="true"/>
</cache:caching>
</cache:advice>
<!--proxy-target-class="false" false基础jdk方法代理 true基于类代理-->
<aop:config>
<aop:advisor advice-ref="cacheAdvice" pointcut="execution(* com.hong.spring.service.*.*(..))" />
</aop:config>
</beans>
com.hong.spring.service.UserServiceEhCaceTest#testThis
package com.hong.spring.service;
import com.alibaba.fastjson.JSONObject;
import com.hong.spring.entity.User;
import lombok.extern.log4j.Log4j2;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @Auther: csh
* @Date: 2020/8/24 11:17
* @Description:
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext-mybatis_plus_ehcache.xml")
@Log4j2
public class UserServiceEhCaceTest {
@Autowired
private IUserService userService;
/**
*
* 功能描述:通过spring获取缓存
*
* @param:
* @return:
* @auther: csh
* @date: 2020/8/27 19:04
*/
@Test
public void findSpringGuavaCacheById() throws InterruptedException {
for(int i=0;i<10;i++){
log.info("第{}次,获取结果{}",i,JSONObject.toJSONString(userService.findSpringGuavaCacheById(1)));
}
log.info("睡上10秒再拿!");
Thread.sleep(11000);
log.info("重新获取结果{}",JSONObject.toJSONString(userService.findSpringGuavaCacheById(1)));
}
@Test
public void deleteCacheTest(){
log.info("获取结果{}",JSONObject.toJSONString(userService.findSpringGuavaCacheById(1)));
userService.deleteCache(1);
log.info("重新获取结果{}",JSONObject.toJSONString(userService.findSpringGuavaCacheById(1)));
}
@Test
public void testThis(){
User user = userService.findByUserId(1);
log.info("第一次拿:"+JSONObject.toJSONString(user));
User user2 = userService.findByUserId(1);
log.info("第二次拿:"+JSONObject.toJSONString(user2));
}
}
结果
17:22:56.557 [main] DEBUG org.mybatis.spring.SqlSessionUtils - Creating a new SqlSession
17:22:56.563 [main] DEBUG org.mybatis.spring.SqlSessionUtils - SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@5b5e7036] was not registered for synchronization because synchronization is not active
17:22:56.570 [main] DEBUG org.mybatis.spring.transaction.SpringManagedTransaction - JDBC Connection [com.mysql.jdbc.JDBC4Connection@712cfb63] will not be managed by Spring
17:22:56.573 [main] DEBUG com.hong.spring.dao.UserMapper.findById - ==> Preparing: SELECT * FROM user WHERE id = ?
17:22:56.594 [main] DEBUG com.hong.spring.dao.UserMapper.findById - ==> Parameters: 1(Integer)
17:22:56.605 [main] DEBUG com.hong.spring.dao.UserMapper.findById - <== Total: 1
17:22:56.607 [main] DEBUG com.alibaba.druid.pool.PreparedStatementPool - stmt enter cache
17:22:56.608 [main] DEBUG org.mybatis.spring.SqlSessionUtils - Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@5b5e7036]
17:22:56.620 [main] INFO com.hong.spring.service.impl.UserServiceImpl - 从数据库中获取用户信息{"age":100,"id":1,"username":"333"}
17:22:56.621 [main] INFO com.hong.spring.service.UserServiceEhCaceTest - 第一次拿:{"age":100,"id":1,"username":"333"}
17:22:56.622 [main] INFO com.hong.spring.service.UserServiceEhCaceTest - 第二次拿:{"age":100,"id":1,"username":"333"}
总结:ehcache的性能的确也是非常优越,同样也是支持集群的,所以这点区别于springCache与guava两种。但是ehcache与java绑得太死了,不太建议作为集群方案,并且实现起来非常麻烦。
参考文章:
官网:http://www.ehcache.org/documentation/configuration/configuration
相关说明:https://blog.csdn.net/angjunqiang/article/details/43155437
源码学习:https://www.iteye.com/blog/distantlight1-2253447
最后
本地缓存中,建议还是以guava来做并且通过手动方式来实现,这样的话有利于维护,也不需要去配置spring或xml,并且可以作为一级缓存,像api层面缓存。而spring cache可以单 独来使用,也可以结合其他缓存来使用,但一般项目中较少使用到这样的结合,毕竟,如果一级缓存可以直接考虑用guava手动来配置,而分布式只直接用redis,关于分布式缓存考虑篇幅,下文完善。
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- Thinkphp5.0 框架的请求方式与响应方式分析
- Yii框架视图、视图布局、视图数据块操作示例
- 用php定义一个数组最简单的方法
- laravel-admin自动生成模块,及相关基础配置方法
- laravel-admin select框默认选中的方法
- Laravel-admin之修改操作日志的方法
- php使用curl伪造浏览器访问操作示例
- 关于laravel后台模板laravel-admin select框的使用详解
- 基于Laravel-admin 后台的自定义页面用法详解
- php解决约瑟夫环算法实例分析
- 浅谈laravel-admin的sortable和orderby使用问题
- 使用composer安装使用thinkphp6.0框架问题【视频教程】
- 基于laravel-admin 后台 列表标签背景的使用方法
- 解决laravel-admin 自己新建页面里 js 需要刷新一次的问题
- laravel-admin 中列表筛选方法