簡體   English   中英

使用 Spring @Cacheable 和 @PostFilter

[英]Using Spring @Cacheable with @PostFilter

我正在嘗試在 Spring 中同時使用@Cacheable@PostFilter注釋。 期望的行為是應用程序將緩存完整的、未過濾的 Segments 列表(這是一個非常小且非常頻繁引用的列表,因此需要性能),但用戶將只能根據其角色訪問某些 Segments。

我開始在一個方法上同時使用@Cacheable@PostFilter ,但是當它不起作用時,我將它們分成兩個單獨的類,這樣我就可以在每個方法上都有一個注釋。 但是,無論我這樣做,它的行為似乎都是一樣的,也就是說,當用戶 A 第一次點擊服務時,他們得到了正確的過濾列表,然后當用戶 B 下次點擊服務時,他們沒有得到任何結果,因為緩存僅存儲用戶 A 的過濾結果,用戶 B 無權訪問其中任何一個。 (所以 PostFilter 仍在運行,但 Cache 似乎存儲的是過濾后的列表,而不是完整的列表。)

所以這里是相關的代碼:

配置:

@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.
    }
}

有誰知道為什么緩存似乎在過濾器運行存儲 Service 方法的結果,而不是像我期望的那樣在存儲庫級別存儲完整的結果集? 或者有另一種方法來實現我想要的行為?

如果您知道我如何在服務中的相同方法上優雅地實現緩存和過濾,則可以加分。 我只構建了多余的存儲庫,因為我認為拆分方法可以解決緩存問題。

原來 Spring 緩存的內容是可變的, @PostFilter注釋修改了返回的列表,它沒有過濾成新的列表。

因此,當@PostFilter 在我上面的 Service 方法調用之后運行時,它實際上是從存儲在緩存中的列表中刪除項目,因此第二個請求只有 1 個結果開始,而第三個請求將為零。

我的解決方案是修改服務以return new ArrayList<>(scopeRepo.getSegments()); 這樣 PostFilter 就不會更改緩存列表。

(注意,這當然不是深度克隆,所以如果有人在服務上游修改了段 model,它可能也會在緩存中的 model 中發生變化。所以這可能不是最好的解決方案,但它適用於我個人用例。)

我不敢相信 Spring 緩存是可變的......

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM