简介
SpringMVC 中也可以将缓存标签和 redis 结合起来使用,其实此时缓存没有起作用,只是通过缓存的那几个注解来操作 redis 而已;SpringMVC 中整合 redis 比较麻烦的是注意版本冲突的问题,如下是官网有关于版本的要求
https://docs.spring.io/spring-data/data-redis/docs/current/reference/html/#requirements
实现步骤
- 在 pom.xml 中添加如下依赖,尤其要注意各个依赖的版本,基本上都是 maven 官网上的最新版
<properties>
<!-- java 版本 -->
<java.version>1.8</java.version>
<!-- spring 版本 -->
<spring.version>5.1.2.RELEASE</spring.version>
<!-- jstl 版本 -->
<jstl.version>1.2</jstl.version>
<!-- standard 版本 -->
<standard.version>1.2.5</standard.version>
</properties>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlets</artifactId>
<version>9.1.0.M0</version>
</dependency>
<!-- begin SpringMVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-instrument</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc-portlet</artifactId>
<version>4.3.20.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-instrument-tomcat</artifactId>
<version>4.3.20.RELEASE</version>
</dependency>
<!-- end SpringMVC -->
<!-- begin jstl -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>${jstl.version}</version>
</dependency>
<dependency>
<groupId>org.apache.taglibs</groupId>
<artifactId>taglibs-standard-impl</artifactId>
<version>${standard.version}</version>
</dependency>
<!-- end jstl -->
<!-- config redis data and client jar-->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>2.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
<!-- 用于处理将对象转化为json格式的字符串 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.7</version>
</dependency>
</dependencies>
- 新建 CacheConfig.class,实现 Cache 接口,添加如下属性和方法
@Component
public class CacheConfig implements Cache {
private StringRedisTemplate redisTemplate;
private String name;
public CacheConfig() {}
public CacheConfig(StringRedisTemplate redisTemplate, String name) {
this.redisTemplate = redisTemplate;
this.name = name;
}
public StringRedisTemplate getStringRedisTemplate() {
return redisTemplate;
}
public void setStringRedisTemplate(StringRedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void clear() {
redisTemplate.execute(new RedisCallback < String > () {
public String doInRedis(RedisConnection connection) throws DataAccessException {
connection.flushDb();
return "ok";
}
});
}
public void evict(Object object) {
final String key = (String) object;
redisTemplate.execute(new RedisCallback < Long > () {
public Long doInRedis(RedisConnection connection) throws DataAccessException {
return connection.del(key.getBytes());
}
});
}
public ValueWrapper get(Object obj) {
final String key = (String) obj;
Object object = null;
object = redisTemplate.execute(new RedisCallback < Object > () {
public Object doInRedis(RedisConnection connection) throws DataAccessException {
byte[] keys = key.getBytes();
byte[] value = connection.get(keys);
if (value == null) {
return null;
}
return toObject(value);
}
});
return (object != null ? new SimpleValueWrapper(object) : null);
}
public Object getNativeCache() {
return this.redisTemplate;
}
public void put(Object key, Object value) {
final String keyf = (String) key;
final Object valuef = value;
final long liveTime = 86400;
redisTemplate.execute(new RedisCallback < Long > () {
public Long doInRedis(RedisConnection connection) throws DataAccessException {
byte[] keyb = keyf.getBytes();
byte[] valueb = toByteArray(valuef);
connection.set(keyb, valueb);
if (liveTime > 0) {
connection.expire(keyb, liveTime);
}
return 1 L;
}
});
}
public ValueWrapper putIfAbsent(Object key, Object value) {
return null;
}
public < T > T get(Object object, Class < T > clz) {
return null;
}
public < T > T get(Object object, Callable < T > callable) {
return null;
}
private Object toObject(byte[] bytes) {
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
Object obj = null;
try {
bis = new ByteArrayInputStream(bytes);
ois = new ObjectInputStream(bis);
obj = ois.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
closeIO(ois, bis);
}
return obj;
}
private byte[] toByteArray(Object obj) {
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
byte[] bytes = null;
try {
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(obj);
oos.flush();
bytes = bos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
} finally {
closeIO(oos, bos);
}
return bytes;
}
public void closeIO(Closeable...ios) {
try {
for (Closeable io: ios) {
if (null != io) {
io.close();
}@Configuration
@EnableWebMvc
@EnableCaching
@ComponentScan(basePackages = "com.ibm.redis")
public class SpringConfig extends WebMvcConfigurationSupport {@
Resource
private AppSetting appSetting;
@Bean
public CacheManager cacheManager(StringRedisTemplate redisTemplate) {
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager
// 注意此处设置的 cache name 后面将会用到
.setCaches(Arrays.asList(new ConcurrentMapCache("myCache"), new CacheConfig(redisTemplate, "redis")));
return cacheManager;
}
@Bean
public KeyGenerator keyGenerator() {
return new MyKeyGenerator();
}
// Jedis 连接池配置(对于缓存+redis配置来说, 该 bean 不是必需的, 只有单独使用 jedis 才用到)
@Bean
public GenericObjectPoolConfig poolConfig() {
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
poolConfig.setMaxWaitMillis(appSetting.getMaxWaitMillis());
poolConfig.setMaxTotal(appSetting.getMaxActive());
poolConfig.setMaxIdle(appSetting.getMaxIdle());
poolConfig.setMinIdle(appSetting.getMinIdle());
return poolConfig;
}
// 创建 Jedis 连接池(对于缓存+redis配置来说, 该 bean 不是必需的, 只有单独使用 jedis 才用到)
@Bean
public JedisPool jedisPool(GenericObjectPoolConfig poolConfig) {
JedisPool jedisPool = new JedisPool(poolConfig, appSetting.getHost(), appSetting.getPort(), 60, appSetting.getPass());
return jedisPool;
}
// 创建 JedisConnectionFactory
@Bean
public JedisConnectionFactory redisConnectionFactory() {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setHostName(appSetting.getHost());
redisStandaloneConfiguration.setPassword(RedisPassword.of(appSetting.getPass()));
redisStandaloneConfiguration.setPort(appSetting.getPort());
return new JedisConnectionFactory(redisStandaloneConfiguration);
}
// 创建 StringRedisTemplate
@Bean
public StringRedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
StringRedisTemplate redisTemplate = new StringRedisTemplate(redisConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
return redisTemplate;
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 新建 SpringConfig.class 为 spring 的配置类,添加如下配置
在这里插入代码片
注意:上面的主键生成器 Bean 的实现如下
// 缓存键的生成策略
public class MyKeyGenerator implements KeyGenerator {
public Object generate(Object target, Method method, Object ... params) {
return target.getClass().getName() + method.getName();
}
}
- Appsetting.class 类主要用于从 redis.properties 中绑定数据
Appsetting.class
@PropertySource(value="classpath:redis.properties")
@Component
public class AppSetting {
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.password}")
private String pass;
@Value("${spring.redis.port}")
private int port;
@Value("${spring.redis.timeout}")
private int timeout;
@Value("${spring.redis.pool.max-active}")
private int maxActive;
@Value("${spring.redis.pool.max-idle}")
private int maxIdle;
@Value("${spring.redis.pool.min-idle}")
private int minIdle;
@Value("${spring.redis.pool.max-wait}")
private long maxWaitMillis;
... ... 省略 getter 和 setter 方法
}
redis.properties
# REDIS
#redis jedis配置
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址 (默认为127.0.0.1)
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=dufu9137
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=200
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=0
#spring-session 使用
spring.session.store-type=none
- 在 RedisService.class 中使用
@Service
// 此处的 cache name 和第 3 步中声明 CacheManager 时添加的 name 要对应上
@CacheConfig(cacheNames = "redis", keyGenerator = "keyGenerator")
public class RedisService {
@Resource
private CacheManager cacheManager;
@Cacheable
public String get1() {
System.out.println(new Date() + "--> No value from cache");
return "Ramos";
}
@Cacheable
public User get2() {
System.out.println(new Date() + "--> No value from cache");
return new User(1, "dufu");
}
}
运行效果
- 操作字符串
查看 redis 客户端,发现已经有数据存入到库中
2. 操作对象
查看 redis 客户端,发现已经有数据存入到库中
此时,如果我们反复刷新页面,RedisService.class 中的 get1() 和 get2() 方法也只会打印出一次:No value from cache,说明缓存(redis)已经生效