1. 程式人生 > >第七章 企業專案開發--本地快取guava cache

第七章 企業專案開發--本地快取guava cache

1、在實際專案開發中,會使用到很多快取技術,而且資料庫的設計一般也會依賴於有快取的情況下設計。

  • 常用的快取分兩種:本地快取和分散式快取。
  • 常用的本地快取是guava cache,本章主要介紹guava cache在專案中的使用。

關於常用快取以及每種快取常用場景的介紹,之後可以去檢視我記錄的"Java快取相關"系列部落格。連結如下:

2、實際使用

本專案的程式碼基於第六章的程式碼進行構建,這裡只列出修改過的程式碼:

2.1、ssmm0-data

pom.xml:

        <!-- guava cache -->
        <
dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>14.0.1</version> </dependency>
View Code

在pom.xml中引入了guava cache14.0.1的依賴包。

AdminMapper:

package com.xxx.mapper.userManagement;

import java.util.List; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Result; import org.apache.ibatis.annotations.Results; import org.apache.ibatis.annotations.Select; import com.xxx.model.userManagement.Admin;
/** * 管理員Mapper */ public interface AdminMapper { /**************註解**************/ @Insert("INSERT INTO userinfo(username, password) VALUES(#{username},#{password})") public int insertAdmin(Admin admin); @Select("SELECT * FROM userinfo WHERE username = #{username} AND password = #{password}") @Results(value = { @Result(id = true, column = "id", property = "id"), @Result(column = "username", property = "username"), @Result(column = "password", property = "password") }) public Admin selectAdmin(@Param("username") String username, @Param("password") String password); /***************xml**************/ /** * 條件不定式查詢 * 我們這裡使用@Param指定引數,這樣的話,在AdminMapper.xml中就不用再使用parameterType屬性了;否則得寫parameterType屬性 */ public List<Admin> getAdminByConditions(@Param("username")String username, @Param("password")String password, @Param("start")int start, @Param("limit")int limit); /** * 返回主鍵 */ public int insertAdminWithBackId(Admin admin); /****************guava cache*****************/ @Select("SELECT * FROM userinfo WHERE username = #{username}") @Results(value = { @Result(id = true, column = "id", property = "id"), @Result(column = "username", property = "username"), @Result(column = "password", property = "password") }) public List<Admin> getUserByName(@Param("username") String username); }
View Code

將使用到的兩個方法:

  • public List<Admin> getUserByName(String username)
  • public List<Admin> getAdminByConditions(String username, String password, int start, int limit)

AdminDao:

package com.xxx.dao.userManagement;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.xxx.mapper.userManagement.AdminMapper;
import com.xxx.model.userManagement.Admin;

/**
 * 管理員DAO
 */
@Repository
public class AdminDao {
    @Autowired
    private AdminMapper adminMapper;
    /***************註解*****************/
    public boolean register(Admin admin){
        return adminMapper.insertAdmin(admin)==1?true:false;
    }
    
    public Admin login(String username ,String password){
        return adminMapper.selectAdmin(username, password);
    }
    /****************xml******************/
    public List<Admin> findAdmin(String username, String password, int start, int limit){
        return adminMapper.getAdminByConditions(username, password, start, limit);
    }
    
    public int insertAdminWithBackId(Admin admin){
        return adminMapper.insertAdminWithBackId(admin);
    }
    /******************guava cache********************/
    public List<Admin> getUserByName(String username){
        return adminMapper.getUserByName(username);
    }
}
View Code

將使用到的兩個方法:

  • public List<Admin> getUserByName(String username)
  • public List<Admin> findAdmin(String username, String password, int start, int limit)

AdminService:

package com.xxx.service.userManagement;

import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.xxx.dao.userManagement.AdminDao;
import com.xxx.model.userManagement.Admin;
import com.xxx.vo.userManagement.AdminCacheKey;

/**
 * 管理員service
 */
