標籤:rgs condition not csdn tar mode type 運行 就會
第一章 需求分析
計劃在Team的開源項目裡加入Redis實現緩衝處理,因為業務功能已經實現了一部分,通過寫Redis工具類,然後引用,改動量較大,而且不可以實現解耦合,所以想到了Spring架構的AOP(面向切面編程)。
開源項目:https://github.com/u014427391/jeeplatform
歡迎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")@Entitypublic 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@Aspectpublic 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裡儲存到了緩衝
項目代碼:https://github.com/u014427391/jeeplatform,歡迎去github上star(收藏)
SpringBoot整合Redis實現緩衝處理(Spring AOP實現)