前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >spring redis实现注解缓存

spring redis实现注解缓存

作者头像
叔牙
发布2020-11-19 14:46:26
7480
发布2020-11-19 14:46:26
举报
文章被收录于专栏:一个执拗的后端搬砖工

使用缓存的方式很多,有基于工具类手动操作的,也有基于注解的,各有千秋,接下来将借助spring+redis实现基于注解的缓存使用.

一、新建maven项目并添加依赖

新建一个简单的maven项目,引入spring最简依赖以及mysql驱动和redis客户端等依赖:

<!-- mysql驱动 -->

<dependency>

<groupId>mysql</groupId>

<artifactId>mysql-connector-java</artifactId>

<version>5.1.41</version>

</dependency>

<!-- spring-jdbc操作数据库 -->

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-jdbc</artifactId>

<version>4.3.7.RELEASE</version>

</dependency>

<!-- 阿里开源数据库连接池 -->

<dependency>

<groupId>com.alibaba</groupId>

<artifactId>druid</artifactId>

<version>1.1.2</version>

</dependency>

<!-- spring操作redis依赖 -->

<dependency>

<groupId>org.springframework.data</groupId>

<artifactId>spring-data-redis</artifactId>

<version>1.7.6.RELEASE</version>

</dependency>

<!-- redis客户端 -->

<dependency>

<groupId>redis.clients</groupId>

<artifactId>jedis</artifactId>

<version>2.8.2</version>

</dependency>

<!-- json格式化工具 -->

<dependency>

<groupId>com.alibaba</groupId>

<artifactId>fastjson</artifactId>

<version>1.2.28</version>

</dependency>

二、添加基本配置文件

添加上述三个配置文件,jdbc.properties是mysql连接属性,redis.properties是redis连接属性,spring-root.xml是主配文件,内容分别如下:

jdbc.properties:

driverClassName=com.mysql.jdbc.Driver

url=jdbc:mysql://localhost:3306/test

username=root

password=root

filters=stat

initialSize=2

maxActive=300

maxWait=60000

timeBetweenEvictionRunsMillis=60000

minEvictableIdleTimeMillis=300000

validationQuery=SELECT 1

testWhileIdle=true

testOnBorrow=false

testOnReturn=false

poolPreparedStatements=false

maxPoolPreparedStatementPerConnectionSize=200

redis.properties:

redis.hostName= 10.10.10.204

redis.port=7000

redis.timeout=3000

redis.maxIdle=10

redis.maxTotal=300

redis.minIdle=2

redis.maxWaitMillis=8000

redis.testOnBorrow=true

redis.password=

spring-root.xml:

<!-- 开启注解 -->

<cache:annotation-driven/>

<!-- 加载属性文件 -->

<context:property-placeholder location="classpath:/redis.properties" />

<!-- 扫描包 -->

<context:component-scan base-package="com.typhoon.spring_jdbctemplate"/>

<!-- jedis 配置 -->

<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">

<property name="maxIdle" value="${redis.minIdle}" />

<!--<property name="maxWaitMillis" value="${redis.maxWait}" />-->

<property name="testOnBorrow" value="${redis.testOnBorrow}" />

</bean>

<!-- redis服务器中心 -->

<bean id="connectionFactory"

class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">

<property name="poolConfig" ref="poolConfig" />

<property name="port" value="${redis.port}" />

<property name="hostName" value="${redis.hostName}" />

<!-- <property name="password" value="${redis.password}" /> -->

<property name="timeout" value="${redis.timeout}"></property>

</bean>

<!-- 配置redisTemplate -->

<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">

<property name="connectionFactory" ref="connectionFactory" />

<property name="keySerializer">

<bean

class="org.springframework.data.redis.serializer.StringRedisSerializer" />

</property>

<property name="valueSerializer">

<bean

class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />

</property>

</bean>

<!-- 配置缓存 -->

<bean id="cacheManager" class="org.springframework.data.redis.cache.RedisCacheManager">

<constructor-arg ref="redisTemplate" />

</bean>

<!-- 注入上下文工具类 -->

<bean class="com.typhoon.spring_jdbctemplate.util.SpringContextUtil" />

三、编写业务接口及实现

UserService接口定义:

package com.typhoon.spring_jdbctemplate.service;

import com.typhoon.spring_jdbctemplate.entity.User;

/**

* 用户信息接口

*

* @author Typhoon

* @date 2017-08-29 17:48 Tuesday

* @since V1.0

*/

public interface UserService {

User selectById(Long id);

}

接口默认实现:

package com.typhoon.spring_jdbctemplate.service.impl;

import org.springframework.cache.annotation.Cacheable;

import org.springframework.stereotype.Service;

import com.typhoon.spring_jdbctemplate.dao.UserDao;

