SpringBoot + Caffeine实现本地缓存(内存缓存)

1. Caffeine简介

  Caffeine是一个基于Java8开发的提供了近乎最佳命中率的高性能的缓存库。借鉴Google GuavaConcurrentLinkedHashMap的经验,实现内存缓存。
  缓存和ConcurrentMap有点相似,但还是有所区别。最根本的区别是ConcurrentMap将会持有所有加入到缓存当中的元素,直到它们被从缓存当中手动移除。但是,Caffeine的缓存Cache通常会被配置成自动驱逐缓存中元素,以限制其内存占用。在某些场景下,自动加载缓存LoadingCache和异步自动加载缓存AsyncLoadingCache是非常实用的。
  Caffeine提供了灵活的构造器去创建一个拥有以下列特性的缓存:
基于过期时间的淘汰策略

  • 异步刷新策略
  • key值自动包装成弱引用
  • 元素值自动包装成弱引用或软引用
  • 通知淘汰元素策略
  • 向外部存储资源写入元素
  • 统计缓存访问信息

2. 相关博客

  SpringBoot集成阿里缓存框架Jetcache代替Spring Cache

3. 示例代码

  • 创建项目
  • 修改pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.c3stones</groupId>
	<artifactId>spring-boot-caffeine-demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>spring-boot-caffeine-demo</name>
	<description>Spring Boot Caffeine Demo</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.7.1</version>
		<relativePath />
	</parent>

	<dependencies>
		<dependency>
			<groupId>com.github.ben-manes.caffeine</groupId>
			<artifactId>caffeine</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-cache</artifactId>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>
  • 创建本地缓存配置类
/**
 * 本地缓存配置类
 *
 * @author CL
 */
@Configuration
public class NativeCacheConfig {

    private static final Logger log = LoggerFactory.getLogger(NativeCacheConfig.class);

    /**
     * 本地全局缓存
     *
     * @return
     */
    @Bean
    public Cache<String, Object> globalCache() {
        return Caffeine.newBuilder()
                // 最后一次写入后经过固定时间过期
                .expireAfterWrite(5, TimeUnit.MINUTES)
                // 初始的缓存空间大小
                .initialCapacity(10)
                // 缓存的最大条数
                .maximumSize(100)
                // 移除缓存监听
                .removalListener((k, v, cause) ->
                        log.warn("NativeCache RemovalListener key:{}, value:{}, cause:{}", k, v, cause))
                .build();
    }

    /**
     * 注入本地缓存管理器
     *
     * @return
     */
    @Bean
    public NativeCacheMagger nativeCacheManager() {
        return new NativeCacheMagger();
    }

    /**
     * 构建缓存
     *
     * @param cacheName    缓存名称
     * @param expireTime   过期时间
     * @param initCapacity 初始化大小
     * @param maxCapacity  最大大小
     * @return
     */
    public static org.springframework.cache.Cache buildCache(String cacheName,
                                                             Duration expireTime,
                                                             int initCapacity,
                                                             int maxCapacity) {
        notNull(cacheName, "缓存名称不能为空");
        notNull(expireTime, "过期时间不能为空");
        isTrue(initCapacity >= 0, "初始化大小为正整数");
        isTrue(maxCapacity >= 0, "最大大小为正整数");
        return new CaffeineCache(cacheName, Caffeine.newBuilder()
                // 最后一次写入后经过固定时间过期
                .expireAfterWrite(expireTime)
                // 初始的缓存空间大小
                .initialCapacity(initCapacity)
                // 缓存的最大条数
                .maximumSize(maxCapacity)
                // 设置值软引用
                .softValues()
                // 移除缓存监听
                .removalListener((k, v, cause) ->
                        log.warn("NativeCache RemovalListener key:{}, value:{}, cause:{}", k, v, cause))
                .build());
    }

    /**
     * 本地缓存管理器
     */
    public static class NativeCacheMagger extends AbstractCacheManager {

        /**
         * 缓存队列
         */
        private Collection<org.springframework.cache.Cache> caches = new CopyOnWriteArraySet<>();

        /**
         * 添加缓存到缓存管理器
         *
         * @param cache
         * @return
         */
        public NativeCacheMagger putCache(org.springframework.cache.Cache cache) {
            this.caches.add(cache);
            return this;
        }

        /**
         * 将缓存加载到缓存管理器
         */
        @Override
        public Collection<org.springframework.cache.Cache> loadCaches() {
            return this.caches;
        }
    }

}
  • 创建缓存客户端
/**
 * 缓存客户端
 *
 * @author CL
 */
@Component
public class CacheClient {

    private static final ObjectMapper MAPPER = new ObjectMapper();

    @Autowired
    private NativeCacheMagger nativeCacheMagger;
    @Autowired
    private com.github.benmanes.caffeine.cache.Cache<String, Object> globalCache;

    /**
     * 缓存数据
     *
     * @param key   缓存键
     * @param value 数据
     */
    public void putOfNative(String key, Object value) {
        if (!StringUtils.hasLength(key) || Objects.isNull(value)) {
            return;
        }
        globalCache.put(key, value);
    }

    /**
     * 移除缓存
     *
     * @param key 缓存键
     */
    public void removeOfNative(String key) {
        if (!StringUtils.hasLength(key)) {
            return;
        }
        globalCache.invalidate(key);
    }

