有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java使用Spring@Cacheable和@PostFilter

我试图在Spring中同时使用@Cacheable@PostFilter注释。所需的行为是,应用程序将缓存完整的、未过滤的段列表(这是一个非常小且经常被引用的列表,因此性能是需要的),但用户只能根据其角色访问某些段

一开始,我在一个方法上同时使用了@Cacheable@PostFilter,但当这不起作用时,我将它们分成了两个独立的类,这样每个方法上都可以有一个注释。然而,无论我怎么做,它的行为似乎都是一样的,也就是说,当用户A第一次点击服务时,他们得到了正确的过滤列表,然后当用户B点击下一个服务时,他们没有得到任何结果,因为缓存只存储了用户A的过滤结果,而用户B无权访问其中任何一个。(因此PostFilter仍在运行,但缓存似乎存储的是已过滤列表,而不是完整列表。)

以下是相关代码:

配置:

@Configuration
@EnableCaching
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class BcmsSecurityAutoConfiguration { 

    @Bean
    public CacheManager cacheManager() {
        SimpleCacheManager cacheManager = new SimpleCacheManager();
        cacheManager.setCaches(Arrays.asList(
                new ConcurrentMapCache("bcmsSegRoles"),
                new ConcurrentMapCache("bcmsSegments")
        ));
        return cacheManager;
    }
}

服务:

@Service
public class ScopeService {

    private final ScopeRepository scopeRepository;

    public ScopeService(final ScopeRepository scopeRepository) {
        this.scopeRepository = scopeRepository;
    }

    // Filters the list of segments based on User Roles. User will have 1 role for each segment they have access to, and then it's just a simple equality check between the role and the Segment model.
    @PostFilter(value = "@bcmsSecurityService.canAccessSegment( principal, filterObject )")
    public List<BusinessSegment> getSegments() {
        List<BusinessSegment> segments = scopeRepository.getSegments();
        return segments; // Debugging shows 4 results for User A (post-filtered to 1), and 1 result for User B (post-filtered to 0)
    }
}

存储库:

@Repository
public class ScopeRepository {
    private final ScopeDao scopeDao; // This is a MyBatis interface.

    public ScopeRepository(final ScopeDao scopeDao) {
        this.scopeDao = scopeDao;
    }

    @Cacheable(value = "bcmsSegments")
    public List<BusinessSegment> getSegments() {
        List<BusinessSegment> segments = scopeDao.getSegments(); // Simple SELECT * FROM TABLE; Works as expected.
        return segments; // Shows 4 results for User A, breakpoint not hit for User B cache takes over.
    }
}

有人知道为什么缓存似乎在过滤器运行后存储服务方法的结果,而不是像我期望的那样在存储库级别存储完整的结果集吗?或者有其他方法来实现我想要的行为

如果您知道我如何在服务中的同一个方法上优雅地实现缓存和过滤,您将获得额外的积分。我只是构建了多余的存储库,因为我认为拆分这些方法可以解决缓存问题


共 (1) 个答案

  1. # 1 楼答案

    结果表明,Spring缓存的内容是可变的,@PostFilter注释修改了返回的列表,它不会过滤到新列表中

    因此,当@PostFilter在我上面的服务方法调用之后运行时,它实际上是在从存储在缓存中的列表中删除项目,因此第二个请求只有一个结果,第三个结果为零

    我的解决方案是将服务修改为return new ArrayList<>(scopeRepo.getSegments());,这样PostFilter就不会更改缓存列表

    (请注意,这当然不是深度克隆,因此如果有人在服务的上游修改了一个段模型,它可能也会在缓存中更改模型。因此,这可能不是最佳解决方案,但它适用于我的个人用例。)

    我不敢相信春天的储藏室是可变的