使用 Spring @Cacheable 和 @PostFilter
Using Spring @Cacheable with @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.
}
}
有谁知道为什么缓存似乎在过滤器运行后存储服务方法的结果,而不是像我一样在存储库级别存储完整的结果集'我期待它应该?或者有另一种方法来实现我想要的行为?
如果您知道我如何在服务中的同一方法上优雅地实现缓存和过滤,则加分。我只构建了多余的存储库,因为我认为拆分方法可以解决缓存问题。
原来Spring缓存的内容是可变的,@PostFilter
注释修改了返回的列表,它不会过滤成新的。
所以当@PostFilter 运行 在我上面的服务方法调用之后,它实际上是从缓存中存储的列表中删除项目,所以第二个请求只有 1 个结果开始,第三个请求将有零。
我的解决方案是将服务修改为 return new ArrayList<>(scopeRepo.getSegments());
,这样 PostFilter 就不会更改缓存列表。
(注意,这当然不是深度克隆,所以如果有人修改了服务上游的 Segment 模型,它也可能会更改缓存中的模型。所以这可能不是最好的解决方案,但是它适用于我的个人用例。)
我不敢相信Spring缓存是可变的...
我正在尝试在 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.
}
}
有谁知道为什么缓存似乎在过滤器运行后存储服务方法的结果,而不是像我一样在存储库级别存储完整的结果集'我期待它应该?或者有另一种方法来实现我想要的行为?
如果您知道我如何在服务中的同一方法上优雅地实现缓存和过滤,则加分。我只构建了多余的存储库,因为我认为拆分方法可以解决缓存问题。
原来Spring缓存的内容是可变的,@PostFilter
注释修改了返回的列表,它不会过滤成新的。
所以当@PostFilter 运行 在我上面的服务方法调用之后,它实际上是从缓存中存储的列表中删除项目,所以第二个请求只有 1 个结果开始,第三个请求将有零。
我的解决方案是将服务修改为 return new ArrayList<>(scopeRepo.getSegments());
,这样 PostFilter 就不会更改缓存列表。
(注意,这当然不是深度克隆,所以如果有人修改了服务上游的 Segment 模型,它也可能会更改缓存中的模型。所以这可能不是最好的解决方案,但是它适用于我的个人用例。)
我不敢相信Spring缓存是可变的...