    /**
     * 获取缓存
     *
     * @param key 缓存键
     * @return
     */
    public Object getByNative(String key) {
        if (!StringUtils.hasLength(key)) {
            return null;
        }
        return globalCache.getIfPresent(key);
    }

    /**
     * 获取缓存
     *
     * @param key   缓存键
     * @param clazz 数据类型
     * @param <T>
     * @return
     */
    public <T> T getByNative(String key, Class<T> clazz) {
        if (Objects.nonNull(clazz)) {
            Object obj = getByNative(key);
            try {
                if (Objects.nonNull(obj) && obj.getClass().isAssignableFrom(clazz)) {
                    return MAPPER.convertValue(obj, clazz);
                }
            } catch (Exception e) {
            }
        }
        return null;
    }

    /**
     * 获取全部缓存
     *
     * @return
     */
    public Map<String, Object> getAllByNative() {
        return globalCache.asMap();
    }


    /**
     * 缓存数据
     *
     * @param cacheName 缓存名称
     * @param key       缓存键
     * @param value     缓存值
     */
    public void putOfNative(String cacheName, String key, Object value) {
        if (Objects.isNull(value)) {
            return;
        }
        putOfNative(Optional.ofNullable(nativeCacheMagger.getCache(cacheName))
                .orElse(buildCache(cacheName, Duration.ofHours(1), 20, 100)), key, value);
    }

    /**
     * 缓存数据
     *
     * @param cache 缓存
     * @param key   缓存键
     * @param value 数据
     */
    public void putOfNative(org.springframework.cache.Cache cache, String key, Object value) {
        if (Objects.isNull(value)) {
            return;
        }
        cache.put(key, value);
        nativeCacheMagger.putCache(cache).initializeCaches();
    }

    /**
     * 移除缓存
     *
     * @param cacheName 缓存名称
     * @param key       缓存键
     */
    public void removeOfNative(String cacheName, String key) {
        if (!StringUtils.hasLength(cacheName)) {
            return;
        }
        removeOfNative(nativeCacheMagger.getCache(cacheName), key);
    }

    /**
     * 移除缓存
     *
     * @param cache 缓存
     * @param key   缓存键
     */
    public void removeOfNative(Cache cache, String key) {
        if (Objects.isNull(cache) || !StringUtils.hasLength(key)) {
            return;
        }
        cache.evict(key);
    }

    /**
     * 移除全部
     *
     * @param cache
     * @return
     */
    public boolean removeAllOfNative(Cache cache) {
        if (Objects.isNull(cache)) {
            return true;
        }
        return cache.invalidate();
    }

    /**
     * 移除全部
     *
     * @param cacheName
     * @return
     */
    public boolean removeAllOfNative(String cacheName) {
        if (!StringUtils.hasLength(cacheName)) {
            return true;
        }
        return removeAllOfNative(nativeCacheMagger.getCache(cacheName));
    }

    /**
     * 加载所有缓存
     *
     * @return
     */
    public Collection<Cache> loadCaches() {
        return nativeCacheMagger.loadCaches();
    }

    /**
     * 获取缓存
     *
     * @param cacheName 缓存名称
     * @param key       缓存键
     * @return
     */
    public Object getByNative(String cacheName, String key) {
        if (!StringUtils.hasLength(cacheName)) {
            return null;
        }
        return getByNative(nativeCacheMagger.getCache(cacheName), key);
    }

    /**
     * 获取缓存
     *
     * @param cache 缓存
     * @param key   缓存键
     * @return
     */
    public Object getByNative(Cache cache, String key) {
        if (Objects.isNull(cache) || !StringUtils.hasLength(key)) {
            return null;
        }
        return cache.get(key);
    }

    /**
     * 获取缓存
     *
     * @param cacheName 缓存名称
     * @param key       缓存键
     * @param clazz     数据类型
     * @param <T>
     * @return
     */
    public <T> T getByNative(String cacheName, String key, Class<T> clazz) {
        if (!StringUtils.hasLength(cacheName)) {
            return null;
        }
        return getByNative(nativeCacheMagger.getCache(cacheName), key, clazz);
    }

    /**
     * 获取缓存
     *
     * @param cache 缓存
     * @param key   缓存键
     * @param clazz 数据类型
     * @param <T>
     * @return
     */
    public <T> T getByNative(Cache cache, String key, Class<T> clazz) {
        if (Objects.isNull(cache) || Objects.isNull(key) || Objects.isNull(clazz)) {
            return null;
        }
        return cache.get(key, clazz);
    }

    /**
     * 获取全部缓存
     *
     * @param cache 缓存
     * @return
     */
    public List<Object> getAllByNative(Cache cache) {
        if (Objects.nonNull(cache) && cache instanceof CaffeineCache) {
            return ((CaffeineCache) cache).getNativeCache().asMap().values().stream().collect(Collectors.toList());
        }
        return Collections.emptyList();
    }

    /**
     * 获取全部缓存
     *
     * @param cache 缓存
     * @param clazz 数据类型
     * @param <T>
     * @return
     */
    public <T> List<T> getAllByNative(Cache cache, Class<T> clazz) {
        return getAllByNative(cache).stream().map(obj -> {
            if (obj.getClass().isAssignableFrom(clazz)) {
                return MAPPER.convertValue(obj, clazz);
            }
            return null;
        }).filter(Objects::nonNull).collect(Collectors.toList());
    }

