但是,Spring的家伙現在已經基于幾個注釋設計了一個易于使用的緩存系統:@Cacheable和@CacheEvict。
@Cacheable批注的想法是,您可以使用它來標記將存儲在緩存中的方法返回值。 @Cacheable批注可以應用于方法級別或類型級別。 在方法級別應用時,將對帶注釋的方法的返回值進行緩存。 在類型級別應用時,將緩存每個方法的返回值。
下面的代碼演示了如何在類型級別應用@Cacheable:
@Cacheable(value = "employee")
public class EmployeeDAO {public Person findEmployee(String firstName, String surname, int age) {return new Person(firstName, surname, age);}public Person findAnotherEmployee(String firstName, String surname, int age) {return new Person(firstName, surname, age);}
}
Cacheable批注采用三個參數:value(必填)以及key和condition。 其中第一個(值)用于指定存儲a方法的返回值的一個或多個緩存的名稱。
@Cacheable(value = "employee")public Person findEmployee(String firstName, String surname, int age) {return new Person(firstName, surname, age);}
上面的代碼確保新的Person對象存儲在“員工”緩存中。
緩存中存儲的任何數據都需要一個密鑰來快速檢索。 默認情況下,Spring使用注釋方法的簽名創建緩存密鑰,如上面的代碼所示。 您可以使用@Cacheable的第二個參數:key覆蓋它。 要定義自定義鍵,請使用SpEL表達式。
@Cacheable(value = "employee", key = "#surname")public Person findEmployeeBySurname(String firstName, String surname, int age) {return new Person(firstName, surname, age);}
在findEmployeeBySurname(…)代碼中,“#surname”字符串是SpEL表達式,表示“使用findEmployeeBySurname(…)方法的surname參數創建并創建密鑰”。
最后的@Cacheable參數是可選的條件參數。 同樣,這引用了SpEL表達式,但是這次它指定了一個條件,該條件用于確定是否將方法的返回值添加到緩存中。
@Cacheable(value = "employee", condition = "#age < 25")public Person findEmployeeByAge(String firstName, String surname, int age) {return new Person(firstName, surname, age);}
在上面的代碼中,我應用了可笑的業務規則,即如果雇員不到25歲,則僅緩存Person對象。
快速演示了如何應用某些緩存后,接下來要做的就是看一看所有含義。
@Testpublic void testCache() {Person employee1 = instance.findEmployee("John", "Smith", 22);Person employee2 = instance.findEmployee("John", "Smith", 22);assertEquals(employee1, employee2);}
上面的測試演示了最簡單的緩存。 第一次調用findEmployee(...),結果尚未緩存,因此將調用我的代碼,Spring將其返回值存儲在緩存中。 在對findEmployee(...)的第二次調用中,未調用我的代碼,Spring返回了緩存的值; 因此,局部變量employee1引用了與employee2相同的對象引用,這意味著以下情況成立:
assertEquals(employee1, employee2);
但是,事情并非總是那么清晰。 記住,在findEmployeeBySurname中,我已經修改了緩存密鑰,以便使用surname參數創建密鑰,而在創建自己的密鑰算法時要注意的事情是確保任何密鑰都引用唯一的對象。
@Testpublic void testCacheOnSurnameAsKey() {Person employee1 = instance.findEmployeeBySurname("John", "Smith", 22);Person employee2 = instance.findEmployeeBySurname("Jack", "Smith", 55);assertEquals(employee1, employee2);}
上面的代碼找到了兩個Person實例,這些實例顯然指向不同的員工; 但是,由于我只緩存姓氏,因此Spring將返回對我第一次調用findEmployeeBySurname(…)時創建的對象的引用。 對于Spring來說這不是問題,但是由于我的緩存鍵定義不佳。
當引用由將條件應用于@Cachable注釋的方法創建的對象時,必須采取類似的措施。 在我的示例代碼中,我應用了僅緩存員工年齡在25歲以下的Person實例的任意條件。
@Testpublic void testCacheWithAgeAsCondition() {Person employee1 = instance.findEmployeeByAge("John", "Smith", 22);Person employee2 = instance.findEmployeeByAge("John", "Smith", 22);assertEquals(employee1, employee2);}
在上面的代碼中,對employee1和employee2的引用是相等的,因為在第二次調用findEmployeeByAge(...)時,Spring返回其緩存的實例。
@Testpublic void testCacheWithAgeAsCondition2() {Person employee1 = instance.findEmployeeByAge("John", "Smith", 30);Person employee2 = instance.findEmployeeByAge("John", "Smith", 30);assertFalse(employee1 == employee2);}
同樣,在上面的單元測試代碼中,對employee1和employee2的引用引用了不同的對象,在這種情況下,John Smith已超過25歲。
這僅涉及@Cacheable,但是@CacheEvict和清除緩存中的項目呢? 另外,還有一個問題,就是在您的Spring配置中添加緩存并選擇合適的緩存實現。 但是,稍后會有更多……。
參考:來自Captain Debug's Blog博客的JCG合作伙伴 Roger Hughes的Spring 3.1 Caching和@Cacheable 。
翻譯自: https://www.javacodegeeks.com/2012/09/spring-31-caching-and-cacheable.html