第一章 需求分析
武山網(wǎng)站制作公司哪家好,找成都創(chuàng)新互聯(lián)!從網(wǎng)頁設(shè)計、網(wǎng)站建設(shè)、微信開發(fā)、APP開發(fā)、成都響應(yīng)式網(wǎng)站建設(shè)等網(wǎng)站項目制作,到程序開發(fā),運(yùn)營維護(hù)。成都創(chuàng)新互聯(lián)2013年開創(chuàng)至今到現(xiàn)在10年的時間,我們擁有了豐富的建站經(jīng)驗和運(yùn)維經(jīng)驗,來保證我們的工作的順利進(jìn)行。專注于網(wǎng)站建設(shè)就選成都創(chuàng)新互聯(lián)。
計劃在Team的開源項目里加入redis實現(xiàn)緩存處理,因為業(yè)務(wù)功能已經(jīng)實現(xiàn)了一部分,通過寫Redis工具類,然后引用,改動量較大,而且不可以實現(xiàn)解耦合,所以想到了Spring框架的AOP(面向切面編程)。
開源項目:https://github.com/u014427391/jeeplatform
第二章 SpringBoot簡介
Spring框架作為JavaEE框架領(lǐng)域的一款重要的開源框架,在企業(yè)應(yīng)用開發(fā)中有著很重要的作用,同時Spring框架及其子框架很多,所以知識量很廣。
SpringBoot:一款Spring框架的子框架,也可以叫微框架,是2014年推出的一款使Spring框架開發(fā)變得容易的框架。學(xué)過Spring框架的都知識,Spring框架難以避免地需要配置不少XMl,而使用SpringBoot框架的話,就可以使用注解開發(fā),極大地簡化基于Spring框架的開發(fā)。SpringBoot充分利用了JavaConfig的配置模式以及“約定優(yōu)于配置”的理念,能夠極大的簡化基于SpringMVC的Web應(yīng)用和REST服務(wù)開發(fā)。
第三章 Redis簡介
3.1 Redis安裝部署(Linux)
Redis安裝部署的可以參考我的博客(Redis是基于C編寫的,所以安裝前先安裝gcc編譯器):https://www.jb51.net/article/79096.htm
3.2 Redis簡介
Redis如今已經(jīng)成為Web開發(fā)社區(qū)最火熱的內(nèi)存數(shù)據(jù)庫之一,隨著Web2.0的快速發(fā)展,再加上半結(jié)構(gòu)數(shù)據(jù)比重加大,網(wǎng)站對高效性能的需求也越來越多。
而且大型網(wǎng)站一般都有幾百臺或者更多Redis服務(wù)器。Redis作為一款功能強(qiáng)大的系統(tǒng),無論是存儲、隊列還是緩存系統(tǒng),都有其用武之地。
SpringBoot框架入門的可以參考之前的文章:https://www.jb51.net/article/111197.htm
第四章 Redis緩存實現(xiàn)
4.1下面結(jié)構(gòu)圖
項目結(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 # 配置獲取連接等待超時的時間 max-wait: 60000 # 配置間隔多久才進(jìn)行一次檢測,檢測需要關(guān)閉的空閑連接,單位是毫秒 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 # 配置監(jiān)控統(tǒng)計攔截的filters,去掉后監(jiān)控界面sql無法統(tǒng)計,'wall'用于防火墻 filters: stat,wall,slf4j # 通過connectProperties屬性來打開mergeSql功能;慢SQL記錄 connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 # 合并多個DruidDataSource的監(jiān)控數(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
編寫一個配置類啟動配置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,被改注解定義的類都自動實現(xiàn)AOP緩存處理
package org.muses.jeeplatform.annotation; import org.muses.jeeplatform.common.RedisCacheNamespace; import java.lang.annotation.*; /** * 元注解 用來標(biāo)識查詢數(shù)據(jù)庫的方法 */ @Documented @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RedisCache { // RedisCacheNamespace nameSpace(); }
JDK 5提供的注解,除了Retention以外,還有另外三個,即Target 、Inherited 和 Documented?;谶@個,我們可以實現(xiàn)自定義的元注解
我們設(shè)置RedisCache基于Method方法級別引用。
1.RetentionPolicy.SOURCE 這種類型的Annotations只在源代碼級別保留,編譯時就會被忽略
2.RetentionPolicy.CLASS 這種類型的Annotations編譯時被保留,在class文件中存在,但JVM將會忽略
3.RetentionPolicy.RUNTIME 這種類型的Annotations將被JVM保留,所以他們能在運(yùn)行時被JVM或其他使用反射機(jī)制的代碼所讀取和使用.
4.4 調(diào)用JedisPool實現(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; } }
對象序列化的工具類:
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類都要實現(xiàn)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; /** 菜單圖標(biāo)**/ private String menuIcon; /** 菜單URL**/ private String menuUrl; /** 菜單類型**/ private String menuType; /** 菜單排序**/ private String menuOrder; /**菜單狀態(tài)**/ private String menuStatus; private List
4.5 Spring AOP實現(xiàn)監(jiān)控所有被@RedisCache注解的方法緩存
先從Redis里獲取緩存,查詢不到,就查詢MySQL數(shù)據(jù)庫,然后再保存到Redis緩存里,下次查詢時直接調(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實現(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ù)庫, * 然后再保存到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處理所用時間:"+(endTime-startTime)); LOGGER.info("**********沒有從Redis查到數(shù)據(jù)**********"); try{ obj = joinPoint.proceed(); }catch(Throwable e){ e.printStackTrace(); } LOGGER.info("**********開始從MySQL查詢數(shù)據(jù)**********"); //后置:將數(shù)據(jù)庫查到的數(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實現(xiàn)緩存
/** * 通過菜單Id獲取菜單信息 * @param id * @return */ @Transactional @RedisCache public Menu findMenuById(@RedisCacheKey int id){ return menuRepository.findMenuByMenuId(id); }
登錄系統(tǒng),然后加入@RedisCache注解的方法都會實現(xiàn)Redis緩存處理
可以看到Redis里保存到了緩存
項目代碼:https://github.com/u014427391/jeeplatform
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。