    /**
     * 获取全部缓存
     *
     * @param cacheName 缓存名称
     * @return
     */
    public List<Object> getAllByNative(String cacheName) {
        return getAllByNative(nativeCacheMagger.getCache(cacheName));
    }

    /**
     * 获取全部缓存
     *
     * @param cacheName 缓存名称
     * @param clazz     数据类型
     * @return
     */
    public <T> List<T> getAllByNative(String cacheName, Class<T> clazz) {
        return getAllByNative(nativeCacheMagger.getCache(cacheName), clazz);
    }
    
}
  • 创建实体类
/**
 * 用户信息
 *
 * @author CL
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class User {

    private String id;
    private String username;
    private Integer age;

}
/**
 * 班级信息
 *
 * @author CL
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class Classes {
    private String code;
    private String name;
}
/**
 * 学生信息
 *
 * @author CL
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class Student {

    private String stuNo;
    private String name;
    private Classes classes;

}
  • 创建响应体
/**
 * 响应实体
 *
 * @author CL
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Response<T> {

    /**
     * 成功响应码
     */
    public static final int SUCCESS_CODE = 200;
    /**
     * 异常响应码
     */
    public static final int ERROR_CODE = 500;

    /**
     * 响应码
     */
    private int code;

    /**
     * 响应消息体
     */
    private String msg;

    /**
     * 响应数据
     */
    private T data;

    /**
     * 失败响应
     *
     * @param msg 响应消息体
     * @return
     */
    public static <T> Response<T> error(String msg) {
        return new Response<T>(ERROR_CODE, msg, null);
    }

    /**
     * 成功响应
     *
     * @return
     */
    public static <T> Response<T> success() {
        return new Response<T>(SUCCESS_CODE, null, null);
    }

    /**
     * 成功响应
     *
     * @param data 响应数据
     * @return
     */
    public static <T> Response<T> success(T data) {
        return new Response<T>(SUCCESS_CODE, null, data);
    }

    /**
     * 成功响应
     *
     * @param msg 响应消息体
     * @return
     */
    public static <T> Response<T> success(String msg) {
        return new Response<T>(SUCCESS_CODE, msg, null);
    }

    /**
     * 成功响应
     *
     * @param msg  响应消息体
     * @param data 响应数据
     * @return
     */
    public static <T> Response<T> success(String msg, T data) {
        return new Response<T>(SUCCESS_CODE, msg, data);
    }

}
  • 创建工具类
/**
 * Bean 工具类
 * 
 * @author CL
 */
public class BeanUtil extends BeanUtils {

    public static void copyPropertiesIgnoreNull(Object source, Object target) {
        BeanUtils.copyProperties(source, target, getNullProperties(source));
    }

    /**
     * 获取为Null的属性
     *
     * @param obj
     * @return
     */
    public static String[] getNullProperties(Object obj) {
        final BeanWrapper src = new BeanWrapperImpl(obj);
        return Arrays.stream(src.getPropertyDescriptors())
                .filter(pd -> Objects.isNull(src.getPropertyValue(pd.getName())))
                .map(PropertyDescriptor::getName)
                .distinct()
                .toArray(String[]::new);
    }
}
  • 创建Controller
/**
 * 用户Controller
 *
 * @author CL
 */
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private CacheClient cacheClient;

    /**
     * 新增用户
     *
     * @param user 用户
     * @return
     */
    @RequestMapping("/save")
    public Response save(User user) {
        cacheClient.putOfNative(user.getId(), user);
        return Response.success();
    }

    /**
     * 更新用户
     *
     * @param user 用户
     * @return
     */
    @RequestMapping("/update")
    public Response update(User user) {
        User oldUser = cacheClient.getByNative(user.getId(), User.class);
        if (Objects.nonNull(oldUser)) {
            BeanUtil.copyPropertiesIgnoreNull(user, oldUser);
            cacheClient.putOfNative(user.getId(), oldUser);
        }
        return Response.success();
    }

    /**
     * 删除用户
     *
     * @param id 用户ID
     * @return
     */
    @RequestMapping("/delete")
    public Response delete(String id) {
        cacheClient.removeOfNative(id);
        return Response.success();
    }

    /**
     * 查询用户
     *
     * @param id 用户ID
     * @return
     */
    @RequestMapping("/get")
    public Response get(String id) {
        User user = cacheClient.getByNative(id, User.class);
        return Response.success(user);
    }

}
/**
 * 学生Controller
 *
 * @author CL
 */
@RestController
@RequestMapping("/student")
public class StudentController {

    @Autowired
    private CacheClient cacheClient;

    /**
     * 新增学生
     *
     * @param student
     * @return
     */
    @RequestMapping("/save")
    public Response save(Student student) {
        cacheClient.putOfNative(student.getClasses().getCode(), student.getStuNo(), student);
        return Response.success();
    }

    /**
     * 更新学生
     *
     * @param student 学生
     * @return
     */
    @RequestMapping("/update")
    public Response update(Student student) {
        Student oldStudent = cacheClient.getByNative(student.getClasses().getCode(), student.getStuNo(), Student.class);
        if (Objects.nonNull(oldStudent)) {
            BeanUtil.copyPropertiesIgnoreNull(student, oldStudent);
            cacheClient.putOfNative(student.getClasses().getCode(), student.getStuNo(), oldStudent);
        }
        return Response.success();
    }