@Service
public class AdminService {
    @Autowired
    private AdminDao adminDao;

    public boolean register(Admin admin) {
        return adminDao.register(admin);
    }

    public Admin login(String username, String password) {
        return adminDao.login(username, password);
    }

    /*********** 以下方法是為了測試mybatis中使用xml **********/
    public List<Admin> findAdmin(String username, 
                                 String password, 
                                 int start,
                                 int limit) {
        return adminDao.findAdmin(username, password, start, limit);
    }

    public Admin insertAdminWithBackId(Admin admin) {
        int record = adminDao.insertAdminWithBackId(admin);
        if (record == 1) {
            return admin;// 這時的admin已經被賦予主鍵了
        }
        return null;
    }

    /************************ guava cache *************************/
    /************單條件的查詢,key為String***********/
    public List<Admin> findByUsername(String username) {
        List<Admin> adminList = null;
        try {
            adminList = adminListCache.get(username);
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        return adminList;
    }

    LoadingCache<String, List<Admin>> adminListCache = CacheBuilder.newBuilder()
            .expireAfterWrite(20, TimeUnit.MINUTES)// 快取20分鐘
            .maximumSize(1000)// 最多快取1000個物件
            .build(new CacheLoader<String, List<Admin>>() {
                public List<Admin> load(String username) throws Exception {
                    return adminDao.getUserByName(username);
                }
            });

    /************多條件的查詢,key為Object(封裝了多個條件的VO類)***********/
    public List<Admin> findAdminList(String username, 
                                     String password,
                                     int start, 
                                     int limit) {
        /*
         * 注意:
         * 如果以一個新建的物件做為key的話,因為每次都是新建一個物件,所以這樣的話,實際上每次訪問key都是不同的,即每次訪問都是重新進行快取;
         * 但是實際上,我們想要根據物件的屬性來判斷物件是否相等,只需要根據這些屬性重寫物件的hashCode與equals方法即可,
         * 所以重寫了AdminCacheKey類的hashCode和equals方法,這樣,每次訪問的話,就會以每個條件是否相等來判斷物件(即key)是否相等了,這一塊兒的快取就會起作用了
         */
        AdminCacheKey cacheKey = new AdminCacheKey(username, 
                                                   password, 
                                                   start,
                                                   limit);
        List<Admin> adminList = null;
        try {
            System.out.println(cacheKey);
            adminList = adminsCache.get(cacheKey);
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        return adminList;
    }

    LoadingCache<AdminCacheKey, List<Admin>> adminsCache = CacheBuilder.newBuilder()
            .expireAfterWrite(60, TimeUnit.MINUTES) // 快取項在給定時間內(60min)沒有被寫訪問(建立或覆蓋),則回收
            .maximumSize(100) // 最多快取100項
            .build(new CacheLoader<AdminCacheKey, List<Admin>>() {
                public List<Admin> load(AdminCacheKey key) throws Exception {
                    return adminDao.findAdmin(key.getUsername(),
                                              key.getPassword(), 
                                              key.getStart(), 
                                              key.getLimit());
                }
            });

}
View Code

將使用到的兩個方法:

  • public List<Admin> findByUsername(String username)
  • public List<Admin> findAdminList(String username, String password, int start, int limit)

這一塊兒是整個guava cache使用的部分。這裡邊寫出了兩種guava cache使用的方式:

  • 單查詢條件:key為String或Object都可以
  • 多查詢條件:key為Object,該Object封裝了多個查詢條件,並通過這些查詢條件重寫了該Object的hashcode()和equals()

這一部分中guava cache的使用方式,就是實際開發中最常用的方法。

AdminCacheKey:

package com.xxx.vo.userManagement;

/**
 * guava cache的key
 */
public class AdminCacheKey {
    private String username;
    private String password;
    private int start;
    private int limit;

    public AdminCacheKey() {
    }

    public AdminCacheKey(String username, String password, int start, int limit) {
        this.username = username;
        this.password = password;
        this.start = start;
        this.limit = limit;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public int getStart() {
        return start;
    }

    public void setStart(int start) {
        this.start = start;
    }

    public int getLimit() {
        return limit;
    }

    public void setLimit(int limit) {
        this.limit = limit;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + limit;
        result = prime * result
                + ((password == null) ? 0 : password.hashCode());
        result = prime * result + start;
        result = prime * result
                + ((username == null) ? 0 : username.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        AdminCacheKey other = (AdminCacheKey) obj;
        if (limit != other.limit)
            return false;
        if (password == null) {
            if (other.password != null)
                return false;
        } else if (!password.equals(other.password))
            return false;
        if (start != other.start)
            return false;
        if (username == null) {
            if (other.username != null)
                return false;
        } else if (!username.equals(other.username))
            return false;
        return true;
    }
}
View Code

該類是封裝了多個查詢條件的一個VO類。

2.2、ssmm0-userManagement

AdminController:

package com.xxx.web.admin;

import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import com.xxx.model.userManagement.Admin;
import com.xxx.service.userManagement.AdminService;
import com.xxx.util.admin.AdminCookieUtil;

/**
 * adminController
 */
@Controller
@RequestMapping("/admin")
public class AdminController {
    
    @Autowired
    private AdminService adminService;
    
    /**
     * 管理員註冊
     */
    @ResponseBody
    @RequestMapping("/register")
    public boolean register(@RequestParam("username") String username,
                            @RequestParam("password") String password){
        Admin admin = new Admin();
        admin.setUsername(username);
        admin.setPassword(password);
        
        boolean isRegisterSuccess = adminService.register(admin);
        
        return isRegisterSuccess;
    }
    
    /**
     * 管理員登入
     */
    @RequestMapping("/login")
    public ModelAndView login(@RequestParam("username") String username,
                              @RequestParam("password") String password,
                              HttpServletResponse response,
                              HttpSession session){
        
        
        Admin admin = adminService.login(username, password);
        
        ModelAndView modelAndView = new ModelAndView();
        if(admin == null){
            modelAndView.addObject("message", "使用者不存在或者密碼錯誤!請重新輸入");
            modelAndView.setViewName("error");
        }else{
            modelAndView.addObject("admin", admin);
            modelAndView.setViewName("userinfo");
            /*
             * 這為什麼不直接傳一個username,而傳了一個admin,
             * 是因為在實際開發中,你傳過去的資訊可能不只是username,還有使用者手機號、地址等等
             */
            //使用cookie
            AdminCookieUtil.addLoginCookie(admin, response);
            //使用session
            //session.setAttribute("adminSession", admin);
        }
        
        return modelAndView;
    }
    
    /*****************************mybatis xml方式解決的問題*******************************/
    /**
     * 根據username或password查詢List<Admin>
     */
    @ResponseBody
    @RequestMapping("/findAdmin")
    public List<Admin> findAdmin(@RequestParam(value="username",required=false) String username,
                                    @RequestParam(value="password",required=false) String password,
                                    @RequestParam("start") int start,
                                    @RequestParam("limit") int limit,
                                    HttpServletRequest request,
                                    HttpSession session){
        Admin admin = AdminCookieUtil.getLoginCookie(request);
        //Admin admin = (Admin) session.getAttribute("adminSession");
        
        if(admin == null){//未登入
            return null;
        }
        System.out.println(admin.toJson());
        List<Admin> adminList = adminService.findAdmin(username, password, start, limit);
        return adminList;
    }
    
    /**
     * 插入一個使用者並返回主鍵
     * 注意:get請求也會自動裝配(即將前臺傳入的username和password傳入admin)
     */
    @ResponseBody
    @RequestMapping("/insert")
    public Admin insertAdminWithBackId(Admin admin){
        return adminService.insertAdminWithBackId(admin);
    }
    /*************************guava cache******************************/
    /**
     * 根據username查詢List<Admin>
     */
    @ResponseBody
    @RequestMapping("/findAdminByUsername")
    public List<Admin> findAdminByUserName(@RequestParam(value="username") String username){
        
        List<Admin> adminList = adminService.findByUsername(username);
        return adminList;
    }
    
    @ResponseBody
    @RequestMapping("/findAdminList")
    public List<Admin> findAdminList(@RequestParam(value="username") String username,
                                         @RequestParam(value="password",required=false) String password,
                                         @RequestParam("start") int start,
                                         @RequestParam("limit") int limit){
        
        List<Admin> adminList = adminService.findAdminList(username, password, start, limit);
        return adminList;
    }
}
View Code

將使用到的兩個方法:

  • public List<Admin> findAdminByUserName(String username)
  • public List<Admin> findAdminList(String username, String password, int start, int limit)

3、測試

  • 單元測試:使用springJunit去測就行
  • 整體測試:程式碼寫好之後,注意對程式碼去做測試的方法,先執行相應的controller的方法,然後對查詢出來的部分資料在資料庫中直接進行修改,再執行之前的controller對應的方法。出現兩種結果:
    • 第二次執行與第一次結果相同:快取成功
    • 第二次執行與第一次結果不同:快取不成功

4、總結:

  • 常用的幾個API:
    • get(Object key):首先獲取value-->若獲取不到,先快取-->再從快取中去取(以上三步是原子操作),使用該方法優先於使用put
    • getIfPresent(Object key):獲取value,若獲取不到,返回null;若獲取的到,返回value
    • put(Object key, Object value):顯示的新增快取key-value
  • guava cache的get(Object key)的value不能為null(這個可以去看原始碼的註釋),看下邊的程式碼例子:
        LoadingCache<String, List<Admin>> adminListCache = CacheBuilder.newBuilder()
                .expireAfterWrite(20, TimeUnit.MINUTES)// 快取20分鐘
                .maximumSize(1000)// 最多快取1000個物件
                .build(new CacheLoader<String, List<Admin>>() {
                    public List<Admin> load(String username) throws Exception {
                        //1、下邊這樣null的話,不拋異常
                        /*List<Admin> admins = adminDao.getUserByName(username);
                        if(admins==null){
                            return null; 
                        }
                        return admins;*/
                        //2、但是如果這裡查詢出來的結果為null的話,也沒關係
                        //return adminDao.getUserByName(username);
                        //3、如果這裡直接返回null,就會出現com.google.common.cache.CacheLoader$InvalidCacheLoadException
                        return null;
                    }
                });
    View Code

    注意:該程式碼中的三種null情況,只有第三種會丟擲異常。前兩種不為空的原因是因為,即使admins沒有元素,admins也不會是null,而是[],這應該是mybatis的功勞?!這個是個問題,以後在讀mybatis原始碼的時候,會仔細研究!!!但是實際使用中,我們判斷一個list是否為空,會使用CollectionUtil.isNotEmpty(list)類似於下邊這樣,就會丟擲異常了。

        LoadingCache<String, List<Admin>> adminListCache = CacheBuilder.newBuilder()
                .expireAfterWrite(20, TimeUnit.MINUTES)// 快取20分鐘
                .maximumSize(1000)// 最多快取1000個物件
                .build(new CacheLoader<String, List<Admin>>() {
                    public List<Admin> load(String username) throws Exception {
                        //1、下邊這樣null的話,不拋異常
                        List<Admin> admins = adminDao.getUserByName(username);
                        //System.out.println(admins);//如果admins為空,不會返回null,而是返回[]
                        if(CollectionUtils.isEmpty(admins)){
                            System.out.println(admins+"-->");
                            return null; 
                        }
                        return admins;
                        //2、但是如果這裡查詢出來的結果為null的話,也沒關係
                        //return adminDao.getUserByName(username);
                        //3、如果這裡直接返回null,就會出現com.google.common.cache.CacheLoader$InvalidCacheLoadException
                        //return null;
                    }
                });
    View Code

    但是,為了在guava cache的使用中不丟擲異常,我們這裡直接使用下邊這句就好了,由mybatis將[]返回就好了。

    return adminDao.getUserByName(username);

相關推薦

企業專案開發--本地快取guava cache

1、在實際專案開發中,會使用到很多快取技術,而且資料庫的設計一般也會依賴於有快取的情況下設計。 常用的快取分兩種:本地快取和分散式快取。 常用的本地快取是guava cache,本章主要介紹guava cache在專案中的使用。 關於常用快取以及每種快取常用場景的介紹,之後可以去檢視我記錄的"J

企業專案開發--本地快取guava cache(1)

此文已由作者趙計剛授權網易雲社群釋出。 歡迎訪問網易雲社群,瞭解更多網易技術產品運營經驗。 1、在實際專案開發中,會使用到很多快取技術,而且資料庫的設計一般也會依賴於有快取的情況下設計。 常用的快取分兩種:本地快取和分散式快取。 常用的本地快取是guava cache,本章主要介紹guava cach

企業專案開發--本地快取guava cache(2)

此文已由作者趙計剛授權網易雲社群釋出。 歡迎訪問網易雲社群,瞭解更多網易技術產品運營經驗。 AdminCacheKey: package com.xxx.vo.userManagement; /**  * guava cache的key  */

企業專案開發--分散式快取memcached

1、本地快取的問題 本地快取速度一開始高於分散式快取,但是隨著其快取數量的增加,所佔記憶體越來越大,系統執行記憶體越來越小,最後系統會被拖慢(這一點與第二點聯絡起來) 本地快取存於本機,其快取數量與大小受本機記憶體大小限制 本地快取存於本機,其他機器的訪問不到這樣的快取 解決方案:分散式快

PMBOK(六版) PMP筆記——《專案成本管理)

第七章 專案成本管理 1、規劃成本管理:制定成本管理計劃,用來指導後續的專案成本管理工作。 2、估算成本:估算各項進度活動的成本。 3、制定預算:把估算成本過程得出的各活動或工作的成本逐層向上彙總,建立成本基準。 4、控制成本:監督專案成本績效,管理成本基準變更。 估算成本:

企業專案開發--分散式快取memcached(3)

此文已由作者趙計剛授權網易雲社群釋出。 歡迎訪問網易雲社群,瞭解更多網易技術產品運營經驗。 3.3、ssmm0-data 結構: 3.3.1、pom.xml  1 <?xml version="1.0" encoding="UTF-8"?> &nb

第一 企業專案開發--maven+springmvc+spring+mybatis+velocity整合

說明:本系列文章主要是對自己在一家大型網際網路公司實習的過程中對所學知識的總結!參與的是實際中使用的上線專案。 一、ssmm簡介 ssmm是當下企業最常用的開發框架架構 maven:管理專案jar包,構建專案 spring:IOC容器,事務管理 springmvc:mvc框架 myBati

【.NET Core專案實戰-統一認證平臺】 閘道器篇-自定義客戶端限流

原文: 【.NET Core專案實戰-統一認證平臺】第七章 閘道器篇-自定義客戶端限流 【.NET Core專案實戰-統一認證平臺】開篇及目錄索引 上篇文章我介紹瞭如何在閘道器上增加自定義客戶端授權功能,從設計到編碼實現,一步一步詳細講解,相信大家也掌握了自定義中介軟體的開發技巧了,本篇我們將介紹如

【Python web 開發 開發總結

第七章開發總結:整理一下我們的知識點: 1、首先是django rest framwork 的token 登入和原理  django rest framwork  為我們提供了三種 認證模式 重點是 TokenAuthentication 這個   

Java本地介面(JNI)程式設計指南和規範()

第七章 呼叫介面 這章告訴你怎樣能嵌入一個"Java"虛擬器到你的本地應用程式中。一個Java虛擬器的實現是典型作為一個本地庫的運用。本地應用程式能針對這個庫連結和使用載入Java虛擬機器的呼叫介面。真正地,在"JDK"或"Java 2 SDK release"中得標準的

軟考-架構師--系統規劃 第一節 專案的提出與選擇 (讀書筆記)

#版權宣告 主要針對希賽出版的架構師考試教程《系統架構設計師教程(第4版)》,作者“希賽教育軟考學院”。完成相關的讀書筆記以便後期自查,僅供個人學習使用,不得用於任何商業用途。 文章目錄 第一節 專案的提出與選擇 專案的提出與選擇 專案的

使用BI Publisher開發報表-建立一個簡單的RTF模板(2/5)

第七章建立一個簡單的RTF模板 安裝好Oracle BI Publisher Desktop之後,可以使用Word中的BI Publisher外掛來進行報表佈局模板(RTF格式)設計。 1.開啟W

【無私分享:ASP.NET CORE 專案實戰()】檔案操作 FileHelper

目錄索引 簡介   在程式設計中,我們很多情況下,會用到對檔案的操作,在 上一個系列 中,我們有很多檔案基本操作的示例,在Core中有一些改變,主要是我們常用的Server.MapPath()不存在了,不知道後續的版本會不會有,在這裡,我們只能自己封裝方法去實現。今天,我們就對一些基本

【閱讀】《head first jquery》——定製函式提供定製效果(完善變臉小專案

window物件:這是javascript中最頂層的物件。它包括屬性、事件處理程式和方法,可以幫助檢測和響應瀏覽器事件。onFocus指出一個瀏覽器視窗何時啟用onBlur檢測一個視窗何時失去焦點定時

07.ThreeJs開發指南--粒子系統

第七章 粒子系統 function createParticles(){ var meaterial = new THREE.ParticleBasicMaterial(); for(var x = -5 ; x < 5 ; x +

《Head First Python》 Web開發之——資料建模 (電子書247-250)

資料建模 說人話:就是解決如何把原始的文字形式的資料檔案,轉換成按照預先定製類的一個個例項的方法,從而方便使用者呼叫、處理。 上圖來自《head First Python》P247 上圖來自《head First Python

:基於九鼎X210開發板移植2014.10版U-boot之初始化時鐘模組

重新燒錄,啟動,發現可以正常執行,然後卡死在DRAM之後,還打印出了一個O,這個O看起來挺熟悉的,開機時候列印的那個O?一下子就想到,我們雖然自己移植的那個重定位程式碼(拷貝BL2到記憶體中),但原版

阿里雲伺服器配置開發環境:Centos7.3安裝nginx以及環境配置

Nginx是什麼? Nginx是一款輕量級Web伺服器,也是一款反向代理伺服器 Nginx能幹什麼? 可直接支援Rails和PHP的程式 可作為HTTP反向代理伺服器 作為負載均衡伺服器 作

專案成本管理

將專案的範圍、成本、進度綜合考慮形成一套體系當中的一個分析模式。 保證專案在批准的預算內完成,並對成本進行規劃、估算、預算、融資、籌資、管理和控制的過程。 成本管理的重點是關注完成專案活動所需資源的成本; 同時也要考慮專案決策對專案產品、服務和成果的使用成

[讀書筆記] 專案成本管理(下)

第七章 專案成本管理(下)1.什麼是控制成本,它的輸入輸出是什麼更新專案狀態,以更新專案成本,管理成本基準的過程。它的作用是發現實際與計劃的差異,以便採取糾正措施,降低風險。應重點分析專案資金支出與相應完成的實際工作之間的關係。有效成本控制的關鍵在於,對經批准的成本基準及其變