淺談SpringBoot集成Redis實(shí)現(xiàn)緩存處理(Spring AOP實(shí)現(xiàn))
第一章 需求分析
計(jì)劃在Team的開(kāi)源項(xiàng)目里加入Redis實(shí)現(xiàn)緩存處理,因?yàn)闃I(yè)務(wù)功能已經(jīng)實(shí)現(xiàn)了一部分,通過(guò)寫Redis工具類,然后引用,改動(dòng)量較大,而且不可以實(shí)現(xiàn)解耦合,所以想到了Spring框架的AOP(面向切面編程)。
開(kāi)源項(xiàng)目:https://github.com/u014427391/jeeplatform
第二章 SpringBoot簡(jiǎn)介
Spring框架作為JavaEE框架領(lǐng)域的一款重要的開(kāi)源框架,在企業(yè)應(yīng)用開(kāi)發(fā)中有著很重要的作用,同時(shí)Spring框架及其子框架很多,所以知識(shí)量很廣。
SpringBoot:一款Spring框架的子框架,也可以叫微框架,是2014年推出的一款使Spring框架開(kāi)發(fā)變得容易的框架。學(xué)過(guò)Spring框架的都知識(shí),Spring框架難以避免地需要配置不少XMl,而使用SpringBoot框架的話,就可以使用注解開(kāi)發(fā),極大地簡(jiǎn)化基于Spring框架的開(kāi)發(fā)。SpringBoot充分利用了JavaConfig的配置模式以及“約定優(yōu)于配置”的理念,能夠極大的簡(jiǎn)化基于SpringMVC的Web應(yīng)用和REST服務(wù)開(kāi)發(fā)。
第三章 Redis簡(jiǎn)介
3.1 Redis安裝部署(Linux)
Redis安裝部署的可以參考我的博客(Redis是基于C編寫的,所以安裝前先安裝gcc編譯器):http://chabaoo.cn/article/79096.htm
3.2 Redis簡(jiǎn)介
Redis如今已經(jīng)成為Web開(kāi)發(fā)社區(qū)最火熱的內(nèi)存數(shù)據(jù)庫(kù)之一,隨著Web2.0的快速發(fā)展,再加上半結(jié)構(gòu)數(shù)據(jù)比重加大,網(wǎng)站對(duì)高效性能的需求也越來(lái)越多。
而且大型網(wǎng)站一般都有幾百臺(tái)或者更多Redis服務(wù)器。Redis作為一款功能強(qiáng)大的系統(tǒng),無(wú)論是存儲(chǔ)、隊(duì)列還是緩存系統(tǒng),都有其用武之地。
SpringBoot框架入門的可以參考之前的文章:http://chabaoo.cn/article/111197.htm
第四章 Redis緩存實(shí)現(xiàn)
4.1下面結(jié)構(gòu)圖
項(xiàng)目結(jié)構(gòu)圖:
4.2 SpringBoot的yml文件配置
添加resource下面的application.yml配置,這里主要配置mysql,druid,redis
spring:
datasource:
# 主數(shù)據(jù)源
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
# 連接池設(shè)置
druid:
initial-size: 5
min-idle: 5
max-active: 20
# 配置獲取連接等待超時(shí)的時(shí)間
max-wait: 60000
# 配置間隔多久才進(jìn)行一次檢測(cè),檢測(cè)需要關(guān)閉的空閑連接,單位是毫秒
time-between-eviction-runs-millis: 60000
# 配置一個(gè)連接在池中最小生存的時(shí)間,單位是毫秒
min-evictable-idle-time-millis: 300000
# Oracle請(qǐng)使用select 1 from dual
validation-query: SELECT 'x'
test-while-idle: true
test-on-borrow: false
test-on-return: false
# 打開(kāi)PSCache,并且指定每個(gè)連接上PSCache的大小
pool-prepared-statements: true
max-pool-prepared-statement-per-connection-size: 20
# 配置監(jiān)控統(tǒng)計(jì)攔截的filters,去掉后監(jiān)控界面sql無(wú)法統(tǒng)計(jì),'wall'用于防火墻
filters: stat,wall,slf4j
# 通過(guò)connectProperties屬性來(lái)打開(kāi)mergeSql功能;慢SQL記錄
connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
# 合并多個(gè)DruidDataSource的監(jiān)控?cái)?shù)據(jù)
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
編寫一個(gè)配置類啟動(dòng)配置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 元注解類編寫
編寫一個(gè)元注解類RedisCache.java,被改注解定義的類都自動(dòng)實(shí)現(xiàn)AOP緩存處理
package org.muses.jeeplatform.annotation;
import org.muses.jeeplatform.common.RedisCacheNamespace;
import java.lang.annotation.*;
/**
* 元注解 用來(lái)標(biāo)識(shí)查詢數(shù)據(jù)庫(kù)的方法
*/
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RedisCache {
// RedisCacheNamespace nameSpace();
}
JDK 5提供的注解,除了Retention以外,還有另外三個(gè),即Target 、Inherited 和 Documented?;谶@個(gè),我們可以實(shí)現(xiàn)自定義的元注解
我們?cè)O(shè)置RedisCache基于Method方法級(jí)別引用。
1.RetentionPolicy.SOURCE 這種類型的Annotations只在源代碼級(jí)別保留,編譯時(shí)就會(huì)被忽略
2.RetentionPolicy.CLASS 這種類型的Annotations編譯時(shí)被保留,在class文件中存在,但JVM將會(huì)忽略
3.RetentionPolicy.RUNTIME 這種類型的Annotations將被JVM保留,所以他們能在運(yùn)行時(shí)被JVM或其他使用反射機(jī)制的代碼所讀取和使用.
4.4 調(diào)用JedisPool實(shí)現(xiàn)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緩存獲取數(shù)據(jù)
* @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;
}
/**
* 保存數(shù)據(jù)到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;
}
}
對(duì)象序列化的工具類:
package org.muses.jeeplatform.cache;
import java.io.*;
public class SerializeUtil {
/**
* 序列化對(duì)象
* @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;
}
/**
* 反序列化對(duì)象
* @param byteArray
* @return
*/
public static Object unSerialize(byte[] byteArray){
ByteArrayInputStream bais = null;
try {
//反序列化為對(duì)象
bais = new ByteArrayInputStream(byteArray);
ObjectInputStream ois = new ObjectInputStream(bais);
return ois.readObject();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
這里記得Vo類都要實(shí)現(xiàn)Serializable
例如菜單信息VO類,這是一個(gè)JPA映射的實(shí)體類
package org.muses.jeeplatform.core.entity.admin;
import javax.persistence.*;
import java.io.Serializable;
import java.util.List;
/**
* @description 菜單信息實(shí)體
* @author Nicky
* @date 2017年3月17日
*/
@Table(name="sys_menu")
@Entity
public class Menu implements Serializable {
/** 菜單Id**/
private int menuId;
/** 上級(jí)Id**/
private int parentId;
/** 菜單名稱**/
private String menuName;
/** 菜單圖標(biāo)**/
private String menuIcon;
/** 菜單URL**/
private String menuUrl;
/** 菜單類型**/
private String menuType;
/** 菜單排序**/
private String menuOrder;
/**菜單狀態(tài)**/
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實(shí)現(xiàn)監(jiān)控所有被@RedisCache注解的方法緩存
先從Redis里獲取緩存,查詢不到,就查詢MySQL數(shù)據(jù)庫(kù),然后再保存到Redis緩存里,下次查詢時(shí)直接調(diào)用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實(shí)現(xiàn)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(){
}
/**
* 環(huán)繞處理,先從Redis里獲取緩存,查詢不到,就查詢MySQL數(shù)據(jù)庫(kù),
* 然后再保存到Redis緩存里
* @param joinPoint
* @return
*/
@Around("pointcutMethod()")
public Object around(ProceedingJoinPoint joinPoint){
//前置:從Redis里獲取緩存
//先獲取目標(biāo)方法參數(shù)
long startTime = System.currentTimeMillis();
String applId = null;
Object[] args = joinPoint.getArgs();
if (args != null && args.length > 0) {
applId = String.valueOf(args[0]);
}
//獲取目標(biāo)方法所在類
String target = joinPoint.getTarget().toString();
String className = target.split("@")[0];
//獲取目標(biāo)方法的方法名稱
String methodName = joinPoint.getSignature().getName();
//redis中key格式: applId:方法名稱
String redisKey = applId + ":" + className + "." + methodName;
Object obj = redisCache.getDataFromRedis(redisKey);
if(obj!=null){
LOGGER.info("**********從Redis中查到了數(shù)據(jù)**********");
LOGGER.info("Redis的KEY值:"+redisKey);
LOGGER.info("REDIS的VALUE值:"+obj.toString());
return obj;
}
long endTime = System.currentTimeMillis();
LOGGER.info("Redis緩存AOP處理所用時(shí)間:"+(endTime-startTime));
LOGGER.info("**********沒(méi)有從Redis查到數(shù)據(jù)**********");
try{
obj = joinPoint.proceed();
}catch(Throwable e){
e.printStackTrace();
}
LOGGER.info("**********開(kāi)始從MySQL查詢數(shù)據(jù)**********");
//后置:將數(shù)據(jù)庫(kù)查到的數(shù)據(jù)保存到Redis
String code = redisCache.saveDataToRedis(redisKey,obj);
if(code.equals("OK")){
LOGGER.info("**********數(shù)據(jù)成功保存到Redis緩存!!!**********");
LOGGER.info("Redis的KEY值:"+redisKey);
LOGGER.info("REDIS的VALUE值:"+obj.toString());
}
return obj;
}
}
然后調(diào)用@RedisCache實(shí)現(xiàn)緩存
/**
* 通過(guò)菜單Id獲取菜單信息
* @param id
* @return
*/
@Transactional
@RedisCache
public Menu findMenuById(@RedisCacheKey int id){
return menuRepository.findMenuByMenuId(id);
}
登錄系統(tǒng),然后加入@RedisCache注解的方法都會(huì)實(shí)現(xiàn)Redis緩存處理
可以看到Redis里保存到了緩存
項(xiàng)目代碼:https://github.com/u014427391/jeeplatform
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Springboot中使用Redisson+AOP+自定義注解實(shí)現(xiàn)訪問(wèn)限流與黑名單攔截
- SpringBoot整合redis+Aop防止重復(fù)提交的實(shí)現(xiàn)
- SpringBoot+Redis使用AOP防止重復(fù)提交的實(shí)現(xiàn)
- SpringBoot?使用AOP?+?Redis?防止表單重復(fù)提交的方法
- SpringBoot使用自定義注解+AOP+Redis實(shí)現(xiàn)接口限流的實(shí)例代碼
- SpringBoot?AOP?Redis實(shí)現(xiàn)延時(shí)雙刪功能實(shí)戰(zhàn)
- SpringBoot AOP控制Redis自動(dòng)緩存和更新的示例
- Springboot整合AOP和redis的示例詳解
相關(guān)文章
Java如何將二維數(shù)組轉(zhuǎn)化為一維數(shù)組
這篇文章主要介紹了Java如何將二維數(shù)組轉(zhuǎn)化為一維數(shù)組,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08
Java調(diào)用接口如何獲取json數(shù)據(jù)解析后保存到數(shù)據(jù)庫(kù)
這篇文章主要介紹了Java調(diào)用接口如何獲取json數(shù)據(jù)解析后保存到數(shù)據(jù)庫(kù)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03
SpringBoot混合使用StringRedisTemplate和RedisTemplate的坑及解決
這篇文章主要介紹了SpringBoot混合使用StringRedisTemplate和RedisTemplate的坑及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12
Java中double和float類型的區(qū)別與使用方法
float和double都是用來(lái)表示浮點(diǎn)數(shù)的數(shù)據(jù)類型,但是它們之間有一些區(qū)別,這篇文章主要給大家介紹了關(guān)于Java中double和float類型的區(qū)別與使用方法的相關(guān)資料,需要的朋友可以參考下2024-07-07
Windows下使用Graalvm將Springboot應(yīng)用編譯成exe大大提高啟動(dòng)和運(yùn)行效率(推薦)
這篇文章主要介紹了Windows下使用Graalvm將Springboot應(yīng)用編譯成exe大大提高啟動(dòng)和運(yùn)行效率,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-02-02
SpringBoot + validation 接口參數(shù)校驗(yàn)的思路詳解
這篇文章主要介紹了SpringBoot + validation 接口參數(shù)校驗(yàn),本文通過(guò)項(xiàng)目實(shí)踐+場(chǎng)景分析給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-10-10