    /**
     * 删除学生
     *
     * @param student 学生
     * @return
     */
    @RequestMapping("/delete")
    public Response delete(Student student) {
        cacheClient.removeOfNative(student.getClasses().getCode(), student.getStuNo());
        return Response.success();
    }

    /**
     * 删除所有学生
     *
     * @return
     */
    @RequestMapping("/deleteAll")
    public Response delete() {
        cacheClient.loadCaches().forEach(cache -> cacheClient.removeAllOfNative(cache));
        return Response.success();
    }

    /**
     * 查询学生
     *
     * @param classesCode 班号
     * @param stuNo       学号
     * @return
     */
    @RequestMapping("/get")
    public Response get(String classesCode, String stuNo) {
        AtomicReference<Student> student = new AtomicReference<>();
        if (Objects.isNull(classesCode)) {
            cacheClient.loadCaches().forEach(cache -> {
                Student temp = cacheClient.getByNative(cache, stuNo, Student.class);
                if (Objects.nonNull(temp)) {
                    student.set(temp);
                    return;
                }
            });
        } else {
            student.set(cacheClient.getByNative(classesCode, stuNo, Student.class));
        }
        return Response.success(student.get());
    }

    /**
     * 查询班级学生
     *
     * @param classesCode
     * @return
     */
    @RequestMapping("list")
    public Response list(String classesCode) {
        List<Student> list = cacheClient.getAllByNative(classesCode, Student.class);
        return Response.success(list);
    }

    /**
     * 查询所有学生
     *
     * @return
     */
    @RequestMapping("/all")
    public Response all() {
        List<Student> list = new ArrayList<>();
        cacheClient.loadCaches().forEach(cache -> {
            list.addAll(cacheClient.getAllByNative(cache, Student.class));
        });
        return Response.success(list);
    }

}
  • 创建启动类
/**
 * 启动类
 * 
 * @author CL
 *
 */
@SpringBootApplication
public class Application {
	
	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}

}

4. 单元测试

  • 使用Eclipse进行单元测试时,如果出现No tests found with test runner Junit5,原因是配置的JUnit版本和使用的JUnit不一致。右键Debug As -> Debug Configurations... -> Test runner,选择JUnit4即可。


  • UserController接口测试
/**
 * UserController 单元测试
 *
 * @author CL
 */
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserControllerTest {

    @Autowired
    private UserController userController;

    private MockMvc mockMvc;

    @Before
    public void setup() {
        mockMvc = MockMvcBuilders.standaloneSetup(userController).build();
    }

    /**
     * 接口测试
     */
    @Test
    public void userTest() throws Exception {
        User user = new User()
                .setId("001")
                .setUsername("张三")
                .setAge(24);

        log.info("新增用户 ==>");
        save(user);

        log.info("查询用户 ==>");
        select(
                user.getId(),
                jsonPath("$.data.id").value(user.getId())
        );

        log.info("修改用户信息 ==>");
        User newUser = new User()
                .setId(user.getId())
                .setAge(26);
        update(newUser);

        log.info("查询用户 ==>");
        select(
                user.getId(),
                jsonPath("$.data.id").value(newUser.getId()),
                jsonPath("$.data.age").value(newUser.getAge())
        );

        log.info("删除用户 ==>");
        delete(user.getId());

        log.info("查询用户 ==>");
        select(
                user.getId(),
                jsonPath("$.data").isEmpty()
        );

        log.info("测试超时 ==>");
        save(user);

        log.info("查询用户 ==>");
        select(
                user.getId(),
                jsonPath("$.data").isNotEmpty()
        );

        log.info("等待五分钟(本地全局缓存默认过期时间) ==>");
        Thread.sleep(5 * 60 * 1000 + 10);

        log.info("查询用户 ==>");
        select(
                user.getId(),
                jsonPath("$.data").isEmpty()
        );
    }

    /**
     * 新增用户
     *
     * @param user
     */
    private void save(User user) throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.post("/user/save")
                        .param("id", user.getId())
                        .param("username", user.getUsername())
                        .param("age", user.getAge().toString()))
                .andExpect(status().isOk())                                     // 增加断言
                .andExpect(jsonPath("$.code").value(SUCCESS_CODE))    // 增加断言
                .andDo(print())                                                 // 打印结果
                .andReturn();
    }

    /**
     * 查询用户
     *
     * @param id
     * @param matcher
     */
    private void select(String id, ResultMatcher... matcher) throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("/user/get")
                        .param("id", id))
                .andExpect(status().isOk())                                     // 增加断言
                .andExpect(jsonPath("$.code").value(SUCCESS_CODE))    // 增加断言
                .andExpectAll(matcher)                                          // 增加断言
                .andDo(print())                                                 // 打印结果
                .andReturn();
    }

    /**
     * 更新用户
     *
     * @param newUser
     */
    private void update(User newUser) throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("/user/update")
                        .param("id", newUser.getId())
                        .param("username", newUser.getUsername())
                        .param("age", newUser.getAge().toString()))
                .andExpect(status().isOk())                                     // 增加断言
                .andExpect(jsonPath("$.code").value(SUCCESS_CODE))    // 增加断言
                .andDo(print())                                                 // 打印结果
                .andReturn();
    }

    /**
     * 删除用户
     *
     * @param id
     */
    private void delete(String id) throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("/user/delete")
                        .param("id", id))
                .andExpect(status().isOk())                                     // 增加断言
                .andExpect(jsonPath("$.code").value(SUCCESS_CODE))    // 增加断言
                .andDo(print())                                                 // 打印结果
                .andReturn();
    }
}

  测试结果:

