【发布时间】:2020-12-29 11:04:00
【问题描述】:
我正在尝试使用@Cacheable 来缓存角色,而不管参数如何。但是@Cacheable 并不能很好地工作,并且该方法会被调用两次。
缓存配置:
@Configuration
@EnableCaching
public class CachingConfig {
@Bean
public CacheManager cacheManager(@Value("${caching.ttl.period}") long period,
@Value("${caching.ttl.unit}") String unit) {
return new ConcurrentMapCacheManager() {
@Override
public Cache createConcurrentMapCache(String name) {
return new ConcurrentMapCache(name, CacheBuilder.newBuilder()
.expireAfterWrite(period, TimeUnit.valueOf(unit)).build().asMap(), true);
}
};
}
}
角色映射服务:
@Service
public class RoleMappingService {
private final AdminClient adminClient;
public RoleMappingService(AdminClient adminClient) {
this.adminClient = adminClient;
}
@Cacheable(value = "allRoles", key = "#root.method")
public List<Role> getAllRoles(String sessionToken) {
AdminSession adminSession = new AdminSession();
AdminSession.setSessionToken(sessionToken);
List<RoleGroup> allRoleGroups = this.adminClient.getAllRoleGroups(adminSession)
.orElse(Collections.emptyList());
List<Role> allRoles = allRoleGroups
.stream()
.map(RoleGroup::getRoles)
.flatMap(List::stream)
.collect(Collectors.toList());
return allRoles;
}
测试:
@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class RoleCachingTest {
private final JFixture fixture = new JFixture();
private AdminClient adminClient = mock(AdminClient.class);
@Test
public void allRolesShouldBeCached(){
RoleGroup mockRoleGroup = mock(RoleGroup.class);
Role mockRole = this.fixture.create(Role.class);
when(this.adminClient.getAllRoleGroups(any(AdminSession.class)))
.thenReturn(Optional.of(Arrays.asList(mockRoleGroup)));
when(mockRoleGroup.getRoles()).thenReturn(Arrays.asList(mockRole));
RoleMappingService sut = new RoleMappingService(adminClient);
List<Role> firstRes = sut.getAllRoles(
fixture.create(String.class));
List<Role> secondRes = sut.getAllRoles(
fixture.create(String.class));
assertEquals(firstRes.size(), secondRes.size());
assertEquals(firstRes.get(0).getId(), secondRes.get(0).getId());
assertEquals(firstRes.get(0).getRoleName(), secondRes.get(0).getRoleName());
// The getAllRoleGroups() should not be called on the second call
verify(this.adminClient, times(1)).getAllRoleGroups(any(AdminSession.class));
}
在这个测试中 adminClient.getAllRoleGroups() 总是会被调用两次,而我预计它只会被调用一次,因为 @Cacheable。
项目结构: project structure
【问题讨论】:
-
@Cachable 注解由 Spring 依赖注入处理。但是,您自己(通过模拟)构建您的测试目标,而不是从 Spring 请求它。例如,在测试类中添加:
@RunWith(SpringRunner.class)和@Autowire private AdminClient adminClient,以便 Spring 构建并注入您的类并使用缓存。 -
@cruftex 这是有道理的。但是按照这些步骤,我会得到 adminClient 的 NullPointer 异常。我还尝试了“@ExtendWith”(Junit5),并得到了 NoSuchBeanDefinitionException:没有可用的“proxy.AdminClient”类型的合格 bean:预计至少有 1 个有资格作为自动装配候选者的 bean。依赖注释:{@org.springframework.beans.factory.annotation.Autowired(required=true)} 我还没有找到解决方法,我的 AdminClient 使用 Component 进行了注释。
标签: java spring spring-boot caching spring-test