SpringBoot整合Redis實現緩衝處理(Spring AOP實現)

來源:互聯網
上載者:User

標籤: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實現)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.