Redis 是目前業界使用最普遍的內存數據存儲。相比 Memcached,Redis 支持更豐富的數據結構,例如 hashes, lists, sets 等,同時支持數據持久化。除此以外,Redis 還提供一些類數據庫的特性,好比事務,HA,主從庫。能夠說 Redis 兼具了緩存系統和數據庫的一些特性,所以有着豐富的應用場景。本文介紹 Redis 在 Spring Boot 中兩個典型的應用場景。html
若是在 Java 應用中使用過 Redis 緩存,那麼對 Jedis
必定不陌生, Lettuce
和 Jedis
同樣,都是鏈接 Redis Server
的客戶端程序。Jedis
在實現上是直連 Redis Server
,多線程環境下非線程安全,除非使用鏈接池,爲每一個 Jedis
實例增長物理鏈接。 Lettuce
基於 Netty
的鏈接實例(StatefulRedisConnection),能夠在多個線程間併發訪問,且線程安全,知足多線程環境下的併發訪問,同時它是可伸縮的設計,一個鏈接實例不夠的狀況也能夠按需增長鏈接實例。java
RedisTemplate
來使用Spring Cache
集成 Redis
Spring Session
作 Session
共享代碼清單:spring-boot-redis/pom.xmlgit
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
spring-boot-starter-data-redis :在 Spring Boot 2.x
後底層再也不是使用 Jedis
,而是換成了 Lettuce
,如圖:github
commons-pool2 : 用做 redis
鏈接池,如不引入啓動會報錯。web
spring-session-data-redis : Spring Session
引入,用做共享 Session
。redis
代碼清單:spring-boot-redis/src/main/resources/application.ymlspring
server: port: 8080 servlet: session: timeout: 30m spring: application: name: spring-boot-redis cache: # 使用了Spring Cache後,能指定spring.cache.type就手動指定一下,雖然它會自動去適配已有Cache的依賴,但前後順序會對Redis使用有影響(JCache -> EhCache -> Redis -> Guava) type: REDIS redis: host: 192.168.0.128 port: 6379 password: 123456 # 鏈接超時時間(ms) timeout: 10000 # Redis默認狀況下有16個分片,這裏配置具體使用的分片,默認是0 database: 0 lettuce: pool: # 鏈接池最大鏈接數(使用負值表示沒有限制) 默認 8 max-active: 100 # 鏈接池最大阻塞等待時間(使用負值表示沒有限制) 默認 -1 max-wait: -1 # 鏈接池中的最大空閒鏈接 默認 8 max-idle: 8 # 鏈接池中的最小空閒鏈接 默認 0 min-idle: 0
這裏的配置很少解釋,須要解釋的已經標註註釋。sql
代碼清單:spring-boot-redis/src/main/java/com/springboot/springbootredis/model/User.java數據庫
@Data @AllArgsConstructor @NoArgsConstructor public class User implements Serializable { private static final long serialVersionUID = 662692455422902539L; private Long id; private String name; private int age; }
默認狀況下的模板只能支持 RedisTemplate<String, String>
,也就是隻能存入字符串,這在開發中是不友好的,因此自定義模板是頗有必要的,當自定義了模板又想使用 String
存儲這時候就可使用 StringRedisTemplate
的方式,它們並不衝突,添加配置類 RedisCacheConfig.java
,代碼以下:apache
代碼清單:spring-boot-redis/src/main/java/com/springboot/springbootredis/config/RedisCacheConfig.java
@Configuration @AutoConfigureAfter(RedisAutoConfiguration.class) public class RedisCacheConfig { @Bean public RedisTemplate<String, Serializable> redisCacheTemplate(LettuceConnectionFactory redisConnectionFactory) { RedisTemplate<String, Serializable> template = new RedisTemplate<>(); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); template.setConnectionFactory(redisConnectionFactory); return template; } }
代碼清單:
@RestController @Slf4j public class UserController { @Autowired private StringRedisTemplate stringRedisTemplate; @Autowired RedisTemplate<String, Serializable> redisCacheTemplate; @Autowired UserService userService; @GetMapping("/test") public void test() { stringRedisTemplate.opsForValue().set("geekdigging", "https://www.geekdigging.com/"); log.info("當前獲取對象:{}",stringRedisTemplate.opsForValue().get("geekdigging")); redisCacheTemplate.opsForValue().set("geekdigging.com", new User(1L, "geekdigging", 18)); User user = (User) redisCacheTemplate.opsForValue().get("geekdigging.com"); log.info("當前獲取對象:{}", user); } }
啓動服務,打開瀏覽器訪問連接:http://localhost:8080/test ,查看控制檯日誌打印,以下:
2019-09-24 23:49:30.191 INFO 19108 --- [nio-8080-exec-1] c.s.s.controller.UserController : 當前獲取對象:https://www.geekdigging.com/ 2019-09-24 23:49:30.243 INFO 19108 --- [nio-8080-exec-1] c.s.s.controller.UserController : 當前獲取對象:User(id=1, name=geekdigging, age=18)
測試成功。
Spring 3.1 引入了激動人心的基於註釋(annotation)的緩存(cache)技術,它本質上不是一個具體的緩存實現方案(例如 EHCache 或者 Redis),而是一個對緩存使用的抽象,經過在既有代碼中添加少許它定義的各類 annotation,即可以達到緩存方法的返回對象的效果。
Spring Cache 具有至關的好的靈活性,不只可以使用 SpEL(Spring Expression Language)來定義緩存的 key 和各類 condition,還提供開箱即用的緩存臨時存儲方案,也支持和主流的專業緩存例如 EHCache、Redis、Guava 的集成。
代碼清單:spring-boot-redis/src/main/java/com/springboot/springbootredis/service/UserService.java
public interface UserService { User save(User user); User get(Long id); void delete(Long id); }
代碼清單:spring-boot-redis/src/main/java/com/springboot/springbootredis/service/impl/UserServiceImpl.java
@Service @Slf4j public class UserServiceImpl implements UserService { private static final Map<Long, User> USER_MAP = new HashMap<>(); static { USER_MAP.put(1L, new User(1L, "geekdigging.com", 18)); USER_MAP.put(2L, new User(2L, "geekdigging.com", 19)); USER_MAP.put(3L, new User(3L, "geekdigging.com", 20)); } @CachePut(value = "user", key = "#user.id") @Override public User save(User user) { USER_MAP.put(user.getId(), user); log.info("進入 save 方法,當前存儲對象:{}", user); return user; } @Cacheable(value = "user", key = "#id") @Override public User get(Long id) { log.info("進入 get 方法,當前獲取對象:{}", USER_MAP.get(id)); return USER_MAP.get(id); } @CacheEvict(value = "user", key = "#id") @Override public void delete(Long id) { USER_MAP.remove(id); log.info("進入 delete 方法,刪除成功"); } }
爲了方便演示數據庫操做,直接定義了一個 Map<Long, User> USER_MAP
,這裏的核心就是三個註解 @Cacheable
、 @CachePut
、 @CacheEvict
。
根據方法的請求參數對其結果進行緩存
@Cacheable(value="user",key="#userName")
)@Cacheable(value="user")
或者 @Cacheable(value={"user1","use2"})
)@Cacheable(value = "user", key = "#id",condition = "#id < 10")
)根據方法的請求參數對其結果進行緩存,和 @Cacheable 不一樣的是,它每次都會觸發真實方法的調用
根據條件對緩存進行清空
@CacheEvict(value = "user", key = "#id", allEntries = true)
)@CacheEvict(value = "user", key = "#id", beforeInvocation = true)
)代碼清單:spring-boot-redis/src/main/java/com/springboot/springbootredis/SpringBootRedisApplication.java
@SpringBootApplication @EnableCaching public class SpringBootRedisApplication { public static void main(String[] args) { SpringApplication.run(SpringBootRedisApplication.class, args); } }
@EnableCaching
開啓 Spring Session。代碼清單:spring-boot-redis/src/main/java/com/springboot/springbootredis/controller/UserController.java
@GetMapping("/test1") public void test1() { User user = userService.save(new User(4L, "geekdigging.com", 35)); log.info("當前 save 對象:{}", user); user = userService.get(1L); log.info("當前 get 對象:{}", user); userService.delete(5L); }
啓動服務,打開瀏覽器訪問連接:http://localhost:8080/test ,刷新頁面,控制檯日誌打印以下:
2019-09-25 00:07:21.887 INFO 21484 --- [nio-8080-exec-1] c.s.s.service.impl.UserServiceImpl : 進入 save 方法,當前存儲對象:User(id=4, name=geekdigging.com, age=35) 2019-09-25 00:07:21.897 INFO 21484 --- [nio-8080-exec-1] c.s.s.controller.UserController : 當前 save 對象:User(id=4, name=geekdigging.com, age=35) 2019-09-25 00:07:21.899 INFO 21484 --- [nio-8080-exec-1] c.s.s.service.impl.UserServiceImpl : 進入 get 方法,當前獲取對象:User(id=1, name=geekdigging.com, age=18) 2019-09-25 00:07:21.900 INFO 21484 --- [nio-8080-exec-1] c.s.s.controller.UserController : 當前 get 對象:User(id=1, name=geekdigging.com, age=18) 2019-09-25 00:07:21.901 INFO 21484 --- [nio-8080-exec-1] c.s.s.service.impl.UserServiceImpl : 進入 delete 方法,刪除成功
再次刷新頁面,查看控制檯日誌:
2019-09-25 00:08:54.076 INFO 21484 --- [nio-8080-exec-7] c.s.s.service.impl.UserServiceImpl : 進入 save 方法,當前存儲對象:User(id=4, name=geekdigging.com, age=35) 2019-09-25 00:08:54.077 INFO 21484 --- [nio-8080-exec-7] c.s.s.controller.UserController : 當前 save 對象:User(id=4, name=geekdigging.com, age=35) 2019-09-25 00:08:54.079 INFO 21484 --- [nio-8080-exec-7] c.s.s.controller.UserController : 當前 get 對象:User(id=1, name=geekdigging.com, age=18) 2019-09-25 00:08:54.079 INFO 21484 --- [nio-8080-exec-7] c.s.s.service.impl.UserServiceImpl : 進入 delete 方法,刪除成功
結果和咱們指望的一致,能夠看到增刪改查中,查詢是沒有日誌輸出的,由於它直接從緩存中獲取的數據,而添加、修改、刪除都是會進入 UserServiceImpl
的方法內執行具體的業務代碼。
Spring Session 提供了一套建立和管理 Servlet HttpSession 的方案。Spring Session 提供了集羣 Session(Clustered Sessions)功能,默認採用外置的 Redis 來存儲 Session 數據,以此來解決 Session 共享的問題。
代碼清單:spring-boot-redis/src/main/java/com/springboot/springbootredis/SpringBootRedisApplication.java
@SpringBootApplication @EnableCaching @EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1800) public class SpringBootRedisApplication { public static void main(String[] args) { SpringApplication.run(SpringBootRedisApplication.class, args); } }
application.yml
中的 server.session.timeout
屬性再也不生效。代碼清單:spring-boot-redis/src/main/java/com/springboot/springbootredis/controller/UserController.java
@GetMapping("/getBlogUrl") public String getSessionId(HttpServletRequest request) { String url = (String) request.getSession().getAttribute("url"); if (StringUtils.isEmpty(url)) { request.getSession().setAttribute("url", "https://www.geekdigging.com/"); } log.info("獲取session內容爲: {}", request.getSession().getAttribute("url")); return request.getRequestedSessionId(); }
啓動服務,打開瀏覽器訪問連接:http://localhost:8080/getBlogUrl ,查看 Redis 當前存儲內容,以下圖:
其中 1569339180000 爲失效時間,意思是這個時間後 Session 失效, b2522824-1094-478e-a435-554a551bc8bb 爲 SessionId 。
按照上面的步驟在另外一個項目中再次配置一次,啓動後自動就進行了 Session 共享。
http://emacoo.cn/backend/spring-redis/ https://blog.battcn.com/2018/05/11/springboot/v2-nosql-redis/
原文出處:https://www.cnblogs.com/babycomeon/p/11595609.html