2022-09-07 22:25:03.755  INFO 23060 --- [           main] c.c.controller.UserControllerTest        : 新增用户 ==>

MockHttpServletRequest:
      HTTP Method = POST
      Request URI = /user/save
       Parameters = {id=[001], username=[张三], age=[24]}
          Headers = []
             Body = <no character encoding set>
    Session Attrs = {}

Handler:
             Type = com.c3stones.controller.UserController
           Method = com.c3stones.controller.UserController#save(User)

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = [Content-Type:"application/json"]
     Content type = application/json
             Body = {"code":200,"msg":null,"data":null}
    Forwarded URL = null
   Redirected URL = null
          Cookies = []
2022-09-07 22:25:04.178  INFO 23060 --- [           main] c.c.controller.UserControllerTest        : 查询用户 ==>

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /user/get
       Parameters = {id=[001]}
          Headers = []
             Body = <no character encoding set>
    Session Attrs = {}

Handler:
             Type = com.c3stones.controller.UserController
           Method = com.c3stones.controller.UserController#get(String)

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = [Content-Type:"application/json"]
     Content type = application/json
             Body = {"code":200,"msg":null,"data":{"id":"001","username":"张三","age":24}}
    Forwarded URL = null
   Redirected URL = null
          Cookies = []
2022-09-07 22:25:04.245  INFO 23060 --- [           main] c.c.controller.UserControllerTest        : 修改用户信息 ==>

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /user/update
       Parameters = {id=[001], username=[null], age=[26]}
          Headers = []
             Body = <no character encoding set>
    Session Attrs = {}

Handler:
             Type = com.c3stones.controller.UserController
           Method = com.c3stones.controller.UserController#update(User)

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
2022-09-07 22:25:04.255  WARN 23060 --- [onPool-worker-1] com.c3stones.config.NativeCacheConfig    : NativeCache RemovalListener key:001, value:User(id=001, username=张三, age=24), cause:REPLACED
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = [Content-Type:"application/json"]
     Content type = application/json
             Body = {"code":200,"msg":null,"data":null}
    Forwarded URL = null
   Redirected URL = null
          Cookies = []
2022-09-07 22:25:04.259  INFO 23060 --- [           main] c.c.controller.UserControllerTest        : 查询用户 ==>

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /user/get
       Parameters = {id=[001]}
          Headers = []
             Body = <no character encoding set>
    Session Attrs = {}

Handler:
             Type = com.c3stones.controller.UserController
           Method = com.c3stones.controller.UserController#get(String)

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = [Content-Type:"application/json"]
     Content type = application/json
             Body = {"code":200,"msg":null,"data":{"id":"001","username":"张三","age":26}}
    Forwarded URL = null
   Redirected URL = null
          Cookies = []
2022-09-07 22:25:04.263  INFO 23060 --- [           main] c.c.controller.UserControllerTest        : 删除用户 ==>
2022-09-07 22:25:04.266  WARN 23060 --- [onPool-worker-1] com.c3stones.config.NativeCacheConfig    : NativeCache RemovalListener key:001, value:User(id=001, username=张三, age=26), cause:EXPLICIT

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /user/delete
       Parameters = {id=[001]}
          Headers = []
             Body = <no character encoding set>
    Session Attrs = {}

Handler:
             Type = com.c3stones.controller.UserController
           Method = com.c3stones.controller.UserController#delete(String)

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = [Content-Type:"application/json"]
     Content type = application/json
             Body = {"code":200,"msg":null,"data":null}
    Forwarded URL = null
   Redirected URL = null
          Cookies = []
2022-09-07 22:25:04.270  INFO 23060 --- [           main] c.c.controller.UserControllerTest        : 查询用户 ==>

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /user/get
       Parameters = {id=[001]}
          Headers = []
             Body = <no character encoding set>
    Session Attrs = {}

Handler:
             Type = com.c3stones.controller.UserController
           Method = com.c3stones.controller.UserController#get(String)

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = [Content-Type:"application/json"]
     Content type = application/json
             Body = {"code":200,"msg":null,"data":null}
    Forwarded URL = null
   Redirected URL = null
          Cookies = []
2022-09-07 22:25:04.275  INFO 23060 --- [           main] c.c.controller.UserControllerTest        : 测试超时 ==>

MockHttpServletRequest:
      HTTP Method = POST
      Request URI = /user/save
       Parameters = {id=[001], username=[张三], age=[24]}
          Headers = []
             Body = <no character encoding set>
    Session Attrs = {}

Handler:
             Type = com.c3stones.controller.UserController
           Method = com.c3stones.controller.UserController#save(User)

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = [Content-Type:"application/json"]
     Content type = application/json
             Body = {"code":200,"msg":null,"data":null}
    Forwarded URL = null
   Redirected URL = null
          Cookies = []
2022-09-07 22:25:04.280  INFO 23060 --- [           main] c.c.controller.UserControllerTest        : 查询用户 ==>

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /user/get
       Parameters = {id=[001]}
          Headers = []
             Body = <no character encoding set>
    Session Attrs = {}

