Popular MVC框架方法结果查询缓存使用示例
简介
此项目用于演示如何使用popularmvc提供的缓存切面对方法调用进行拦截。调用方法前先检查是否存在接口结果缓存,如果不存在则执行方法业务,完成后再缓存业务结果,以备后续查询。其实spring已经提供了缓存相关注解,但是我在使用过程中总感觉用起来不方便,故此实现一套简单实用的缓存管理机制。主要有以下内容:
- 对查询结果及时性要求不高的方法调用,可以为方法设置较短时效的结果查询缓存
- 查询非用户级、变化频率低但使用很频繁的热点数据,可以设置较长时效的结果查询缓存
- 方法缓存更新
- 方法结果缓存失效拦截器
- 手动调用缓存管理器进行失效
- 手动管理业务缓存
- 使用自定义的缓存管理器,以自定义redis缓存管理器为例
- 服务缓存状态监控和管理
使用方法:
- 在需要缓存业务结果的方法上添加
@CacheMethodResult
注解即可实现接口结果的缓存;- 如果更新数据的方法需要及时的清除接口查询缓存,则在方法上添加
@CacheMethodResultEvict
注解即可;- 也可以通过
CacheManager
手动管理缓存;
项目示例
1 项目结构
-
项目结构
│ pom.xml │ README.md │ └─src ├─main │ ├─java │ │ └─com │ │ └─danyuanblog │ │ └─framework │ │ └─demo │ │ └─popularmvc │ │ │ StartDemoApplication.java │ │ │ │ │ └─controller │ │ │ TestManualManageCacheController.java │ │ │ TestMethodResultCacheController.java │ │ │ TestMethodResultParamCacheController.java │ │ │ │ │ ├─dto │ │ │ UserDto.java │ │ │ │ │ └─service │ │ UserService.java │ │ │ └─resources │ application.yml │ └─test └─java └─com └─danyuanblog └─framework └─popular └─mvc
-
引入模块依赖,在
pom.xml
添加
<dependency>
<groupId>com.danyuanblog.framework</groupId>
<artifactId>popular-web-mvc</artifactId>
<version>${popular-web-mvc.version}</version>
</dependency>
2 启用PopularMvc框架
/**
* Title StartDemoApplication.java
* Description
* @author danyuan
* @date Oct 31, 2020
* @version 1.0.0
* site: www.danyuanblog.com
*/
package com.danyuanblog.framework.demo.popularmvc;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import com.danyuanblog.framework.popularmvc.annotation.EnablePopularMvc;
@SpringBootApplication
@EnablePopularMvc
public class StartDemoApplication {
public static void main(String[] args) {
SpringApplication.run(StartDemoApplication.class, args);
}
}
3 配置信息
无
4 演示代码解析
4.1 接口缓存使用示例
用于缓存查询类接口的返回值,减轻服务器负载。
示例代码如下所示。
-
接口源码
TestThrowBusinessErrorController.java
/** * Title TestMethodResultParamCacheController.java * Description * @author danyuan * @date Jan 4, 2021 * @version 1.0.0 * site: www.danyuanblog.com */ package com.danyuanblog.framework.demo.popularmvc.controller; import java.util.HashMap; import java.util.Map; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import com.danyuanblog.framework.popularmvc.annotation.CacheMethodResult; @Api(tags = "测试方法结果查询参数化缓存相关接口列表") @RestController public class TestMethodResultParamCacheController { @GetMapping("getProductBySkuId") @ApiOperation(value="测试通过skuId查询商品信息", notes="测试通过skuId查询商品信息") @CacheMethodResult(id="skuId", expireSeconds=10) public Map<String,Object> getProductBySkuId(String skuId){ //Mock 商品信息 Map<String,Object> map = new HashMap<>(); map.put("skuId", skuId); map.put("productName", "大宗商品"+skuId); map.put("loadTime", System.currentTimeMillis()); return map; } @PostMapping("getProductBySkuInfo") @ApiOperation(value="测试通过sku信息查询商品信息", notes="测试通过sku信息查询商品信息") @CacheMethodResult(id="sku[id]", expireSeconds=30) public Map<String,Object> getProductBySkuInfo(@RequestBody Map<String,Object> sku){ //Mock 商品信息 Map<String,Object> map = new HashMap<>(); map.put("skuId", sku.get("id")); map.put("productName", "大宗商品"+sku.get("id")); map.put("loadTime", System.currentTimeMillis()); return map; } }
4.2 方法查询结果缓存示例
popularmvc还提供了更细粒度的缓存能力,可以为任意一个方法添加结果缓存能力。
-
业务服务
UserService.java
/** * Title UserService.java * Description * @author danyuan * @date Jan 4, 2021 * @version 1.0.0 * site: www.danyuanblog.com */ package com.danyuanblog.framework.demo.popularmvc.controller.service; import java.util.Date; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import com.danyuanblog.framework.demo.popularmvc.controller.dto.UserDto; import com.danyuanblog.framework.popularmvc.annotation.CacheMethodResult; import com.danyuanblog.framework.popularmvc.annotation.CacheMethodResultEvict; @Service @Slf4j public class UserService { private UserDto user = new UserDto() .setUsername("小明") .setAge(28) .setEmail("16721368817@163.com") .setPhone("16721368817") .setSex(true) .setDesc("阳光乐观快乐的小伙子!"); /** * 查询用户信息 * @author danyuan */ @CacheMethodResult(prefix="UserService:user", expireSeconds=600) public UserDto getUserInfo(){ log.info("query user info from database !"); return user.setLoadTime(new Date()); } /** * 更新用户信息 * @author danyuan */ @CacheMethodResultEvict(prefix="UserService:user", expireSeconds=600) public void updateUserInfo(UserDto user){ this.user = user; } }
-
接口
TestMethodResultCacheController.java
/** * Title TestMethodResultCacheController.java * Description * @author danyuan * @date Jan 4, 2021 * @version 1.0.0 * site: www.danyuanblog.com */ package com.danyuanblog.framework.demo.popularmvc.controller; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import com.danyuanblog.framework.demo.popularmvc.controller.dto.UserDto; import com.danyuanblog.framework.demo.popularmvc.controller.service.UserService; @Api(tags = "测试方法结果查询缓存相关接口列表") @RestController public class TestMethodResultCacheController { @Autowired private UserService userService; @GetMapping("getUserInfo") @ApiOperation(value="测试查询用户信息", notes="测试查询用户信息") public UserDto getUserInfo(){ return userService.getUserInfo(); } @PostMapping("updateUserInfo") @ApiOperation(value="测试更新用户信息", notes="测试更新用户信息") public void updateUserInfo(@RequestBody UserDto user){ userService.updateUserInfo(user); } }
-
用户信息
UserDto.java
/** * Title UserDto.java * Description * @author danyuan * @date Jan 4, 2021 * @version 1.0.0 * site: www.danyuanblog.com */ package com.danyuanblog.framework.demo.popularmvc.controller.dto; import java.io.Serializable; import java.util.Date; import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; import lombok.experimental.Accessors; @Data @Accessors(chain = true) public class UserDto implements Serializable{/** *serialVersionUID */ private static final long serialVersionUID = 1L; private String username; private String email; private String phone; private Boolean sex; private Integer age; private String desc; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date loadTime; }
4.3 使用缓存管理器手动管理接口缓存
当然popularmvc提供了使用注解这种业务无侵入式的缓存服务,同样也提供了手动管理缓存的能力。
-
接口示例
TestManualManageCacheController.java
/** * Title TestManualManageCacheController.java * Description * @author danyuan * @date Jan 4, 2021 * @version 1.0.0 * site: www.danyuanblog.com */ package com.danyuanblog.framework.demo.popularmvc.controller; import java.util.Map; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.danyuanblog.framework.popularmvc.CacheManager; import com.danyuanblog.framework.popularmvc.consts.CacheExpireMode; import com.danyuanblog.framework.popularmvc.utils.StringUtils; @Api(tags = "测试手动管理方法结果缓存相关接口列表") @RestController @RequestMapping("manual") public class TestManualManageCacheController { @Autowired private CacheManager cacheManager; @GetMapping("getCachedProductBySkuId") @ApiOperation(value="测试通过skuId手动查询商品信息缓存", notes="测试通过skuId手动查询商品信息缓存") public Object getCachedProductBySkuId(String skuId){ //手动查询接口缓存结果 Object result = cacheManager.get("TestMethodResultParamCacheController:getProductBySkuId"+":"+skuId, 10L, CacheExpireMode.EXPIRE_AFTER_REDA); if(StringUtils.isBlank(result)){ result = "未找到skuId:"+skuId+"的商品缓存信息"; } return result; } @PostMapping("evictProductBySkuInfoCache") @ApiOperation(value="测试手动删除方法结果缓存", notes="测试手动删除方法结果缓存") public void evictProductBySkuInfoCache(@RequestBody Map<String,Object> sku){ //手动删除方法结果缓存 cacheManager.remove("TestMethodResultParamCacheController:getProductBySkuInfo"+":"+sku.get("id"), 30L, CacheExpireMode.EXPIRE_AFTER_REDA); } }