1.情景展示
在實際開發過程中,我們為了減少對數據庫的頻繁訪問,會把不易更改的數據放到緩存中,減少對數據庫的訪問,以此,既能減少數據庫的操作次數,也能節省響應時間;
但是,緩存同樣是一把雙刃劍,也會給我們帶來不便,比如:
對於后端開發人員來說,我們習慣於直接操作數據庫完成對數據庫的修改,而不是通過前端發起請求,這樣,就會導致一個問題:
不會觸發緩存更新操作,數據庫雖然已經改好,但是,緩存沒有得到更新;
或者是,有的項目根本就不涉及修改操作,只負責讀取數據,這就不存在更新緩存的情形;
而一旦數據庫發生修改,就無法保持數據同步,這就非常尷尬了,怎么辦?
2.情況分析
我們通常使用的緩存注解是@CacheEnable,該注解只在第一次請求時會從數據庫拿到數據,並放到緩存當中,后續請求將直接從緩存讀取;
鑒於spring啟用緩存,是通過使用注解@EnableCaching來完成的,我腦海里首先想到的是:
能不能在配置文件中進行動態設置,換言之就是:將是否啟動緩存的決定權遷移到配置文件中,比如:application.yml中。
事實證明,我想太多了,無法實現!
3.解決方案
我們知道,spring的緩存只存在於項目運行期間,它不像redis,即使你把項目停了,緩存依然會存在;
由此,就誕生了第一種解決方案:
方案一:重啟項目
重啟項目,所有的緩存將不復存在。
方案二:禁用緩存
我們只要把使用注解@EnableCaching刪掉或者是注銷掉,重啟項目就OK啦。
但是,這對於已經部署在Linux服務器下的項目而言,可操作性不強,我們還得替換class文件才行。
方案三:手動觸發清除緩存操作
可能,會有小伙伴說,設置緩存的失效時間不就完了,可以設置失效時間,但那是只有到指定時間后才會失效,不能滿足我們的及時性,總不能一直等到緩存失效,再干活吧?
我們可以創建控制器,向外提供接口,手動清除緩存,通過這種方式來間接實現緩存的清除操作,這樣,也不用重啟服務器,緩存也能接着用(重啟服務器的目的就是為了清除緩存)
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; import com.github.xiaoymin.knife4j.annotations.ApiSupport; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import lombok.extern.slf4j.Slf4j; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.stereotype.Controller; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import javax.annotation.Resource; import javax.validation.constraints.NotBlank;
/** * 清除緩存控制器 * @description: * @author: Marydon * @date: 2020-12-16 12:51 * @version: 1.0 * @email: marydon20170307@163.com */ @Api("CacheController") @ApiSupport(order = 266, author = "Marydon") @Slf4j // 必須作用在類上,不然只能校驗Null,不能校驗Empty @Validated @Controller public class CacheController { // 注入緩存管理器 @Resource private CacheManager cacheManager; @ApiOperation(value = "清除所有緩存", notes = "清空所有緩存") @ApiOperationSupport(author = "Marydon", order = 1) @GetMapping("/clearAll") public String clearAllCache() { cacheManager.getCacheNames().forEach(cacheName -> { cacheManager.getCache(cacheName).clear(); log.info("緩存".concat(cacheName).concat("清理成功!")); }); return "所有緩存清理完畢"; } @ApiOperation(value = "清除指定緩存", notes = "根據入參清除緩存") @ApiOperationSupport(author = "Marydon", order = 10) // knife4j會將headers的值識別為響應數據類型 @RequestMapping(value = "clear", method = {RequestMethod.GET, RequestMethod.POST}, headers = {"Accept=text/pain"}) // 入參需要使用@RequestParam,否則請求會被spring攔截,進不來(請求數據類型為空) public String clearCacheByName(@ApiParam(value = "緩存名稱", required = true, example = "aaCache") @RequestParam @NotBlank(message = "緩存名稱不能為空") String cacheName) { Cache cache = cacheManager.getCache(cacheName); if (null == cache) return cacheName + "並不存在"; cache.clear(); log.info(cacheName + "緩存清理完畢"); return cacheName + "緩存清理完畢"; } }
上面提供了兩種清除緩存的方法,一種是清除所有緩存,另一種是清除指定緩存。
4.測試
調用使用緩存的地方,一般是從前端發起請求;
初次請求,從數據庫拿數據;
清空控制台,現在來二次請求;
已經不再從數據庫獲取數據
清空控制台,仙子,我們來調用清空緩存操作:
緩存清理成功
此時,我們發起第三次觸發緩存的請求
從數據庫讀取,這就證明了咱們的代碼生效了,搞定。
5.bug修復
2020-12-17
上面的代碼存在一個問題:我們實際想返回字符串信息,但是實際上,springmvc會自動將字符串識別為路徑,這就非常尷尬,返回的就是404;
如何讓springmvc不把它當成路徑,而是作為數據返回呢?
這里提供三種實現方式:
最古老的方式:返回字符流
第二種:將@Controller替換成@RestController
第三種:在接口方法上添加@ResponseBody