@Cacheable不起作用,仍在调用缓存方法

问题描述

无论参数如何,我都尝试使用@Cacheable来缓存角色。但是@Cacheable不能正常工作,该方法将被调用两次。

CachingConfig:

@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);
            }
        };
    }
}

RoleMappingService:

@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

解决方法

我认为您的@Cacheable注释不起作用,因为您尚未为类指定接口。这是因为Spring为缓存创建了代理。 Spring在其文档中指定了以下内容。我还没有指定proxy-target-class,这意味着它将默认为false。如果为假,它将使用基于JDK接口的代理。但是在您的情况下,您可以进行分类,即RollMappingService没有实现接口。用方法getAllRoles创建接口RollMappingService并实现它,将解决您的问题。

控制为使用@Cacheable或@CacheEvict注释注释的类创建哪种类型的缓存代理。如果proxy-target-class属性设置为true,则将创建基于类的代理。如果proxy-target-class为false或省略了属性,则将创建基于标准JDK接口的代理。 (有关不同代理类型的详细检查,请参见第9.6节“代理机制”。)

还可以按照以下方式修改测试类以为RoleMappingService创建Spring bean,并将AdminClient的模拟注入其中。

 @Mock
private AdminClient mockedAdminClient;

@InjectMocks
@Autowired
private RoleMappingService roleMappingService

@Before
public void setUp() throws Exception {
    MockitoAnnotations.initMocks(this);
    ReflectionTestUtils.setField(roleMappingService,"adminClient",mockedAdminClient);
} 

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...