import com.typhoon.spring_jdbctemplate.entity.User;

import com.typhoon.spring_jdbctemplate.service.UserService;

@Service("userService")

public class UserServiceImpl implements UserService {

static UserDao userDao = new UserDao();

@Override

@Cacheable(value="testId",key="'id_'+#id")

//@Cacheable(value="testId",keyGenerator = "customKeyGenerator")

public User selectById(Long id) {

System.out.println("绕过缓存,DB查询");

return userDao.selectById(id);

}

}

数据库操作dao层实现:

package com.typhoon.spring_jdbctemplate.dao;

import java.util.List;

import org.springframework.jdbc.core.BeanPropertyRowMapper;

import org.springframework.jdbc.core.JdbcTemplate;

import com.typhoon.spring_jdbctemplate.entity.User;

import com.typhoon.spring_jdbctemplate.util.DataSourceUtils;

/**

* 用户操作dao

*

* @author Typhoon

* @date 2017-08-22 15:32 Tuesday

* @since V1.3.1

*/

public class UserDao {

/**

* 获取jdbcTemplate

*/

private JdbcTemplate jdbcTemplate = DataSourceUtils.getJdbcTemplate();

public User selectById(Long id) {

List<User> list = jdbcTemplate.query(" select * from User where id = ? limit 1 ", BeanPropertyRowMapper.newInstance(User.class),id);

if(null == list || list.isEmpty()) {

return null;

}

return list.get(0);

}

}

自定义缓存key生成器:

package com.typhoon.spring_jdbctemplate.cachekey;

import java.lang.reflect.Method;

import org.springframework.cache.interceptor.KeyGenerator;

import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSON;

import com.typhoon.spring_jdbctemplate.util.MD5Util;

/**

* 自定义缓存key生成器

*

* @author Typhoon

* @date 2017-08-29 17:47 Tuesday

* @since V1.0

*/

@Component("customKeyGenerator")

public class CustomKeyGenerator implements KeyGenerator {

@Override

public Object generate(Object target, Method method, Object... params) {

String jsonstr = JSON.toJSONString(params);

return method.getName()+":"+params.hashCode();

}

}

自定义key生成规则尽可能满足不同的参数生成的key不同,一定要满足相同的查询条件生成的key相同,这样我们才可能最大限度的使用缓存

分析:

我们的缓存注解一般情况下都是写到service层,key的生成方式可以使用SpEL表达式,当然也可是使用我们自己定义的key生成器,特别需要注意的是,@Cacheable中key和keyGenerator属性不能同时存在.

四、编写单元测试

编写一个测试类模拟服务消费方:

package com.typhoon.spring_jdbctemplate.consumer;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.typhoon.spring_jdbctemplate.entity.User;

import com.typhoon.spring_jdbctemplate.service.UserService;

import com.typhoon.spring_jdbctemplate.util.SpringContextUtil;

public class RedisCacheConsumer {

@SuppressWarnings("resource")

public static void main(String[] args) {

new ClassPathXmlApplicationContext("spring-root.xml");

UserService userService = SpringContextUtil.getBean("userService", UserService.class);

User u = userService.selectById(1L);

System.out.println(u);

}

}

第一次运行后可以看到如下结果:

很明显,我们第一次调用查询,缓存中数据,直接到DB中读取并返回.接着我们再一次运行程序,可以看到以下结果:

没有打印DB查询中的日志信息,目测应该是走了缓存,为了验证我们的猜测,我们使用redis-cli命令连接redis查看缓存中有没有信息:

redis-cli -h 10.10.10.204 -p 7000

执行如下命令:

执行keys *testId*可以看到我们在@Cachable定义的value="testId"开头的eky已经存在redis中,使用type命令我们可以看到testId~keys的类型是zset也就是有序集合,也就是所有调用该方法产生的缓存对应的key都会放到改集合中,接着使用zrange命令查看集合中的key:

可以看到结果中已经有了我们使用SpEL表达式生成的key,然后我们获取该key对应的内容:

根据上述结果,我们得出我们查询出的User对象是以字符串的形式存储到redis中.

上边这种是使用SpEL表达式生成key的方式存储查询缓存,当然我们也可以把注解中的keyGenerator="customerkeyGenerator"然后删除key属性来生成key的方式存储缓存,此处不做赘述,可以自己尝试一下.

总结:

通过上述一系列的配置和编码,我们实现了spring+redis实现的基于注解的查询缓存,接下来分析一下其优缺点:

  • 优点:1)使用简单方便,2)缓存key管理统一
  • 缺点:1)对于一些特定的场景不够灵活 2)存在缓存穿透、雪崩的问题

ps:原创不易,多多支持!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2017-08-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 PersistentCoder 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档