完成springboot-cache

This commit is contained in:
Xiong Neng 2018-09-14 11:01:12 +08:00
parent 1f6885b8bf
commit 1cb7cdb7b7
6 changed files with 108 additions and 124 deletions

View File

@ -16,6 +16,7 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/>
</parent>
<properties>
@ -34,10 +35,10 @@
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>

View File

@ -2,8 +2,10 @@ package com.xncoding.trans;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
@SpringBootApplication
@EnableCaching
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);

View File

@ -11,11 +11,14 @@ import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
@ -37,73 +40,38 @@ import java.util.Map;
*/
@Configuration
@EnableCaching
public class RedisCacheConfig extends CachingConfigurerSupport {
public class RedisCacheConfig {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private String port;
@Autowired
private Environment env;
@Bean
public RedisStandaloneConfiguration getRedisClient() {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(host, Integer.parseInt(port));
return redisStandaloneConfiguration;
public LettuceConnectionFactory redisConnectionFactory() {
RedisStandaloneConfiguration redisConf = new RedisStandaloneConfiguration();
redisConf.setHostName(env.getProperty("spring.redis.host"));
redisConf.setPort(Integer.parseInt(env.getProperty("spring.redis.port")));
redisConf.setPassword(RedisPassword.of(env.getProperty("spring.redis.password")));
return new LettuceConnectionFactory(redisConf);
}
@Bean
public JedisConnectionFactory redisConnectionFactory(RedisStandaloneConfiguration RedisStandaloneConfiguration) {
JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(RedisStandaloneConfiguration);
return jedisConnectionFactory;
public RedisCacheConfiguration cacheConfiguration() {
RedisCacheConfiguration cacheConfig = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(600))
.disableCachingNullValues();
return cacheConfig;
}
@Bean
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory cf) {
RedisTemplate<String, String> redisTemplate = new RedisTemplate<String, String>();
redisTemplate.setConnectionFactory(cf);
return redisTemplate;
public RedisCacheManager cacheManager() {
RedisCacheManager rcm = RedisCacheManager.builder(redisConnectionFactory())
.cacheDefaults(cacheConfiguration())
.transactionAware()
.build();
return rcm;
}
@Bean
public RedisCacheConfiguration redisCacheConfiguration() {
return RedisCacheConfiguration
.defaultCacheConfig()
.serializeKeysWith(
RedisSerializationContext
.SerializationPair
.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(
RedisSerializationContext
.SerializationPair
.fromSerializer(new GenericJackson2JsonRedisSerializer()))
.entryTtl(Duration.ofSeconds(600L));
}
@Bean
public CacheManager cacheManager(RedisConnectionFactory cf) {
//RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(cf);
//RedisCacheManager cacheManager = new RedisCacheManager(redisCacheWriter, RedisCacheConfiguration.defaultCacheConfig());
RedisCacheManager cm = RedisCacheManager.builder(cf).cacheDefaults(redisCacheConfiguration()).build();
return cm;
}
// @Bean
// public KeyGenerator keyGenerator() {
// return new KeyGenerator() {
// @Override
// public Object generate(Object o, Method method, Object... objects) {
// StringBuilder sb = new StringBuilder();
// sb.append(o.getClass().getName());
// sb.append(method.getName());
// for (Object obj : objects) {
// sb.append(obj.toString());
// }
// return sb.toString();
// }
// };
// }
/**
* 自定义缓存key的生成类实现
*/

View File

@ -1,19 +1,19 @@
package com.xncoding.trans.service;
import com.baomidou.mybatisplus.mapper.Condition;
import com.xncoding.trans.dao.entity.User;
import com.xncoding.trans.dao.repository.UserMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.List;
@Service
@CacheConfig(cacheNames = "users")
@Transactional
public class UserService {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Resource
@ -21,83 +21,64 @@ public class UserService {
/**
* cacheNames 设置缓存的值
* key指定缓存的key这是指参数id值 key可以使用spEl表达式
* key指定缓存的key这是指参数id值key可以使用spEl表达式
*
* @param id
* @return
*/
@Cacheable(key = "#id")
@Cacheable(value = "userCache", key = "#id", unless="#result == null")
public User getById(int id) {
logger.info("获取用户start...");
return userMapper.selectById(id);
}
/***
* 如果设置sync=true
* 如果缓存中没有数据多个线程同时访问这个方法则只有一个方法会执行到方法其它方法需要等待
* 如果缓存中已经有数据则多个线程可以同时从缓存中获取数据
* @param id
* @return
*/
@Cacheable(key = "#id", sync = true)
public User getById2(int id) {
logger.info("获取用户start...");
return userMapper.selectById(id);
@Cacheable(value = "allUsersCache", unless = "#result.size() == 0")
public List<User> getAllUsers() {
logger.info("获取所有用户列表");
return userMapper.selectList(null);
}
/**
* 以上我们使用默认的keyGenerator对应spring的SimpleKeyGenerator
* 如果你的使用很复杂我们也可以自定义myKeyGenerator的生成key
* <p>
* key和keyGenerator是互斥如果同时制定会出异常
* The key and keyGenerator parameters are mutually exclusive and an operation specifying both will result in an exception.
*
* @param id
* @return
* 创建用户同时使用新的返回值的替换缓存中的值
* 创建用户后会将allUsersCache缓存全部清空
*/
@Cacheable(keyGenerator = "myKeyGenerator")
public User queryUserById(int id) {
logger.info("queryUserById,id={}", id);
return userMapper.selectById(id);
}
/**
* 每次执行都会执行方法同时使用新的返回值的替换缓存中的值
*
* @param user
*/
@CachePut(key = "#user.id")
public void createUser(User user) {
@Caching(
put = {@CachePut(value = "userCache", key = "#user.id")},
evict = {@CacheEvict(value = "allUsersCache", allEntries = true)}
)
public User createUser(User user) {
logger.info("创建用户start..., user.id=" + user.getId());
userMapper.insert(user);
return user;
}
/**
* 每次执行都会执行方法同时使用新的返回值的替换缓存中的值
*
* @param user
* 更新用户同时使用新的返回值的替换缓存中的值
* 更新用户后会将allUsersCache缓存全部清空
*/
@CachePut(key = "#user.id")
public void updateUser(User user) {
@Caching(
put = {@CachePut(value = "userCache", key = "#user.id")},
evict = {@CacheEvict(value = "allUsersCache", allEntries = true)}
)
public User updateUser(User user) {
logger.info("更新用户start...");
userMapper.updateById(user);
return user;
}
/**
* 对符合key条件的记录从缓存中book1移除
* 对符合key条件的记录从缓存中移除
* 删除用户后会将allUsersCache缓存全部清空
*/
@CacheEvict(key = "#id")
@Caching(
evict = {
@CacheEvict(value = "userCache", key = "#id"),
@CacheEvict(value = "allUsersCache", allEntries = true)
}
)
public void deleteById(int id) {
logger.info("删除用户start...");
userMapper.deleteById(id);
}
/**
* allEntries = true: 清空user1里的所有缓存
*/
@CacheEvict(allEntries=true)
public void clearUser1All(){
logger.info("clearAll");
}
}

View File

@ -10,9 +10,9 @@ spring:
profiles:
active: dev
datasource:
url: jdbc:mysql://127.0.0.1:3306/test?useSSL=false&autoReconnect=true&tinyInt1isBit=false&useUnicode=true&characterEncoding=utf8
url: jdbc:mysql://123.207.66.156:3306/test?useSSL=false&autoReconnect=true&tinyInt1isBit=false&useUnicode=true&characterEncoding=utf8
username: root
password: 123456
password: _EnZhi123
################### mybatis-plus配置 ###################
mybatis-plus:
@ -42,10 +42,22 @@ spring:
profiles: dev
cache:
type: REDIS
redis:
cache-null-values: false
time-to-live: 600000ms
use-key-prefix: true
cache-names: userCache,allUsersCache
redis:
host: 123.207.66.156
port: 6379
database: 0
lettuce:
shutdown-timeout: 200ms
pool:
max-active: 7
max-idle: 7
min-idle: 2
max-wait: -1ms
logging:
level:

View File

@ -7,8 +7,11 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Random;
import static org.junit.Assert.assertEquals;
@ -24,24 +27,41 @@ import static org.junit.Assert.assertNull;
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
@Transactional
public class UserServiceTest {
@Autowired
private UserService userService;
@Test
public void testCache() {
int id = new Random().nextInt(100);
// 创建一个用户admin
int id = new Random().nextInt(1000);
User user = new User(id, "admin", "admin");
userService.createUser(user);
User user1 = userService.getById(id); // 第1次访问
assertEquals(user1.getPassword(), "admin");
User user2 = userService.getById(id); // 第2次访问
assertEquals(user2.getPassword(), "admin");
User user3 = userService.queryUserById(id); // 第3次访问使用自定义的KeyGenerator
// 再创建一个用户xiong
int id2 = new Random().nextInt(1000);
User user2 = new User(id2, "xiong", "neng");
userService.createUser(user2);
// 查询所有用户列表
List<User> list = userService.getAllUsers();
assertEquals(list.size(), 2);
// 两次访问看看缓存命中情况
User user3 = userService.getById(id); // 第1次访问
assertEquals(user3.getPassword(), "admin");
user.setPassword("123456");
userService.updateUser(user);
User user4 = userService.getById(id); // 第4次访问
assertEquals(user4.getPassword(), "123456");
User user4 = userService.getById(id); // 第2次访问
assertEquals(user4.getPassword(), "admin");
// 更新用户密码
user4.setPassword("123456");
userService.updateUser(user4);
// 更新完成后再次访问用户
User user5 = userService.getById(id); // 第4次访问
assertEquals(user5.getPassword(), "123456");
// 删除用户admin
userService.deleteById(id);
assertNull(userService.getById(id));
}