使用缓存的方式很多,有基于工具类手动操作的,也有基于注解的,各有千秋,接下来将借助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实现的基于注解的查询缓存,接下来分析一下其优缺点:
ps:原创不易,多多支持!
本文分享自 PersistentCoder 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!