高手如何處理快取:SpringBoot整合Redis實現快取處理(AOP技術)!
作者:smileNicky
第一章 需求分析
計劃在Team的開源專案里加入Redis實現快取處理,因為業務功能已經實現了一部分,通過寫Redis工具類,然後引用,改動量較大,而且不可以實現解耦合,所以想到了Spring框架的AOP(面向切面程式設計)。
開源專案:https://github.com/u014427391/jeeplatform
推薦教程:https://edu.csdn.net/courses/o280_s348_k?utm_source=blog11
歡迎star(收藏)
第二章 SpringBoot簡介
Spring框架作為JavaEE框架領域的一款重要的開源框架,在企業應用開發中有著很重要的作用,同時Spring框架及其子框架很多,所以知識量很廣。
SpringBoot:一款Spring框架的子框架,也可以叫微框架,是2014年推出的一款使Spring框架開發變得容易的框架。學過Spring框架的都知識,Spring框架難以避免地需要配置不少XMl,而使用SpringBoot框架的話,就可以使用註解開發,極大地簡化基於Spring框架的開發。SpringBoot充分利用了JavaConfig的配置模式以及“約定優於配置”的理念,能夠極大的簡化基於SpringMVC的Web應用和REST服務開發。
第三章 Redis簡介
3.1 Redis安裝部署(Linux)
Redis安裝部署的可以參考我的部落格(Redis是基於C編寫的,所以安裝前先安裝gcc編譯器):http://blog.csdn.net/u014427391/article/details/71210989
3.2 Redis簡介
Redis如今已經成為Web開發社群最火熱的記憶體資料庫之一,隨著Web2.0的快速發展,再加上半結構資料比重加大,網站對高效效能的需求也越來越多。
而且大型網站一般都有幾百臺或者更多Redis伺服器。Redis作為一款功能強大的系統,無論是儲存、佇列還是快取系統,都有其用武之地。
SpringBoot框架入門的可以參考我之前的部落格:http://blog.csdn.net/u014427391/article/details/70655332
第四章 Redis快取實現
4.1下面結構圖
專案結構圖:
4.2 SpringBoot的yml檔案配置
新增resource下面的application.yml配置,這裡主要配置mysql,druid,redis
spring:
datasource:
# 主資料來源
shop:
url: jdbc:mysql://127.0.0.1:3306/jeeplatform?autoReconnect=true&useUnicode=true&characterEncoding=utf8&characterSetResults=utf8&useSSL=false
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
# 連線池設定
druid:
initial-size: 5
min-idle: 5
max-active: 20
# 配置獲取連線等待超時的時間
max-wait: 60000
# 配置間隔多久才進行一次檢測,檢測需要關閉的空閒連線,單位是毫秒
time-between-eviction-runs-millis: 60000
# 配置一個連線在池中最小生存的時間,單位是毫秒
min-evictable-idle-time-millis: 300000
# Oracle請使用select 1 from dual
validation-query: SELECT 'x'
test-while-idle: true
test-on-borrow: false
test-on-return: false
# 開啟PSCache,並且指定每個連線上PSCache的大小
pool-prepared-statements: true
max-pool-prepared-statement-per-connection-size: 20
# 配置監控統計攔截的filters,去掉後監控介面sql無法統計,'wall'用於防火牆
filters: stat,wall,slf4j
# 通過connectProperties屬性來開啟mergeSql功能;慢SQL記錄
connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
# 合併多個DruidDataSource的監控資料
use-global-data-source-stat: true
jpa:
database: mysql
hibernate:
show_sql: true
format_sql: true
ddl-auto: none
naming:
physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
mvc:
view:
prefix: /WEB-INF/jsp/
suffix: .jsp
#Jedis配置
jedis :
pool :
host : 127.0.0.1
port : 6379
password : password
timeout : 0
config :
maxTotal : 100
maxIdle : 10
maxWaitMillis : 100000
編寫一個配置類啟動配置JedisConfig.java:
package org.muses.jeeplatform.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
@Configuration
//@ConfigurationProperties(prefix = JedisConfig.JEDIS_PREFIX )
public class JedisConfig {
//public static final String JEDIS_PREFIX = "jedis";
@Bean(name= "jedisPool")
@Autowired
public JedisPool jedisPool(@Qualifier("jedisPoolConfig") JedisPoolConfig config,
@Value("${spring.jedis.pool.host}")String host,
@Value("${spring.jedis.pool.port}")int port,
@Value("${spring.jedis.pool.timeout}")int timeout,
@Value("${spring.jedis.pool.password}")String password) {
return new JedisPool(config, host, port,timeout,password);
}
@Bean(name= "jedisPoolConfig")
public JedisPoolConfig jedisPoolConfig (@Value("${spring.jedis.pool.config.maxTotal}")int maxTotal,
@Value("${spring.jedis.pool.config.maxIdle}")int maxIdle,
@Value("${spring.jedis.pool.config.maxWaitMillis}")int maxWaitMillis) {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(maxTotal);
config.setMaxIdle(maxIdle);
config.setMaxWaitMillis(maxWaitMillis);
return config;
}
}
4.3 元註解類編寫
編寫一個元註解類RedisCache.java,被改註解定義的類都自動實現AOP快取處理
package org.muses.jeeplatform.annotation;
import org.muses.jeeplatform.common.RedisCacheNamespace;
import java.lang.annotation.*;
/**
* 元註解 用來標識查詢資料庫的方法
*/
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RedisCache {
// RedisCacheNamespace nameSpace();
}
JDK 5提供的註解,除了Retention以外,還有另外三個,即Target 、Inherited 和 Documented。基於這個,我們可以實現自定義的元註解
我們設定RedisCache基於Method方法級別引用。
1.RetentionPolicy.SOURCE 這種型別的Annotations只在原始碼級別保留,編譯時就會被忽略
2.RetentionPolicy.CLASS 這種型別的Annotations編譯時被保留,在class檔案中存在,但JVM將會忽略
3.RetentionPolicy.RUNTIME 這種型別的Annotations將被JVM保留,所以他們能在執行時被JVM或其他使用反射機制的程式碼所讀取和使用.
4.4 呼叫JedisPool實現Redis快取處理
package org.muses.jeeplatform.cache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import javax.annotation.Resource;
@Component("redisCache")
public class RedisCache {
@Autowired
private JedisPool jedisPool;
private JedisPool getJedisPool(){
return jedisPool;
}
public void setJedisPool(JedisPool jedisPool){
this.jedisPool = jedisPool;
}
/**
* 從Redis快取獲取資料
* @param redisKey
* @return
*/
public Object getDataFromRedis(String redisKey){
Jedis jedis = jedisPool.getResource();
byte[] byteArray = jedis.get(redisKey.getBytes());
if(byteArray != null){
return SerializeUtil.unSerialize(byteArray);
}
return null;
}
/**
* 儲存資料到Redis
* @param redisKey
*/
public String saveDataToRedis(String redisKey,Object obj){
byte[] bytes = SerializeUtil.serialize(obj);
Jedis jedis = jedisPool.getResource();
String code = jedis.set(redisKey.getBytes(), bytes);
return code;
}
}
物件序列化的工具類:
package org.muses.jeeplatform.cache;
import java.io.*;
public class SerializeUtil {
/**
* 序列化物件
* @param obj
* @return
*/
public static byte[] serialize(Object obj){
ObjectOutputStream oos = null;
ByteArrayOutputStream baos = null;
try{
baos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
byte[] byteArray = baos.toByteArray();
return byteArray;
}catch(IOException e){
e.printStackTrace();
}
return null;
}
/**
* 反序列化物件
* @param byteArray
* @return
*/
public static Object unSerialize(byte[] byteArray){
ByteArrayInputStream bais = null;
try {
//反序列化為物件
bais = new ByteArrayInputStream(byteArray);
ObjectInputStream ois = new ObjectInputStream(bais);
return ois.readObject();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
這裡記得Vo類都要實現Serializable
例如選單資訊VO類,這是一個JPA對映的實體類
package org.muses.jeeplatform.core.entity.admin;
import javax.persistence.*;
import java.io.Serializable;
import java.util.List;
/**
* @description 選單資訊實體
* @author Nicky
* @date 2017年3月17日
*/
@Table(name="sys_menu")
@Entity
public class Menu implements Serializable {
/** 選單Id**/
private int menuId;
/** 上級Id**/
private int parentId;
/** 選單名稱**/
private String menuName;
/** 選單圖示**/
private String menuIcon;
/** 選單URL**/
private String menuUrl;
/** 選單型別**/
private String menuType;
/** 選單排序**/
private String menuOrder;
/**選單狀態**/
private String menuStatus;
private List<Menu> subMenu;
private String target;
private boolean hasSubMenu = false;
public Menu() {
super();
}
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
public int getMenuId() {
return this.menuId;
}
public void setMenuId(int menuId) {
this.menuId = menuId;
}
@Column(length=100)
public int getParentId() {
return parentId;
}
public void setParentId(int parentId) {
this.parentId = parentId;
}
@Column(length=100)
public String getMenuName() {
return this.menuName;
}
public void setMenuName(String menuName) {
this.menuName = menuName;
}
@Column(length=30)
public String getMenuIcon() {
return this.menuIcon;
}
public void setMenuIcon(String menuIcon) {
this.menuIcon = menuIcon;
}
@Column(length=100)
public String getMenuUrl() {
return this.menuUrl;
}
public void setMenuUrl(String menuUrl) {
this.menuUrl = menuUrl;
}
@Column(length=100)
public String getMenuType() {
return this.menuType;
}
public void setMenuType(String menuType) {
this.menuType = menuType;
}
@Column(length=10)
public String getMenuOrder() {
return menuOrder;
}
public void setMenuOrder(String menuOrder) {
this.menuOrder = menuOrder;
}
@Column(length=10)
public String getMenuStatus(){
return menuStatus;
}
public void setMenuStatus(String menuStatus){
this.menuStatus = menuStatus;
}
@Transient
public List<Menu> getSubMenu() {
return subMenu;
}
public void setSubMenu(List<Menu> subMenu) {
this.subMenu = subMenu;
}
public void setTarget(String target){
this.target = target;
}
@Transient
public String getTarget(){
return target;
}
public void setHasSubMenu(boolean hasSubMenu){
this.hasSubMenu = hasSubMenu;
}
@Transient
public boolean getHasSubMenu(){
return hasSubMenu;
}
}
4.5 Spring AOP實現監控所有被@RedisCache註解的方法快取
先從Redis裡獲取快取,查詢不到,就查詢MySQL資料庫,然後再儲存到Redis快取裡,下次查詢時直接呼叫Redis快取
package org.muses.jeeplatform.cache;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
/**
* AOP實現Redis快取處理
*/
@Component
@Aspect
public class RedisAspect {
private static final Logger LOGGER = LoggerFactory.getLogger(RedisAspect.class);
@Autowired
@Qualifier("redisCache")
private RedisCache redisCache;
/**
* 攔截所有元註解RedisCache註解的方法
*/
@Pointcut("@annotation(org.muses.jeeplatform.annotation.RedisCache)")
public void pointcutMethod(){
}
/**
* 環繞處理,先從Redis裡獲取快取,查詢不到,就查詢MySQL資料庫,
* 然後再儲存到Redis快取裡
* @param joinPoint
* @return
*/
@Around("pointcutMethod()")
public Object around(ProceedingJoinPoint joinPoint){
//前置:從Redis裡獲取快取
//先獲取目標方法引數
long startTime = System.currentTimeMillis();
String applId = null;
Object[] args = joinPoint.getArgs();
if (args != null && args.length > 0) {
applId = String.valueOf(args[0]);
}
//獲取目標方法所在類
String target = joinPoint.getTarget().toString();
String className = target.split("@")[0];
//獲取目標方法的方法名稱
String methodName = joinPoint.getSignature().getName();
//redis中key格式: applId:方法名稱
String redisKey = applId + ":" + className + "." + methodName;
Object obj = redisCache.getDataFromRedis(redisKey);
if(obj!=null){
LOGGER.info("**********從Redis中查到了資料**********");
LOGGER.info("Redis的KEY值:"+redisKey);
LOGGER.info("REDIS的VALUE值:"+obj.toString());
return obj;
}
long endTime = System.currentTimeMillis();
LOGGER.info("Redis快取AOP處理所用時間:"+(endTime-startTime));
LOGGER.info("**********沒有從Redis查到資料**********");
try{
obj = joinPoint.proceed();
}catch(Throwable e){
e.printStackTrace();
}
LOGGER.info("**********開始從MySQL查詢資料**********");
//後置:將資料庫查到的資料儲存到Redis
String code = redisCache.saveDataToRedis(redisKey,obj);
if(code.equals("OK")){
LOGGER.info("**********資料成功儲存到Redis快取!!!**********");
LOGGER.info("Redis的KEY值:"+redisKey);
LOGGER.info("REDIS的VALUE值:"+obj.toString());
}
return obj;
}
}
然後呼叫@RedisCache實現快取
/**
* 通過選單Id獲取選單資訊
* @param id
* @return
*/
@Transactional
@RedisCache
public Menu findMenuById(@RedisCacheKey int id){
return menuRepository.findMenuByMenuId(id);
}
登入系統,然後加入@RedisCache註解的方法都會實現Redis快取處理
可以看到Redis裡儲存到了快取
更多學習需求可以留言告訴CSDN學院的我們哦~~
推薦CSDN學院地址:https://edu.csdn.net?utm_source=blog11
-------------------------------------------------
希望本文對大家有用!
相關文章
- SpringBoot快取管理(二) 整合Redis快取實現Spring Boot快取Redis
- Redis 快取常見問題處理Redis快取
- 15.SpringBoot整合Redis快取實現Spring BootRedis快取
- SpringBoot整合Redis快取Spring BootRedis快取
- spring boot使用Jedis整合Redis實現快取(AOP)Spring BootRedis快取
- SpringBoot中整合Redis(快取篇)Spring BootRedis快取
- 介紹SpringBoot 整合 Redis 快取Spring BootRedis快取
- 如何處理快取導致的無效曝光快取
- SpringBoot中使用Redis實現快取Spring BootRedis快取
- 【SpringBoot】結合Redis實現快取Spring BootRedis快取
- Spring AOP整合redis(註解方式) 實現快取統一管理SpringRedis快取
- Springboot 整合 SpringCache 使用 Redis 作為快取Spring BootGCRedis快取
- Redis 快取雪崩,快取擊穿和快取穿透技術方案總結Redis快取穿透
- 3大問題!Redis快取異常及處理方案總結Redis快取
- 如何處理 Spring Boot 中與快取相關的錯誤?Spring Boot快取
- SpringBoot整合MongoDB(實現一個簡單快取)Spring BootMongoDB快取
- 實現SpringBoot + Redis快取的原始碼與教程Spring BootRedis快取原始碼
- springboot +lettuce +redis 快取使用Spring BootRedis快取
- Laravel 中的快取,屬性沒了,類沒了如何處理?Laravel快取
- 快取技術快取
- SpringBoot 整合快取效能之王 CaffeineSpring Boot快取
- SpringBoot+Redis實現介面級別快取資訊Spring BootRedis快取
- YYWebImage 原始碼剖析:執行緒處理與快取策略Web原始碼執行緒快取
- SpringBoot系列(十五)整合快取,專案會用得到的技術Spring Boot快取
- SpringBoot2 基礎案例(08):整合Redis資料庫,實現快取管理Spring BootRedis資料庫快取
- SpringBoot 註解呼叫Redis快取Spring BootRedis快取
- Redis快取擊穿、快取穿透、快取雪崩Redis快取穿透
- [Redis]快取穿透/快取擊穿/快取雪崩Redis快取穿透
- 如何使用 Redis 快取Redis快取
- SpringBoot 實戰 (十一) | 整合資料快取 CacheSpring Boot快取
- SpringBoot中實現兩級快取Spring Boot快取
- Python快取技術Python快取
- 如何利用Redis實現延時處理Redis
- WEB 應用快取解析以及使用 Redis 實現分散式快取Web快取Redis分散式
- 聊聊如何利用redis實現多級快取同步Redis快取
- 搞懂分散式技術14:Spring Boot使用註解整合Redis快取分散式Spring BootRedis快取
- SpringBoot快取管理(三) 自定義Redis快取序列化機制Spring Boot快取Redis
- springboot註解方式使用redis快取Spring BootRedis快取