Spring Boot中使用缓存
随着时间的积累,应用的使用用户不断增加,数据规模也越来越大,往往数据库查询操作会成为影响用户使用体验的瓶颈,此时使用缓存往往是解决这一问题非常好的手段之一。 原始的使用缓存的方式如下:这样的缓存使用方式将数据读取后,主动对缓存进行更新操作,这样的方式使用方便,但是代码的耦合性高,代码侵入性强。 复制代码
1 /**
2 * 使用缓存以id为字样,如果id所对应的缓存信息已经存在,则不会再读db
3 * @param id
4 * @return
5 */
6 public UserInfo getUserInfoById(int id){
7 UserInfo userInfo = (UserInfo) redisTemplate.opsForValue().get(id+"");
8 if(userInfo != null){
9 return userInfo;
10 }
11 System.out.println("若下面没出现“无缓存的时候调用”字样且能打印出数据表示测试成功");
12 UserInfo userInfo1 = userInfoDao.findById(id);
13 ValueOperations<String, UserInfo> valueOperations = redisTemplate.opsForValue();
14 valueOperations.set(id+"", userInfo1);
15 return userInfo1;
16 }
17
18 @Transactional
19 public int saveUserInfo(UserInfo userInfo){
20 //更新缓存
21 userInfoDao.saveUserInfo(userInfo);
22 int id = userInfo.getId();
23 //userInfo 里面的id值已经发生了变化
24 System.out.println(userInfo.getId());
25 ValueOperations<String, UserInfo> valueOperations = redisTemplate.opsForValue();
26 valueOperations.set(id+"", userInfo);
27 redisTemplate.opsForValue().set(id+"", userInfo);
28 return id;
29 }
复制代码
Spring 3开始提供了强大的基于注解的缓存支持,可以通过注解配置方式低侵入的给原有Spring应用增加缓存功能,提高数据访问性能。 在Spring Boot中对于缓存的支持,提供了一系列的自动化配置,使我们可以非常方便的使用缓存。下面我们通过一个简单的例子来展示,我们是如何给一个既有应用增加缓存功能的。 引入缓存 复制代码
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
复制代码
在application.preoperties中定义redis的配置 复制代码
# REDIS (RedisProperties)
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=0
复制代码 自定义缓存,本文使用redis作为缓存,自定义缓存配置,继承CachingConfigurerSupport类 复制代码
1 /**
2 * 自定义缓存配置文件,继承 CachingConfigurerSupport
3 * Created by huanl on 2017/8/22.
4 */
5 @Configuration
6 @EnableCaching
7 public class RedisConfig extends CachingConfigurerSupport{
8 public RedisConfig() {
9 super();
10 }
11
12 /**
13 * 指定使用哪一种缓存
14 * @param redisTemplate
15 * @return
16 */
17 @Bean
18 public CacheManager cacheManager(RedisTemplate<?,?> redisTemplate) {
19 RedisCacheManager rcm = new RedisCacheManager(redisTemplate);
20 return rcm;
21 }
22
23 /**
24 * 指定默认的key生成方式
25 * @return
26 */
27 @Override
28 public KeyGenerator keyGenerator() {
29 KeyGenerator keyGenerator = new KeyGenerator() {
30 @Override
31 public Object generate(Object o, Method method, Object... objects) {
32 StringBuilder sb = new StringBuilder();
33 sb.append(o.getClass().getName());
34 sb.append(method.getName());
35 for (Object obj : objects) {
36 sb.append(obj.toString());
37 }
38 return sb.toString();
39 }
40 };
41 return keyGenerator;
42 }
43
44 @Override
45 public CacheResolver cacheResolver() {
46 return super.cacheResolver();
47 }
48
49 @Override
50 public CacheErrorHandler errorHandler() {
51 return super.errorHandler();
52 }
53
54 /**
55 * redis 序列化策略 ,通常情况下key值采用String序列化策略
56 * StringRedisTemplate默认采用的是String的序列化策略,保存的key和value都是采用此策略序列化保存的。StringRedisSerializer
57 * RedisTemplate默认采用的是JDK的序列化策略,保存的key和value都是采用此策略序列化保存的。JdkSerializationRedisSerializer
58 * @param factory
59 * @return
60 */
61 @Bean
62 public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory factory){
63 RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
64 redisTemplate.setConnectionFactory(factory);
65
66 // // 使用Jackson2JsonRedisSerialize 替换默认序列化
67 // Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
68 // ObjectMapper om = new ObjectMapper();
69 // om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
70 // om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
71 // jackson2JsonRedisSerializer.setObjectMapper(om);
72 //
73 //
74 // //设置value的序列化方式
75 // redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
76 // //设置key的序列化方式
77 // redisTemplate.setKeySerializer(new StringRedisSerializer());
78 // redisTemplate.setHashKeySerializer(new StringRedisSerializer());
79 // redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
80
81 //使用fastJson作为默认的序列化方式
82 GenericFastJsonRedisSerializer genericFastJsonRedisSerializer = new GenericFastJsonRedisSerializer();
83 redisTemplate.setDefaultSerializer(genericFastJsonRedisSerializer);
84 redisTemplate.setValueSerializer(genericFastJsonRedisSerializer);
85 redisTemplate.setKeySerializer(new StringRedisSerializer());
86 redisTemplate.setHashValueSerializer(genericFastJsonRedisSerializer);
87 redisTemplate.setHashKeySerializer(new StringRedisSerializer());
88 redisTemplate.afterPropertiesSet();
89
90 return redisTemplate;
91
92 }
93
94 /**
95 * 转换返回的object为json
96 * @return
97 */
98 @Bean
99 public HttpMessageConverters fastJsonHttpMessageConverters(){
100 // 1、需要先定义一个converter 转换器
101 FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
102 // 2、添加fastJson 的配置信息,比如:是否要格式化返回的json数据
103 FastJsonConfig fastJsonConfig = new FastJsonConfig();
104 fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
105 // 3、在convert 中添加配置信息
106 fastConverter.setFastJsonConfig(fastJsonConfig);
107 // 4、将convert 添加到converters当中
108 HttpMessageConverter<?> converter = fastConverter;
109 return new HttpMessageConverters(converter);
110 }
111
112
113 }
复制代码
在Spring Boot主类中增加@EnableCaching注解开启缓存功能 复制代码
1 @SpringBootApplication
2 @Import(RedisConfig.class)
3 @MapperScan("com.redistest.dao")
4 @EnableCaching
5 public class RedisApplication {
6
7 public static void main(String[] args) {
8 SpringApplication.run(RedisApplication.class, args);
9 }
10 }
复制代码
在Service类中使用缓存 复制代码
@Service
public class UserInfoService { @Autowired
private UserInfoDao userInfoDao; @Autowired
private RedisTemplate redisTemplate; /**
* 优先从缓存中获取数据,如果缓存中不存在,则从db中读取,读取后将结果按照key存入缓存
* @param id
* @return
*/
@Cacheable(value = "user", key="#id + 'findById'")
public UserInfo getUserInfoByIDNew(int id){
return userInfoDao.findById(id);
} /**
* 使用缓存以id为字样,如果id所对应的缓存信息已经存在,则不会再读db
* @param id
* @return
*/
public UserInfo getUserInfoById(int id){
UserInfo userInfo = (UserInfo) redisTemplate.opsForValue().get(id+"");
if(userInfo != null){
return userInfo;
}
System.out.println("若下面没出现“无缓存的时候调用”字样且能打印出数据表示测试成功");
UserInfo userInfo1 = userInfoDao.findById(id);
ValueOperations<String, UserInfo> valueOperations = redisTemplate.opsForValue();
valueOperations.set(id+"", userInfo1);
return userInfo1;
} @Transactional
public int saveUserInfo(UserInfo userInfo){
//更新缓存
userInfoDao.saveUserInfo(userInfo);
int id = userInfo.getId();
//userInfo 里面的id值已经发生了变化
System.out.println(userInfo.getId());
ValueOperations<String, UserInfo> valueOperations = redisTemplate.opsForValue();
valueOperations.set(id+"", userInfo);
redisTemplate.opsForValue().set(id+"", userInfo);
return id;
} /**
*
* @param userInfo
* @return
*/
@Transactional
//更新后删除指定值的缓存,获取值得时候默认从db中获取
//@CacheEvict(value = "user", key="#userInfo.id+'findById'")
//配置于函数上,能够根据参数定义条件来进行缓存,它与@Cacheable不同的是,它每次都会真是调用函数,所以主要用于数据新增和修改操作上。它的参数与@Cacheable类似,具体功能可参考上面对@Cacheable参数的解析
@CachePut(value = "user", key = "#userInfo.id+'findById'")
public UserInfo updateUserInfo(UserInfo userInfo){
userInfoDao.updateUserInfo(userInfo);
int id = userInfo.getId();
redisTemplate.opsForValue().set(id+"", userInfo);
return userInfo;
}
}
复制代码
使用的MybatisDao 复制代码
@Mapper
@Component
public interface UserInfoDao {
@Select(value = "select * from t_t_user where id=#{id}")
public UserInfo findById(int id); public int saveUserInfo(UserInfo userInfo); public int updateUserInfo(UserInfo userInfo);
}
复制代码
mapper文件,在application.properties文件中定义mapper文件的位置 mybatis.mapper-locations=mapper.xml 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--命名空间:分类管理sql隔离,方便管理-->
<mapper namespace="com.redistest.dao.UserInfoDao"> <!--插入-->
<insert id="saveUserInfo" useGeneratedKeys="true" keyProperty="id" keyColumn="id" parameterType="com.redistest.domain.UserInfo">
INSERT INTO t_t_user (name, password, salt, role) VALUES (#{name}, #{password}, #{salt}, #{role})
</insert> <!--更新-->
<update id="updateUserInfo" parameterType="com.redistest.domain.UserInfo">
UPDATE t_t_user set name=#{name}, password=#{password}, salt=#{salt}, role=#{role} where id=#{id}
</update>
</mapper>
复制代码
Cache注解详解
回过头来我们再来看,这里使用到的两个注解分别作了什么事情。 @CacheConfig:主要用于配置该类中会用到的一些共用的缓存配置。在这里@CacheConfig(cacheNames = "users"):配置了该数据访问对象中返回的内容将存储于名为users的缓存对象中,我们也可以不使用该注解,直接通过@Cacheable自己配置缓存集的名字来定义。 @Cacheable:配置了findByName函数的返回值将被加入缓存。同时在查询时,会先从缓存中获取,若不存在才再发起对数据库的访问。该注解主要有下面几个参数: value、cacheNames:两个等同的参数(cacheNames为Spring 4新增,作为value的别名),用于指定缓存存储的集合名。由于Spring 4中新增了@CacheConfig,因此在Spring 3中原本必须有的value属性,也成为非必需项了
key:缓存对象存储在Map集合中的key值,非必需,缺省按照函数的所有参数组合作为key值,若自己配置需使用SpEL表达式,比如:@Cacheable(key = "#p0"):使用函数第一个参数作为缓存的key值,更多关于SpEL表达式的详细内容可参考官方文档
condition:缓存对象的条件,非必需,也需使用SpEL表达式,只有满足表达式条件的内容才会被缓存,比如:@Cacheable(key = "#p0", condition = "#p0.length() < 3"),表示只有当第一个参数的长度小于3的时候才会被缓存,若做此配置上面的AAA用户就不会被缓存,读者可自行实验尝试。
unless:另外一个缓存条件参数,非必需,需使用SpEL表达式。它不同于condition参数的地方在于它的判断时机,该条件是在函数被调用之后才做判断的,所以它可以通过对result进行判断。
keyGenerator:用于指定key生成器,非必需。若需要指定一个自定义的key生成器,我们需要去实现org.springframework.cache.interceptor.KeyGenerator接口,并使用该参数来指定。需要注意的是:该参数与key是互斥的
cacheManager:用于指定使用哪个缓存管理器,非必需。只有当有多个时才需要使用
cacheResolver:用于指定使用那个缓存解析器,非必需。需通过org.springframework.cache.interceptor.CacheResolver接口来实现自己的缓存解析器,并用该参数指定。
除了这里用到的两个注解之外,还有下面几个核心注解: @CachePut:配置于函数上,能够根据参数定义条件来进行缓存,它与@Cacheable不同的是,它每次都会真是调用函数,所以主要用于数据新增和修改操作上。它的参数与@Cacheable类似,具体功能可参考上面对@Cacheable参数的解析
@CacheEvict:配置于函数上,通常用在删除方法上,用来从缓存中移除相应数据。除了同@Cacheable一样的参数之外,它还有下面两个参数:
allEntries:非必需,默认为false。当为true时,会移除所有数据
beforeInvocation:非必需,默认为false,会在调用方法之后移除数据。当为true时,会在调用方法之前移除数据。
缓存配置
完成了上面的缓存实验之后,可能大家会问,那我们在Spring Boot中到底使用了什么缓存呢? 在Spring Boot中通过@EnableCaching注解自动化配置合适的缓存管理器(CacheManager),Spring Boot根据下面的顺序去侦测缓存提供者: Generic
JCache (JSR-107)
EhCache 2.x
Hazelcast
Infinispan
Redis
Guava
Simple
除了按顺序侦测外,我们也可以通过配置属性spring.cache.type来强制指定。我们可以通过debug调试查看cacheManager对象的实例来判断当前使用了什么缓存。 本文中不对所有的缓存做详细介绍,下面以常用的EhCache为例,看看如何配置来使用EhCache进行缓存管理。 在Spring Boot中开启EhCache非常简单,只需要在工程中加入ehcache.xml配置文件并在pom.xml中增加ehcache依赖,框架只要发现该文件,就会创建EhCache的缓存管理器。 在src/main/resources目录下创建:ehcache.xml
1
2
3
4
5
6
7
8
9
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd"> <cache name="users"
maxEntriesLocalHeap="200"
timeToLiveSeconds="600">
</cache> </ehcache>
在pom.xml中加入
1
2
3
4
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
完成上面的配置之后,再通过debug模式运行单元测试,观察此时CacheManager已经是EhCacheManager实例,说明EhCache开启成功了。 对于EhCache的配置文件也可以通过application.properties文件中使用spring.cache.ehcache.config属性来指定,比如: 1
spring.cache.ehcache.config=classpath:config/another-config.xml

  

Spring Boot中使用缓存的更多相关文章

  1. Spring Boot中的缓存支持(一)注解配置与EhCache使用

    Spring Boot中的缓存支持(一)注解配置与EhCache使用 随着时间的积累,应用的使用用户不断增加,数据规模也越来越大,往往数据库查询操作会成为影响用户使用体验的瓶颈,此时使用缓存往往是解决 ...

  2. 在Spring Boot中使用数据缓存

    春节就要到了,在回家之前要赶快把今年欠下的技术债还清.so,今天继续.Spring Boot前面已经预热了n篇博客了,今天我们来继续看如何在Spring Boot中解决数据缓存问题.本篇博客是以初识在 ...

  3. Spring Boot中使用EhCache实现缓存支持

     SpringBoot提供数据缓存功能的支持,提供了一系列的自动化配置,使我们可以非常方便的使用缓存.,相信非常多人已经用过cache了.因为数据库的IO瓶颈.一般情况下我们都会引入非常多的缓存策略, ...

  4. Spring Boot 中集成 Redis 作为数据缓存

    只添加注解:@Cacheable,不配置key时,redis 中默认存的 key 是:users::SimpleKey [](1.redis-cli 中,通过命令:keys * 查看:2.key:缓存 ...

  5. spring boot(三):Spring Boot中Redis的使用

    spring boot对常用的数据库支持外,对nosql 数据库也进行了封装自动化. redis介绍 Redis是目前业界使用最广泛的内存数据存储.相比memcached,Redis支持更丰富的数据结 ...

  6. springboot(十一):Spring boot中mongodb的使用

    mongodb是最早热门非关系数据库的之一,使用也比较普遍,一般会用做离线数据分析来使用,放到内网的居多.由于很多公司使用了云服务,服务器默认都开放了外网地址,导致前一阵子大批 MongoDB 因配置 ...

  7. springboot(三):Spring boot中Redis的使用

    spring boot对常用的数据库支持外,对nosql 数据库也进行了封装自动化. redis介绍 Redis是目前业界使用最广泛的内存数据存储.相比memcached,Redis支持更丰富的数据结 ...

  8. Spring Boot 使用Redis缓存

    本文示例源码,请看这里 Spring Cache的官方文档,请看这里 缓存存储 Spring 提供了很多缓存管理器,例如: SimpleCacheManager EhCacheCacheManager ...

  9. Spring Boot中使用 Spring Security 构建权限系统

    Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架.它提供了一组可以在Spring应用上下文中配置的Bean,为应用系统提供声明式的安全 ...

随机推荐

  1. shiro认证策略,授权

    有具体问题的可以参考之前的关于shiro的博文,关于shiro的博文均是一次工程的内容 ! 认证策略: 修改认证策略: applicationContext.xml <!-- 认证器 --> ...

  2. Python入门基础:代码的编码风格

    每种语言都有自己的编码风格,对于Python这种比较注重于空格的影响的代码而言,其风格也是相当重要的. 主要包括以下几点: 1:使用 4 空格缩进,而非 TAB  .在小缩进(可以嵌套更深)和大缩进( ...

  3. ARM linux电源管理——Cortex A系列CPU(32位)睡眠和唤醒的底层汇编实现

    ARM linux电源管理——Cortex A系列CPU(32位)睡眠和唤醒的底层汇编实现 承接 http://www.wowotech.net/pm_subsystem/suspend_and_re ...

  4. Before start of result set

    ResultSet:在处理结果集的时候出现了问题. 解决办法:while(rs.next())

  5. Azure服务器上不了外网

    将服务器的DNS改成223.5.5.5或者223.6.6.6

  6. Context initialization failed org.springframework.beans.factory.BeanCreationException

    严重: Context initialization failed org.springframework.beans.factory.BeanCreationException: Error cre ...

  7. Oracle的分析函数

    Oracle的分析函数row_number(),rank(),dense_rank()的用法与区别 比如查询工资排名第7的员工信息,可以用分析函数来做. --查询工资排名第7的员工信息select * ...

  8. Spring入门第一课:Spring基础与配置Bean

    1.入门 Spring是简化java开发的一个框架,其中IoC和AOP是Spring的两个重要核心.由于Spring是非侵入性的,通过Ioc容器来管理bean的生命周期,还整合了许多其他的优秀框架,所 ...

  9. Spring知识点小结(四)

    一.JdbcTemplate(jdbc模版--抽取的工具) web阶段DBUtils:        QueryRunner runner = new QueryRunner(dataSource); ...

  10. React最佳实践(1)

    React最佳实践不敢妄谈,但最差实践非知乎莫属. 旧版知乎看起来土了点,但体验流畅,起码用起来舒服. 新版知乎看起来UI现代化,技术实现上采用了React,但是可能因为知乎缺钱,请不起高水平的前端工 ...