javascript
Spring测试上下文缓存+ AspectJ @Transactional + Ehcache的痛苦
您在使用AspectJ @Transactionals和Spring嗎? 您是否有多個SessionFactory,也許一個用于嵌入式數據庫進行單元測試,一個用于實際數據庫進行集成測試? 您是否遇到這些例外之一?
org.springframework.transaction.CannotCreateTransactionException:無法打開Hibernate Session進行事務處理。 嵌套的異常是org.hibernate.service.UnknownServiceException:請求了未知的服務
要么
net.sf.ehcache.Cache.isKeyInCache(Cache.java:3068)的org.hibernate.cache.ehcache.internal.regions.EhcacheDataRegion.contains(EhcacheDataRegion.java:223)上的java.lang.NullPointerException
然后,您遇到了一個問題,其中多個緩存的應用程序上下文相互踩在一起。 這篇博客文章將描述一些解決我們遇到的問題的策略。
背景
Spring的Text Context框架默認嘗試通過緩存容器來最小化spring容器必須啟動的次數。 如果您正在運行全部使用相同配置的多個測試,則只需為所有測試創建一次容器,而無需在每次測試之前創建容器。 如果您要進行1000次測試,并且容器需要10到15秒的啟動時間,那么構建/測試時間就會大為不同。
僅當每個人(您和您使用的所有庫)都避免使用靜態字段(全局狀態)時,這才有效,不幸的是,在某些情況下,這是很難/不可能避免的,即使spring違反了此規定! 導致我們出現問題的幾個地方:
- Spring AspectJ @事務支持
- EhCache緩存管理器
方面是設計上的單例。 Spring使用它來放置對BeanFactory和PlatformTransactionManager的引用。 如果您有多個帶有各自“自己的” AnnotationTransactionAspect的容器,則它們實際上共享AnnotationTransactionAspect,而最后啟動的容器是“贏家”,從而導致各種意外的難以調試的問題。
Ehcache在這里也很痛苦。 ehcache庫維護它在VM中創建的所有緩存管理器的靜態列表。 因此,如果要使用多個容器,它們將共享對同一緩存的引用。 Spring Test提供了一種機制來指示該測試已“污染”了容器并需要創建它。 這意味著在完成測試類后會破壞容器。 很好,但是如果您的容器具有其他容器共享的對象,則銷毀該共享對象會破壞其他容器。
解決方案
最簡單的解決方案是基本上完全禁用應用程序上下文緩存。 只需在每個測試上放置@DirtiesContext即可完成此操作,或者(最好)您可能應該使用超級類(“抽象測試夾具”)來組織您的測試,在這種情況下,只需在基類上添加@DirtiesContext。 不幸的是,您還失去了所有緩存優勢,并且構建時間將增加。
彈簧容器沒有“清理自身”的通用機制,因為跨容器共享狀態肯定是一種反模式。 他們自己這樣做(AnnotationTransactionAspect,EhCacheManagerFactoryBean.setShared(true)等),這表明他們可能應該添加一些支持。 如果要繼續緩存,則第1步是確保您的代碼中不使用任何“靜態字段”單例。 還要確保將要寫入的所有外部資源分開,以便多個容器可以共存于同一JVM中。
為了解決AspectJ問題,我發現的最佳解決方案是創建一個TestExecutionListener,以在測試執行之前“重置” AnnotationTransactionAspect以指向正確的bean工廠和PTM。 這種偵聽器的代碼在本要點中 。
然后,要使用偵聽器,請將@TestListeners放在基類測試夾具上,以便所有測試都使用新的偵聽器運行。 請注意,使用@TestListeners批注時,必須指定所有執行偵聽器,包括現有的Spring偵聽器。 要點有一個例子。
Ehcache的解決方法是不允許在容器之間共享CacheManager實例。 為此,您必須確保所有緩存管理器都有唯一的名稱。 實際上,這很容易配置。
@org.springframework.context.annotation.Configuration public class CacheBeans {private static final AtomicInteger cacheCounter = new AtomicInteger(0);@Beanpublic EhCacheManagerFactoryBean ecmfb() {EhCacheManagerFactoryBean ecmfb = new EhCacheManagerFactoryBean();// cannot share the cache managersecmfb.setShared(false);// if you are using ehcache.xml on the classpath then there's nothing more to do than just make it // a unique name. If you are using a different config file then use ecmfb.setConfigLocation()ecmfb.setCacheManagerName("ehCache-" + cacheCounter.incrementAndGet());return ecmfb;}// more @Bean defs }相關問題
以下是一些涉及此問題的Spring jira問題的鏈接:
https://jira.spring.io/browse/SPR-6121
https://jira.spring.io/browse/SPR-6353
翻譯自: https://www.javacodegeeks.com/2014/04/spring-test-context-caching-aspectj-transactional-ehcache-pain.html
總結
以上是生活随笔為你收集整理的Spring测试上下文缓存+ AspectJ @Transactional + Ehcache的痛苦的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 正确的醒肤方法 正确的醒肤方法介绍
- 下一篇: Spring Boot 1.0和Spri