Handler:
             Type = com.c3stones.controller.UserController
           Method = com.c3stones.controller.UserController#get(String)

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = [Content-Type:"application/json"]
     Content type = application/json
             Body = {"code":200,"msg":null,"data":{"id":"001","username":"张三","age":24}}
    Forwarded URL = null
   Redirected URL = null
          Cookies = []
2022-09-07 22:25:04.285  INFO 23060 --- [           main] c.c.controller.UserControllerTest        : 等待五分钟(本地全局缓存默认过期时间) ==>
2022-09-07 22:30:04.294  INFO 23060 --- [           main] c.c.controller.UserControllerTest        : 查询用户 ==>

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /user/get
       Parameters = {id=[001]}
          Headers = []
             Body = <no character encoding set>
    Session Attrs = {}

Handler:
             Type = com.c3stones.controller.UserController
           Method = com.c3stones.controller.UserController#get(String)

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = [Content-Type:"application/json"]
     Content type = application/json
             Body = {"code":200,"msg":null,"data":null}
    Forwarded URL = null
   Redirected URL = null
          Cookies = []
2022-09-07 22:30:04.298  WARN 23060 --- [onPool-worker-4] com.c3stones.config.NativeCacheConfig    : NativeCache RemovalListener key:001, value:User(id=001, username=张三, age=24), cause:EXPIRED
  • StudentController接口测试
/**
 * StudentController 单元测试
 *
 * @author CL
 */
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class StudentControllerTest {

    @Autowired
    private StudentController studentController;

    private MockMvc mockMvc;

    @Before
    public void setup() {
        mockMvc = MockMvcBuilders.standaloneSetup(studentController).build();
    }

    /**
     * 接口测试
     */
    @Test
    public void studentTest() throws Exception {
        Classes classes = new Classes()
                .setCode("A")
                .setName("一班");

        Student student1 = new Student()
                .setStuNo("A01")
                .setName("小明")
                .setClasses(classes);

        log.info("新增学生1 ==>");
        save(student1);

        Student student2 = new Student()
                .setStuNo("A02")
                .setName("小李")
                .setClasses(classes);

        log.info("新增学生2 ==>");
        save(student2);

        log.info("查询学生 ==>");
        select(classes.getCode(), student1.getStuNo(),
                jsonPath("$.data.stuNo").value(student1.getStuNo()),
                jsonPath("$.data.classes.code").value(student1.getClasses().getCode()));

        select(classes.getCode(), student2.getStuNo(),
                jsonPath("$.data.stuNo").value(student2.getStuNo()),
                jsonPath("$.data.classes.code").value(student2.getClasses().getCode()));

        log.info("查询班级 ==>");
        selectList(classes.getCode(),
                jsonPath("$.data[0].classes.code").value(classes.getCode()));

        log.info("查询所有学生 ==>");
        selectAll(jsonPath("$.data", hasSize(2)));

        log.info("更新学生 ==>");
        Student newStudent1 = new Student()
                .setStuNo("A01")
                .setName("小鹏")
                .setClasses(classes);
        update(newStudent1);

        log.info("查询学生 ==>");
        select(null, newStudent1.getStuNo(),
                jsonPath("$.data.stuNo").value(newStudent1.getStuNo()),
                jsonPath("$.data.classes.code").value(newStudent1.getClasses().getCode()),
                jsonPath("$.data.name").value(newStudent1.getName()));

        log.info("删除学生 ==>");
        delete(student2.getClasses().getCode(), student2.getStuNo());

        log.info("查询所有学生 ==>");
        selectAll(jsonPath("$.data", hasSize(1)));

        log.info("删除所有学生 ==>");
        deleteAll();

        log.info("查询所有学生 ==>");
        selectAll(jsonPath("$.data", hasSize(0)));
    }

    /**
     * 新增学生
     *
     * @param student
     */
    private void save(Student student) throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.post("/student/save")
                        .param("stuNo", student.getStuNo())
                        .param("name", student.getName())
                        .param("classes.code", student.getClasses().getCode())
                        .param("classes.name", student.getClasses().getName()))
                .andExpect(status().isOk())                                     // 增加断言
                .andExpect(jsonPath("$.code").value(SUCCESS_CODE))    // 增加断言
                .andDo(print())                                                 // 打印结果
                .andReturn();
    }

    /**
     * 查询学生
     *
     * @param classesCode
     * @param stuNo
     * @param matcher
     */
    private void select(String classesCode, String stuNo, ResultMatcher... matcher) throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("/student/get")
                        .param("classesCode", classesCode)
                        .param("stuNo", stuNo))
                .andExpect(status().isOk())                                     // 增加断言
                .andExpect(jsonPath("$.code").value(SUCCESS_CODE))    // 增加断言
                .andExpectAll(matcher)                                          // 增加断言
                .andDo(print())                                                 // 打印结果
                .andReturn();
    }

    /**
     * 查询班级学生
     *
     * @param classesCode
     * @param matcher
     */
    private void selectList(String classesCode, ResultMatcher... matcher) throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("/student/list")
                        .param("classesCode", classesCode))
                .andExpect(status().isOk())                                     // 增加断言
                .andExpect(jsonPath("$.code").value(SUCCESS_CODE))    // 增加断言
                .andExpectAll(matcher)                                          // 增加断言
                .andDo(print())                                                 // 打印结果
                .andReturn();
    }

    /**
     * 查询所有学生
     *
     * @param matcher
     */
    private void selectAll(ResultMatcher... matcher) throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("/student/all"))
                .andExpect(status().isOk())                                     // 增加断言
                .andExpect(jsonPath("$.code").value(SUCCESS_CODE))    // 增加断言
                .andExpectAll(matcher)                                          // 增加断言
                .andDo(print())                                                 // 打印结果
                .andReturn();
    }


    /**
     * 更新学生
     *
     * @param newStudent
     */
    private void update(Student newStudent) throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("/student/update")
                        .param("stuNo", newStudent.getStuNo())
                        .param("name", newStudent.getName())
                        .param("classes.code", newStudent.getClasses().getCode())
                        .param("classes.name", newStudent.getClasses().getName()))
                .andExpect(status().isOk())                                     // 增加断言
                .andExpect(jsonPath("$.code").value(SUCCESS_CODE))    // 增加断言
                .andDo(print())                                                 // 打印结果
                .andReturn();
    }

    /**
     * 删除学生
     *
     * @param classesCode
     * @param stuNo
     */
    private void delete(String classesCode, String stuNo) throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("/student/delete")
                        .param("classes.code", classesCode)
                        .param("stuNo", stuNo))
                .andExpect(status().isOk())                                     // 增加断言
                .andExpect(jsonPath("$.code").value(SUCCESS_CODE))    // 增加断言
                .andDo(print())                                                 // 打印结果
                .andReturn();
    }

    /**
     * 删除所有学生
     */
    private void deleteAll() throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("/student/deleteAll"))
                .andExpect(status().isOk())                                     // 增加断言
                .andExpect(jsonPath("$.code").value(SUCCESS_CODE))    // 增加断言
                .andDo(print())                                                 // 打印结果
                .andReturn();
    }
}

  测试结果:

