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

View File

@ -2,8 +2,10 @@ package com.xncoding.trans;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
@SpringBootApplication @SpringBootApplication
@EnableCaching
public class Application { public class Application {
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(Application.class, 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.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; 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.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory; 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.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; 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.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
@ -37,73 +40,38 @@ import java.util.Map;
*/ */
@Configuration @Configuration
@EnableCaching @EnableCaching
public class RedisCacheConfig extends CachingConfigurerSupport { public class RedisCacheConfig {
private Logger logger = LoggerFactory.getLogger(this.getClass()); private Logger logger = LoggerFactory.getLogger(this.getClass());
@Value("${spring.redis.host}") @Autowired
private String host; private Environment env;
@Value("${spring.redis.port}")
private String port;
@Bean @Bean
public RedisStandaloneConfiguration getRedisClient() { public LettuceConnectionFactory redisConnectionFactory() {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(host, Integer.parseInt(port)); RedisStandaloneConfiguration redisConf = new RedisStandaloneConfiguration();
return 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 @Bean
public JedisConnectionFactory redisConnectionFactory(RedisStandaloneConfiguration RedisStandaloneConfiguration) { public RedisCacheConfiguration cacheConfiguration() {
JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(RedisStandaloneConfiguration); RedisCacheConfiguration cacheConfig = RedisCacheConfiguration.defaultCacheConfig()
return jedisConnectionFactory; .entryTtl(Duration.ofSeconds(600))
.disableCachingNullValues();
return cacheConfig;
} }
@Bean @Bean
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory cf) { public RedisCacheManager cacheManager() {
RedisTemplate<String, String> redisTemplate = new RedisTemplate<String, String>(); RedisCacheManager rcm = RedisCacheManager.builder(redisConnectionFactory())
redisTemplate.setConnectionFactory(cf); .cacheDefaults(cacheConfiguration())
return redisTemplate; .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的生成类实现 * 自定义缓存key的生成类实现
*/ */

View File

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

View File

@ -10,9 +10,9 @@ spring:
profiles: profiles:
active: dev active: dev
datasource: 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 username: root
password: 123456 password: _EnZhi123
################### mybatis-plus配置 ################### ################### mybatis-plus配置 ###################
mybatis-plus: mybatis-plus:
@ -42,10 +42,22 @@ spring:
profiles: dev profiles: dev
cache: cache:
type: REDIS type: REDIS
redis:
cache-null-values: false
time-to-live: 600000ms
use-key-prefix: true
cache-names: userCache,allUsersCache
redis: redis:
host: 123.207.66.156 host: 123.207.66.156
port: 6379 port: 6379
database: 0 database: 0
lettuce:
shutdown-timeout: 200ms
pool:
max-active: 7
max-idle: 7
min-idle: 2
max-wait: -1ms
logging: logging:
level: level:

View File

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