From 1cb7cdb7b7a8fd368a5d3a45a968fdfc916f0e66 Mon Sep 17 00:00:00 2001 From: Xiong Neng Date: Fri, 14 Sep 2018 11:01:12 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90springboot-cache?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- springboot-cache/pom.xml | 7 +- .../java/com/xncoding/trans/Application.java | 2 + .../trans/config/RedisCacheConfig.java | 78 +++++----------- .../xncoding/trans/service/UserService.java | 89 ++++++++----------- .../src/main/resources/application.yml | 16 +++- .../com/xncoding/service/UserServiceTest.java | 40 ++++++--- 6 files changed, 108 insertions(+), 124 deletions(-) diff --git a/springboot-cache/pom.xml b/springboot-cache/pom.xml index 71acf41..1883b89 100644 --- a/springboot-cache/pom.xml +++ b/springboot-cache/pom.xml @@ -16,6 +16,7 @@ org.springframework.boot spring-boot-starter-parent 2.0.4.RELEASE + @@ -34,10 +35,10 @@ spring-boot-starter-data-redis - redis.clients - jedis - 2.9.0 + org.apache.commons + commons-pool2 + com.fasterxml.jackson.core jackson-databind diff --git a/springboot-cache/src/main/java/com/xncoding/trans/Application.java b/springboot-cache/src/main/java/com/xncoding/trans/Application.java index 83e2203..9e545ea 100644 --- a/springboot-cache/src/main/java/com/xncoding/trans/Application.java +++ b/springboot-cache/src/main/java/com/xncoding/trans/Application.java @@ -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); diff --git a/springboot-cache/src/main/java/com/xncoding/trans/config/RedisCacheConfig.java b/springboot-cache/src/main/java/com/xncoding/trans/config/RedisCacheConfig.java index ceeff0a..d66ff44 100644 --- a/springboot-cache/src/main/java/com/xncoding/trans/config/RedisCacheConfig.java +++ b/springboot-cache/src/main/java/com/xncoding/trans/config/RedisCacheConfig.java @@ -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 redisTemplate(RedisConnectionFactory cf) { - RedisTemplate redisTemplate = new RedisTemplate(); - 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的生成类实现 */ diff --git a/springboot-cache/src/main/java/com/xncoding/trans/service/UserService.java b/springboot-cache/src/main/java/com/xncoding/trans/service/UserService.java index cedcd24..e52d95a 100644 --- a/springboot-cache/src/main/java/com/xncoding/trans/service/UserService.java +++ b/springboot-cache/src/main/java/com/xncoding/trans/service/UserService.java @@ -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 getAllUsers() { + logger.info("获取所有用户列表"); + return userMapper.selectList(null); } /** - * 以上我们使用默认的keyGenerator,对应spring的SimpleKeyGenerator - * 如果你的使用很复杂,我们也可以自定义myKeyGenerator的生成key - *

- * 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"); - } - } diff --git a/springboot-cache/src/main/resources/application.yml b/springboot-cache/src/main/resources/application.yml index 64f1182..6fd4aa3 100644 --- a/springboot-cache/src/main/resources/application.yml +++ b/springboot-cache/src/main/resources/application.yml @@ -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: diff --git a/springboot-cache/src/test/java/com/xncoding/service/UserServiceTest.java b/springboot-cache/src/test/java/com/xncoding/service/UserServiceTest.java index 6d167d7..113c36e 100644 --- a/springboot-cache/src/test/java/com/xncoding/service/UserServiceTest.java +++ b/springboot-cache/src/test/java/com/xncoding/service/UserServiceTest.java @@ -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 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)); }