2022-09-07 20:31:25.896  INFO 22204 --- [           main] c.c.controller.StudentControllerTest     : 新增学生1 ==>

MockHttpServletRequest:
      HTTP Method = POST
      Request URI = /student/save
       Parameters = {stuNo=[A01], name=[小明], classes.code=[A], classes.name=[一班]}
          Headers = []
             Body = <no character encoding set>
    Session Attrs = {}

Handler:
             Type = com.c3stones.controller.StudentController
           Method = com.c3stones.controller.StudentController#save(Student)

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = [Content-Type:"application/json"]
     Content type = application/json
             Body = {"code":200,"msg":null,"data":null}
    Forwarded URL = null
   Redirected URL = null
          Cookies = []
2022-09-07 20:31:26.447  INFO 22204 --- [           main] c.c.controller.StudentControllerTest     : 新增学生2 ==>

MockHttpServletRequest:
      HTTP Method = POST
      Request URI = /student/save
       Parameters = {stuNo=[A02], name=[小李], classes.code=[A], classes.name=[一班]}
          Headers = []
             Body = <no character encoding set>
    Session Attrs = {}

Handler:
             Type = com.c3stones.controller.StudentController
           Method = com.c3stones.controller.StudentController#save(Student)

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = [Content-Type:"application/json"]
     Content type = application/json
             Body = {"code":200,"msg":null,"data":null}
    Forwarded URL = null
   Redirected URL = null
          Cookies = []
2022-09-07 20:31:26.454  INFO 22204 --- [           main] c.c.controller.StudentControllerTest     : 查询学生 ==>

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /student/get
       Parameters = {classesCode=[A], stuNo=[A01]}
          Headers = []
             Body = <no character encoding set>
    Session Attrs = {}

Handler:
             Type = com.c3stones.controller.StudentController
           Method = com.c3stones.controller.StudentController#get(String, String)

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = [Content-Type:"application/json"]
     Content type = application/json
             Body = {"code":200,"msg":null,"data":{"stuNo":"A01","name":"小明","classes":{"code":"A","name":"一班"}}}
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /student/get
       Parameters = {classesCode=[A], stuNo=[A02]}
          Headers = []
             Body = <no character encoding set>
    Session Attrs = {}

Handler:
             Type = com.c3stones.controller.StudentController
           Method = com.c3stones.controller.StudentController#get(String, String)

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = [Content-Type:"application/json"]
     Content type = application/json
             Body = {"code":200,"msg":null,"data":{"stuNo":"A02","name":"小李","classes":{"code":"A","name":"一班"}}}
    Forwarded URL = null
   Redirected URL = null
          Cookies = []
2022-09-07 20:31:26.479  INFO 22204 --- [           main] c.c.controller.StudentControllerTest     : 查询班级 ==>

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /student/list
       Parameters = {classesCode=[A]}
          Headers = []
             Body = <no character encoding set>
    Session Attrs = {}

Handler:
             Type = com.c3stones.controller.StudentController
           Method = com.c3stones.controller.StudentController#list(String)

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = [Content-Type:"application/json"]
     Content type = application/json
             Body = {"code":200,"msg":null,"data":[{"stuNo":"A01","name":"小明","classes":{"code":"A","name":"一班"}},{"stuNo":"A02","name":"小李","classes":{"code":"A","name":"一班"}}]}
    Forwarded URL = null
   Redirected URL = null
          Cookies = []
