在講解Springboot相關組件以前,咱們先要了解在java的緩存插件體系,相似於咱們以前學習數據操做的數據源規範同樣,也有一個標準,這個標準叫作JSR107,咱們能夠經過在網上查詢相關的信息。java
Java Caching定義了5個核心接口,分別是CachingProvider, CacheManager, Cache, Entry 和 Expiry。mysql
Spring從3.1開始定義了org.springframework.cache.Cache和org.springframework.cache.CacheManager(緩存管理器)接口來統一不一樣的緩存技術;並支持使用JCache(JSR-107)註解簡化咱們開發;web
Cache接口爲緩存的組件規範定義,包含緩存的各類操做集合; Cache接口下Spring提供了各類xxxCache的實現;如RedisCache,EhCacheCache , ConcurrentMapCache等;redis
每次調用須要緩存功能的方法時,Spring會檢查檢查指定參數的指定的目標方法是否已經被調用過;若是有就直接從緩存中獲取方法調用後的結果,若是沒有就調用方法並緩存結果後返回給用戶。下次調用直接從緩存中獲取。 使用Spring緩存抽象時咱們須要關注如下兩點;spring
在咱們開發應用程序中,比較經常使用的註解以下所示: ||| |-|-| |Cache |緩存接口,定義緩存操做。實現有:RedisCache、EhCacheCache、ConcurrentMapCache等| |CacheManager |緩存管理器,管理各類緩存(Cache)組件| |@Cacheable |主要針對方法配置,可以根據方法的請求參數對其結果進行緩存| |@CacheEvict |清空緩存| |@CachePut |保證方法被調用,又但願結果被緩存。| |@EnableCaching |開啓基於註解的緩存| |@keyGenerator |緩存數據時key生成策略| |@serialize |緩存數據時value序列化策略|sql
接下來咱們編寫一個應用程序體驗一下springboot中緩存的使用。docker
/* Navicat MySQL Data Transfer Source Server : docker Source Server Version : 50505 Source Host : 10.21.1.47:3306 Source Database : joyblack Target Server Type : MYSQL Target Server Version : 50505 File Encoding : 65001 Date: 2018-12-20 09:45:44 */ SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for `department` -- ---------------------------- DROP TABLE IF EXISTS `department`; CREATE TABLE `department` ( `id` int(11) NOT NULL, `department_name` varchar(30) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of department -- ---------------------------- INSERT INTO `department` VALUES ('1', '鄉下冒險者公會'); INSERT INTO `department` VALUES ('2', '城市冒險者公會'); -- ---------------------------- -- Table structure for `user` -- ---------------------------- DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` int(11) NOT NULL, `user_name` varchar(20) NOT NULL, `login_name` varchar(20) NOT NULL, `department_id` int(11) NOT NULL DEFAULT 1, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of user -- ---------------------------- INSERT INTO `user` VALUES ('1', '阿庫婭', 'akuya', '1'); INSERT INTO `user` VALUES ('2', '克里斯汀娜', 'crustina', '1'); INSERT INTO `user` VALUES ('3', '惠惠', 'huihui', '1');
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency>
spring: datasource: username: root password: 123456 url: jdbc:mysql://127.0.0.1:3306/joyblack?characterEncoding=utf8&serverTimezone=GMT driver-class-name: com.mysql.cj.jdbc.Driver server: port: 8090 mybatis: configuration: # 開啓駝峯映射規則 map-underscore-to-camel-case: true
package com.zhaoyi.aweb.bean; public class User { private Integer id; private String loginName; private String userName; private Integer departmentId; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getLoginName() { return loginName; } public void setLoginName(String loginName) { this.loginName = loginName; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public Integer getDepartmentId() { return departmentId; } public void setDepartmentId(Integer departmentId) { this.departmentId = departmentId; } }
package com.zhaoyi.aweb.bean; public class Department { private Integer id; private String departmentName; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getDepartmentName() { return departmentName; } public void setDepartmentName(String departmentName) { this.departmentName = departmentName; } }
package com.zhaoyi.aweb.mapper; import com.zhaoyi.aweb.bean.User; import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; public interface UserMapper { @Insert(" insert into user(id, login_name, user_name, department_id) values (#{id}, #{loginName}, #{userName}, #{departmentId})") int insertUser(User user); @Delete("delete from user where id = #{id}") int deleteUser(Integer id); @Select("select * from user where id = #{id}") User getUserById(Integer id); @Update("update user set user_name = #{userName} where id = #{id}") int updateUser(User user); }
package com.zhaoyi.aweb.mapper; import com.zhaoyi.aweb.bean.Department; import org.apache.ibatis.annotations.*; public interface DepartmentMapper { // insert a derpartment. // @Options(useGeneratedKeys = true, keyProperty = "id") may you want get insert data generated id. @Insert("insert into department(id,department_name) values(#{id}, #{departmentName})") int insertDepartment(Department department); // delete a department by id. @Insert("delete from department where id = #{id}") int deleteDepartment(Integer id); // query a department by id. @Select("select * from department where id = #{id}") Department getDepartmentById(Integer id); // update a department information. @Update("update department set department_name=#{departmentName} where id=#{id}") int updateDepartment(Department department); }
package com.zhaoyi.aweb; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @MapperScan(value = "com.zhaoyi.aweb.mapper") @SpringBootApplication public class AwebApplication { public static void main(String[] args) { SpringApplication.run(AwebApplication.class, args); } }
package com.zhaoyi.aweb.controller; import com.zhaoyi.aweb.bean.User; import com.zhaoyi.aweb.mapper.UserMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class UserController { @Autowired private UserMapper userMapper; @RequestMapping("/user/insert") public User insertUser(User user){ userMapper.insertUser(user); return user; } @RequestMapping("/user/delete/{id}") public Integer insertUser(@PathVariable("id") Integer id){ return userMapper.deleteUser(id); } @RequestMapping("/user/select/{id}") public User getUser(@PathVariable("id") Integer id){ return userMapper.getUserById(id); } @RequestMapping("/user/update") public User updateUser(User user){ userMapper.updateUser(user); return user; } }
訪問 localhost:8090/user/select/1,獲得:數據庫
{"id":1,"loginName":"akuya","userName":"阿庫婭","departmentId":1}
前面咱們至關於複習了一遍以前的操做,咱們如今先作一下改變,通常來講,controller調用的service相關的東西,所以,咱們將對mapper的操做提到servicer一層,咱們添加一個service包:express
package com.zhaoyi.aweb.service; import com.zhaoyi.aweb.mapper.UserMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.zhaoyi.aweb.bean.User; @Service public class UserService { @Autowired private UserMapper userMapper; public User getUser(Integer id){ System.out.println("查詢:" + id); return userMapper.getUserById(id); } }
接下來,咱們從新寫一下controller的方法,以下所示:apache
package com.zhaoyi.aweb.controller; import com.zhaoyi.aweb.bean.User; import com.zhaoyi.aweb.mapper.UserMapper; import com.zhaoyi.aweb.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class UserController { @Autowired private UserService userService; @RequestMapping("/user/select/{id}") public User getUser(@PathVariable("id") Integer id){ return userService.getUser(id); } }
這樣,咱們就保證controller只和service進行交互了。咱們開始新知識了,通常使用緩存,咱們須要以下步驟:
很簡單吧?
如今,咱們每訪問一次select url,都會在控制檯打印一次
查詢:1
也就是說,當前都會調用service的getUser在數據庫進行查詢操做。接下來咱們爲該方法提供緩存效果,即在啓動類中添加註解@EnableCaching
:
package com.zhaoyi.aweb; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCaching; @EnableCaching @MapperScan(value = "com.zhaoyi.aweb.mapper") @SpringBootApplication public class AwebApplication { public static void main(String[] args) { SpringApplication.run(AwebApplication.class, args); } }
而後咱們去標註緩存註解在對應的服務方法上
@Cacheable(value = {"user"}) public User getUser(Integer id){ System.out.println("查詢:" + id); return userMapper.getUserById(id); }
這一次咱們再次訪問,發現除了第一次會打印查詢記錄以外,其餘的查詢都不會打印了(一個id只會進行一次查詢,即第一次),顯然已經作了緩存了。
仍是得先從自動配置類源碼入手。
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package org.springframework.boot.autoconfigure.cache; import java.util.List; import java.util.function.Supplier; import java.util.stream.Collectors; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration; import org.springframework.boot.autoconfigure.data.jpa.EntityManagerFactoryDependsOnPostProcessor; import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration; import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cache.CacheManager; import org.springframework.cache.interceptor.CacheAspectSupport; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.ImportSelector; import org.springframework.core.type.AnnotationMetadata; import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.util.Assert; @Configuration @ConditionalOnClass({CacheManager.class}) @ConditionalOnBean({CacheAspectSupport.class}) @ConditionalOnMissingBean( value = {CacheManager.class}, name = {"cacheResolver"} ) @EnableConfigurationProperties({CacheProperties.class}) @AutoConfigureAfter({CouchbaseAutoConfiguration.class, HazelcastAutoConfiguration.class, HibernateJpaAutoConfiguration.class, RedisAutoConfiguration.class}) @Import({CacheAutoConfiguration.CacheConfigurationImportSelector.class}) public class CacheAutoConfiguration { public CacheAutoConfiguration() { } @Bean @ConditionalOnMissingBean public CacheManagerCustomizers cacheManagerCustomizers(ObjectProvider<CacheManagerCustomizer<?>> customizers) { return new CacheManagerCustomizers((List)customizers.orderedStream().collect(Collectors.toList())); } @Bean public CacheAutoConfiguration.CacheManagerValidator cacheAutoConfigurationValidator(CacheProperties cacheProperties, ObjectProvider<CacheManager> cacheManager) { return new CacheAutoConfiguration.CacheManagerValidator(cacheProperties, cacheManager); } static class CacheConfigurationImportSelector implements ImportSelector { CacheConfigurationImportSelector() { } public String[] selectImports(AnnotationMetadata importingClassMetadata) { CacheType[] types = CacheType.values(); String[] imports = new String[types.length]; for(int i = 0; i < types.length; ++i) { imports[i] = CacheConfigurations.getConfigurationClass(types[i]); } return imports; } } static class CacheManagerValidator implements InitializingBean { private final CacheProperties cacheProperties; private final ObjectProvider<CacheManager> cacheManager; CacheManagerValidator(CacheProperties cacheProperties, ObjectProvider<CacheManager> cacheManager) { this.cacheProperties = cacheProperties; this.cacheManager = cacheManager; } public void afterPropertiesSet() { Assert.notNull(this.cacheManager.getIfAvailable(), () -> { return "No cache manager could be auto-configured, check your configuration (caching type is '" + this.cacheProperties.getType() + "')"; }); } } @Configuration @ConditionalOnClass({LocalContainerEntityManagerFactoryBean.class}) @ConditionalOnBean({AbstractEntityManagerFactoryBean.class}) protected static class CacheManagerJpaDependencyConfiguration extends EntityManagerFactoryDependsOnPostProcessor { public CacheManagerJpaDependencyConfiguration() { super(new String[]{"cacheManager"}); } } }
咱們經過打斷點方式,能夠查看配置相關的信息,這裏就不一一列出了。其運行過程大體以下:
方法運行以前,先查詢Cache
(緩存組件),按照@CacheName
指定的名字獲取(``CacheManager`獲取相應的緩存);第一次獲取緩存時,若是沒有對應的組件,會先自動建立。 總之,第一步獲取了一個緩存組件。
在組件中經過咱們提供的key
,默認是方法的參數,以後,在緩存內部,根據咱們提供的key又根據某種策略生成內部的key
,默認使用SimpleKeyGenerator
生成key(若是沒有參數:key = new SimpleKey(),若是有一個參數,key=參數的值,多個參數 key = SimpleKey(param))查詢對應的值。
若是沒有從對應的key
中查到值,就會調用目標方法(緩存標註的方法)返回的結果放到緩存(key對應的值)之中;若是有值,則直接將結果返回。
@Cacheable
標註的方法執行以前先來檢查緩存中有沒有這個數據,默認按照參數的值查找緩存,若是沒有就運行方法結果放入緩存。之後再次調用就能夠直接使用緩存中的數據。
核心:
使用CacheManager
按照名字獲得Cache組件,若沒有配置,默認就是ConcurrentMapCacheManager
組件;
key是使用KeyGenerator
生成的,默認使用SimpleKeyGenerator
;
CacheEnable主要用於標準方法,表示對該方法的返回結果進行緩存保存。
經過觀察CacheEnable源碼以下所示:
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package org.springframework.cache.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.core.annotation.AliasFor; @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface Cacheable { @AliasFor("cacheNames") String[] value() default {}; @AliasFor("value") String[] cacheNames() default {}; String key() default ""; String keyGenerator() default ""; String cacheManager() default ""; String cacheResolver() default ""; String condition() default ""; String unless() default ""; boolean sync() default false; }
從中能夠查詢到咱們能夠定製的屬性8個,其中value
和cacheNames
是相同功效的兩個註解參數。咱們接下來分析幾個經常使用的,cacheManager放到後面。
value
&cacheNames
緩存組件的名稱,在 spring 配置文件中定義,必須指定至少一個。@Cacheable(value="mycache")
他等同於
@Cacheable(value={"cache1","cache2"}
key
緩存的 key,能夠爲空,若是指定要按照 SpEL 表達式編寫,若是不指定,則缺省按照方法的全部參數進行組合。@Cacheable(value = {"user"}, key="#root.methodName+'[' + #id + ']'")
此方法自定義了key的值爲:methodname[id]。其中id爲咱們傳入的參數的值。
其語法特色遵循以下取值方式: |名字| 位置 |描述 |示例| |-|-|-|-| |methodName |root object| 當前被調用的方法名 #root.methodName |method |root object| 當前被調用的方法 #root.method.name |target |root object| 當前被調用的目標對象 #root.target |targetClass| root object| 當前被調用的目標對象類 #root.targetClass |args |root object| 當前被調用的方法的參數列表 #root.args[0] |caches |root object| 當前方法調用使用的緩存列表(如@Cacheable(value={"cache1", "cache2"})),則有兩個cache #root.caches[0].name |argument name |evaluation context| 方法參數的名字. 能夠直接 #參數名 ,也可使用 #p0或#a0 的形式,0表明參數的索引; #iban 、 #a0 、 #p0 |result |evaluation context| 方法執行後的返回值(僅當方法執行以後的判斷有效,如‘unless’,’cache put’的表達式 ’cache evict’的表達式beforeInvocation=false) #result ||||
咱們來測試一下這個功能,首先編寫配置文件
package com.zhaoyi.aweb.config; import org.springframework.cache.interceptor.KeyGenerator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.lang.reflect.Method; @Configuration public class CacheConfig { @Bean public KeyGenerator keyGenerator(){ return new KeyGenerator() { @Override public Object generate(Object o, Method method, Object... objects) { return method.getName() + "[" + Arrays.asList(objects).toString() + "]"; } }; } }
咱們自定義了一個key的生成器,接下來只須要在緩存註解處標註咱們定義的生成器便可:
@Cacheable(value = {"user"}, keyGenerator="keyGenerator") public User getUser(Integer id){ System.out.println("查詢:" + id); return userMapper.getUserById(id); }
keyGenerator
在孤獨部分的是咱們加入容器的bean的名字,@Bean不指定name時,默認就是方法名做爲類名,所以咱們此時的bean的名字就是keyGenerator
。
經過斷點,咱們能夠發現,當查詢用戶1的信息時,該方式生成的key相似爲:getUser[[1]]
@Cacheable(value = {"user"}, condition = "#a0 > 1") public User getUser(Integer id){ System.out.println("查詢:" + id); return userMapper.getUserById(id); }
當condition
代表只對id大於2的用戶信息進行緩存。
#a0
表明第1個參數,你也能夠直接#參數名
提取對應的參數值。
result
進行判斷。條件爲true不會緩存,fasle才緩存。例如:@Cacheable(value="testcache",unless="#result == null")
#result
表示返回結果。
是否使用異步模式,默認false,是否等待方法執行完才返回結果,另外,該配置若是生效,則@unless
註解失效。
緩存機制若是對一個常常變化的值進行操做的話,顯然咱們須要一個更新機制,例如當編號爲1的用戶被修改了以後,也但願經過返回結果緩存或者更新(若是對應的key已經有了的話)他的緩存信息。這個功能就是由@CachePut
來完成。
他的特色是標註方法在進行操做以後,對結果進行緩存。咱們不難想象,他的使用方式和@Cacheable
一模一樣,對其指定一樣的和@Cacheable
同樣的key便可。
爲了測試一下,咱們爲controller添加一個update方法
@RequestMapping("/user/update") public User updateUser(User user){ return userService.updateUser(user); }
而後對service使用@CachePut
註解,注意指定和讀取時使用的同樣的key(即用戶ID):
@Cacheable(value = "user", key = "#id") public User getUser(Integer id){ System.out.println("查詢:" + id); return userMapper.getUserById(id); } @CachePut(value = "user", key="#user.id") public User updateUser(User user) { userMapper.updateUser(user); return userMapper.getUserById(user.getId()); }
咱們和@Cacheabl
e同樣用了相同的緩存組件user,以及一致的key生成策略——用戶ID,同時查詢了更新後的用戶信息做爲返回值,確保@CachePut
將其放進緩存。
接下來咱們先查詢id爲1的用戶信息,重複查兩次,確保對當前結果進行了緩存,訪問localhost:8090/user/select/1
{"id":1,"loginName":"akuya","userName":"阿庫婭","departmentId":1}
如今,咱們將userName修改成「阿庫婭520」,訪問localhost:8090/user/update?id=1&userName=阿庫婭520,確保修改爲功以後,咱們再來訪問localhost:8090/user/select/1:
{"id":1,"loginName":"akuya","userName":"阿庫婭520","departmentId":1}
獲得了咱們更新以後的結果,說明@CachePut
達到了咱們想要的需求。
爲了測試@CachePut的效果,能夠先去除更新@CachePut的註解,這樣咱們就能夠發現,即使咱們修改了用戶信息,緩存的信息仍是舊用戶信息,添加了@CachePut以後,結果就實時更新了。
和
@Cacheable
不同的是,@CachePut
是優先調用方法,再將結果存儲在緩存中;而@Cacheable
則是先判斷緩存中是否存在對應的key,不存在才調用方法。所以咱們能夠推導出@Cacheable
指定key的時候是不能使用#result
這樣的語法的(聰明的你應該很容易理解)。
緩存清除,例如咱們刪除一個用戶的時候,可使用該註解刪除指定的緩存信息。
一樣須要指定緩存組件,以及key,有了以前的經驗,咱們應該寫這樣的service方法就能夠了:
@CacheEvict(value = "user", key="#id") public void deleteUser(Integer id) { System.out.println("刪除"); userMapper.deleteUser(id); }
咱們也指定了user
,也指定了其key的值。
相比較以前的註解,CacheEvict
還有一些特殊的註解,例如:
allEntries
默認值false,標誌爲true以後,會清除指定組件中的全部緩存信息,例如@CacheEvict(value = "user", allEntries = true)
該註解就會在方法執行後,將user組件中的全部緩存都清除。
beforeInvocation
默認爲false,若是設置爲true,那麼清除緩存操做會發生在方法執行以前。這種狀況比較特殊,默認狀況下,咱們是方法執行後才進行清緩存操做,顯然若是在方法運行過程當中出現異常,就不會清除緩存。因此,若是有特殊要求,咱們可使用該參數,讓緩存一開始就清除掉,而無論你方法是否運行成功與否。若是咱們的緩存規則比較複雜,須要用到以上多個註解的特性,那麼能夠考慮使用@Caching
註解,查詢其源碼咱們就能夠知道,他其實就是其餘註解的組合體:
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface Caching { Cacheable[] cacheable() default {}; CachePut[] put() default {}; CacheEvict[] evict() default {}; }
爲了使用@Caching
,咱們能夠編寫一個這樣的service,經過loginName查詢用戶信息,而後呢,將返回結果放在幾個不一樣的key值之中,這樣,咱們直接經過id或者其餘爲查詢條件查詢時,就能夠直接從 以前的查詢中,拿到結果,而無需再從數據庫中查詢了:
@Select("select * from user where login_name = #{loginName}") User getUserByLoginName(String loginName);
@Caching( cacheable = { @Cacheable(value = "user", key = "#loginName") }, put = { @CachePut(value="user", key = "#result.id"), @CachePut(value="user", key = "#result.userName") } ) public User getUserByLoginName(String loginName){ return userMapper.getUserByLoginName(loginName); }
咱們經過loginName查詢到結果以後,還想要經過userName以及id做爲緩存的key保存到內存中,這些key值都只能從返回結果中取到。咱們前面有提到,@cacheable
是沒辦法拿到返回結果的。所以咱們使用@CachePut
來完善了咱們的需求。
接下來經過測試就會發現,只要咱們經過loginName查詢了akuya的數據,咱們再經過id爲1(就是akuya)來查詢akuya信息,發現這時候就直接獲得結果,無需查詢數據庫了。由於咱們的緩存組件中,經過userService.getUserByLoginName
方法的執行,已經就id、loginName以及userName對akuya的信息進行了緩存。
仔細觀察咱們以前緩存屬性配置你會發現,不少屬性很是的囉嗦,例如value="user"
,重複指定了N次,咱們能不能經過某個註解,直接一次性將這些配置指定了呢?
有,他就是CacheConfig
,用於抽取緩存的公共配置。如下是其源碼:
/* * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.cache.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * {@code @CacheConfig} provides a mechanism for sharing common cache-related * settings at the class level. * * <p>When this annotation is present on a given class, it provides a set * of default settings for any cache operation defined in that class. * * @author Stephane Nicoll * @author Sam Brannen * @since 4.1 */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface CacheConfig { /** * Names of the default caches to consider for caching operations defined * in the annotated class. * <p>If none is set at the operation level, these are used instead of the default. * <p>May be used to determine the target cache (or caches), matching the * qualifier value or the bean names of a specific bean definition. */ String[] cacheNames() default {}; /** * The bean name of the default {@link org.springframework.cache.interceptor.KeyGenerator} to * use for the class. * <p>If none is set at the operation level, this one is used instead of the default. * <p>The key generator is mutually exclusive with the use of a custom key. When such key is * defined for the operation, the value of this key generator is ignored. */ String keyGenerator() default ""; /** * The bean name of the custom {@link org.springframework.cache.CacheManager} to use to * create a default {@link org.springframework.cache.interceptor.CacheResolver} if none * is set already. * <p>If no resolver and no cache manager are set at the operation level, and no cache * resolver is set via {@link #cacheResolver}, this one is used instead of the default. * @see org.springframework.cache.interceptor.SimpleCacheResolver */ String cacheManager() default ""; /** * The bean name of the custom {@link org.springframework.cache.interceptor.CacheResolver} to use. * <p>If no resolver and no cache manager are set at the operation level, this one is used * instead of the default. */ String cacheResolver() default ""; }
源碼也加了很多註釋,但其內在屬性都是提取的公共配置,咱們直接在service類上指定以後,若是內部方法沒有特殊指定,都會套用咱們使用該註解指定的值。例如,咱們抽取出公共value屬性。
@Service @CacheConfig(cacheNames = "user") public class UserService {
這樣,咱們整個service的緩存方法註解,若是都是用的user組件,就無需特殊指定,直接刪除便可。經過該註解能夠自動的幫咱們指定這個值。
cacheNames和value是同一個意思,不過
@CacheConfig
沒對value作兼容,因此咱們這裏必須寫cacheNames
.
通過這些練習,或許您會發現,緩存內部存儲的數據咱們能看到嗎?若是能看到就行了。能夠嗎?固然能夠。因此,咱們接下來要學習的redis,爲咱們解決這個問題。