2022-09-07 20:31:26.560  INFO 22204 --- [           main] c.c.controller.StudentControllerTest     : 查询所有学生 ==>

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /student/all
       Parameters = {}
          Headers = []
             Body = <no character encoding set>
    Session Attrs = {}

Handler:
             Type = com.c3stones.controller.StudentController
           Method = com.c3stones.controller.StudentController#all()

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = [Content-Type:"application/json"]
     Content type = application/json
             Body = {"code":200,"msg":null,"data":[{"stuNo":"A01","name":"小明","classes":{"code":"A","name":"一班"}},{"stuNo":"A02","name":"小李","classes":{"code":"A","name":"一班"}}]}
    Forwarded URL = null
   Redirected URL = null
          Cookies = []
2022-09-07 20:31:26.580  INFO 22204 --- [           main] c.c.controller.StudentControllerTest     : 更新学生 ==>

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /student/update
       Parameters = {stuNo=[A01], name=[小鹏], classes.code=[A], classes.name=[一班]}
          Headers = []
             Body = <no character encoding set>
    Session Attrs = {}

Handler:
             Type = com.c3stones.controller.StudentController
           Method = com.c3stones.controller.StudentController#update(Student)

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = [Content-Type:"application/json"]
     Content type = application/json
             Body = {"code":200,"msg":null,"data":null}
    Forwarded URL = null
   Redirected URL = null
          Cookies = []
2022-09-07 20:31:26.595  INFO 22204 --- [           main] c.c.controller.StudentControllerTest     : 查询学生 ==>

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /student/get
       Parameters = {classesCode=[null], stuNo=[A01]}
          Headers = []
             Body = <no character encoding set>
    Session Attrs = {}

Handler:
             Type = com.c3stones.controller.StudentController
           Method = com.c3stones.controller.StudentController#get(String, String)

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = [Content-Type:"application/json"]
     Content type = application/json
             Body = {"code":200,"msg":null,"data":{"stuNo":"A01","name":"小鹏","classes":{"code":"A","name":"一班"}}}
    Forwarded URL = null
   Redirected URL = null
          Cookies = []
2022-09-07 20:31:26.602  INFO 22204 --- [           main] c.c.controller.StudentControllerTest     : 删除学生 ==>

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /student/delete
       Parameters = {classes.code=[A], stuNo=[A02]}
          Headers = []
             Body = <no character encoding set>
    Session Attrs = {}

Handler:
             Type = com.c3stones.controller.StudentController
           Method = com.c3stones.controller.StudentController#delete(Student)

Async:
    Async started = false
     Async result = null
2022-09-07 20:31:26.611  WARN 22204 --- [onPool-worker-1] com.c3stones.config.NativeCacheConfig    : NativeCache RemovalListener key:A02, value:Student(stuNo=A02, name=小李, classes=Classes(code=A, name=一班)), cause:EXPLICIT

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = [Content-Type:"application/json"]
     Content type = application/json
             Body = {"code":200,"msg":null,"data":null}
    Forwarded URL = null
   Redirected URL = null
          Cookies = []
2022-09-07 20:31:26.616  INFO 22204 --- [           main] c.c.controller.StudentControllerTest     : 查询所有学生 ==>

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /student/all
       Parameters = {}
          Headers = []
             Body = <no character encoding set>
    Session Attrs = {}

Handler:
             Type = com.c3stones.controller.StudentController
           Method = com.c3stones.controller.StudentController#all()

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = [Content-Type:"application/json"]
     Content type = application/json
             Body = {"code":200,"msg":null,"data":[{"stuNo":"A01","name":"小鹏","classes":{"code":"A","name":"一班"}}]}
    Forwarded URL = null
   Redirected URL = null
          Cookies = []
2022-09-07 20:31:26.623  INFO 22204 --- [           main] c.c.controller.StudentControllerTest     : 删除所有学生 ==>
2022-09-07 20:31:26.632  WARN 22204 --- [onPool-worker-1] com.c3stones.config.NativeCacheConfig    : NativeCache RemovalListener key:A01, value:Student(stuNo=A01, name=小鹏, classes=Classes(code=A, name=一班)), cause:EXPLICIT

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /student/deleteAll
       Parameters = {}
          Headers = []
             Body = <no character encoding set>
    Session Attrs = {}

Handler:
             Type = com.c3stones.controller.StudentController
           Method = com.c3stones.controller.StudentController#delete()

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = [Content-Type:"application/json"]
     Content type = application/json
             Body = {"code":200,"msg":null,"data":null}
    Forwarded URL = null
   Redirected URL = null
          Cookies = []
2022-09-07 20:31:26.638  INFO 22204 --- [           main] c.c.controller.StudentControllerTest     : 查询所有学生 ==>

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /student/all
       Parameters = {}
          Headers = []
             Body = <no character encoding set>
    Session Attrs = {}

Handler:
             Type = com.c3stones.controller.StudentController
           Method = com.c3stones.controller.StudentController#all()

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = [Content-Type:"application/json"]
     Content type = application/json
             Body = {"code":200,"msg":null,"data":[]}
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

5. 项目地址

  spring-boot-caffeine-demo

posted @ 2022-09-07 22:35  C3Stones  阅读(1409)  评论(0编辑  收藏  举报