亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > Java > 正文

集成Spring Redis緩存的實現

2024-07-14 08:43:18
字體:
來源:轉載
供稿:網友

這里的緩存主要是用于 Service 層的,所以下面的配置,都是針對 service 模塊的。

本文來自內部分享,對特殊信息進行了簡單處理。

本文都是在以緩存來講 Redis 的使用,實際上 Redis 不僅僅用于緩存,本身還是 NoSQL 數據庫,大家可以自己查找學習 Redis 的常用場景。

一、添加依賴

<!--緩存--><dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>4.3.14.RELEASE</version></dependency><!--redis--><dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> <version>1.8.10.RELEASE</version></dependency><dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <version>2.4.3</version></dependency><dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version></dependency>

二、配置

增加spring-redis.xml 配置文件,內容如下:

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:cache="http://www.springframework.org/schema/cache"xmlns:util="http://www.springframework.org/schema/util"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-2.5.xsdhttp://www.springframework.org/schema/cachehttp://www.springframework.org/schema/cache/spring-cache.xsdhttp://www.springframework.org/schema/utilhttp://www.springframework.org/schema/util/spring-util.xsd">  <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">    <property name="maxIdle" value="${redis.pool.maxIdle}"/>    <property name="maxTotal" value="${redis.pool.maxTotal}"/>    <property name="maxWaitMillis" value="${redis.pool.maxWaitMillis}"/>    <property name="testOnBorrow" value="${redis.pool.testOnBorrow}"/>    <property name="testOnReturn" value="${redis.pool.testOnReturn}"/>  </bean>  <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">    <property name="hostName" value="${redis.master.ip}"/>    <property name="port" value="${redis.master.port}"/>    <property name="poolConfig" ref="jedisPoolConfig"/>  </bean>  <bean id="redisKeySerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"/>  <bean id="redisValueSerializer" class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>  <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">    <property name="connectionFactory" ref="jedisConnectionFactory"/>    <property name="keySerializer" ref="redisKeySerializer"/>    <property name="hashKeySerializer" ref="redisKeySerializer"/>    <property name="valueSerializer" ref="redisValueSerializer"/>    <property name="hashValueSerializer" ref="redisValueSerializer"/>  </bean>  <!--在 redis.properties 配置緩存詳細信息-->  <util:properties id="redisExpires" location="classpath*:META-INF/spring/redis.properties"/>  <bean id="cacheManager" class="org.springframework.data.redis.cache.RedisCacheManager">    <constructor-arg index="0" ref="redisTemplate"/>    <!--默認緩存 10 分鐘-->    <property name="defaultExpiration" value="600"/>    <property name="usePrefix" value="true"/>    <property name="expires" ref="redisExpires"/>  </bean>  <!--啟用 cache 注解-->  <cache:annotation-driven cache-manager="cacheManager" proxy-target-class="true"/></beans>

在 src/main/resources/ 下面如果沒有META-INF/spring/ 目錄就創建一個,然后增加 redis.properties 配置,示例如下:

# 緩存名=有效時間halfHour=1800oneHour=3600oneDay=86400webSession=1800user=1800

除了上面配置外,在系統的 application.properties 中還需要提供下面幾個配置:

# redis 連接配置redis.master.ip=10.10.10.100redis.master.port=6379# redis 連接池配置redis.pool.maxIdle=200redis.pool.maxTotal=1024redis.pool.maxWaitMillis=1000redis.pool.testOnBorrow=trueredis.pool.testOnReturn=true

三、通過注解方式使用緩存

示例中,redis.propreties 配置如下:

# 數據庫定義,緩存 30 天databaseDef=2592000# 數據庫元數據,緩存 1 小時databaseMeta=3600

這個示例在數據庫服務上配置的,數據庫服務中,查詢次數遠遠大于新增、修改、刪除的次數,非常適合使用緩存。

1. 緩存數據 @Cacheable

@Override@Cacheable(value = "databaseDef", key = "'all'")public List<DatabaseDefinitionVo> selectAll() { return databaseDefinitionDao.selectAllVo();}

特別注意:所有這些注解中,key 的值是 Spel 表達式,必須按照 Spel 要求來寫。上面這個例子中,直接定義返回值的 key 是 all 字符串,需要加上單引號' 括起來,下面還有其他用法。

在例子中,下面的方法也使用了這個注解:

@Override@Cacheable(value = "databaseDef", key = "#id.toString()")public DatabaseDefinition selectByPrimaryKey(Long id) {  Assert.notNull(id, "數據庫 ID 不能為空!");  DatabaseDefinition definition = databaseDefinitionDao.selectByPrimaryKey(id);  Assert.notNull(definition, "數據庫定義不存在!");  return definition;}

在上面注解中,key 中的 #id 指的是參數中的 id,在 IDEA 中會有自動提示。.toString() 是調用 id 的方法,在系統中規定了 key 必須是字符串類型,所以當類型是 Long 的時候,需要轉換。

使用緩存的目的就是為了減少上面兩個方法調用時減少和數據庫的交互,減小數據庫的壓力,這是兩個主要的緩存數據的方法,下面的幾個操作都和上面這兩個方法有一定的關系。

重點:這里以及下面幾個注解中,都指定了 value = "databaseDef",這里的意思是說,要使用前面配置中的 databaseDef 對應的配置,也就是會緩存 30天。

2. 更新緩存 @CachePut

@Override@CachePut(value = "databaseDef", key = "#result.id.toString()")public DatabaseDefinition save(DatabaseDefinition definition, CurrentUser userModel)    throws ServiceException { //代碼 return definition;}

更新緩存的方法需要注意的是返回值,在上面 save 方法中,有可能是新增,有可能是更新,不管是那個操作,當操作完成后,上面注解會根據 key 的值生成 key,然后將方法的返回值作為 value 保存到緩存中。

這里 key 的寫法中 #result 指代返回值,.id 是返回值的屬性,.toString() 是調用 id 屬性的方法,在系統中規定了 key 必須是字符串類型,所以當類型是 Long 的時候,需要轉換。

這個方法上加的緩存還有問題,當新增或者更新后,通過 selectAll() 返回的值已經發生了變化,但是這里沒有清除 all 的緩存值,會導致 selectAll() 出現臟數據,下面會通過 @Caching 注解改造這里。

3. 清除緩存 @CacheEvict

@Override@CacheEvict(value = "databaseDef", key = "#id.toString()")public void deleteByPrimaryKey(Long id) throws ServiceException { DatabaseDefinition definition = selectByPrimaryKey(id); if (definition.getLoadState().equals(DatabaseDefinition.LoadState.UP)) {  throw new ServiceException("請先卸載數據庫!"); } databaseDefinitionDao.deleteByPrimaryKey(id);}

在上面新增或者修改的時候根據 id 緩存或者更新了緩存數據,這里當刪除數據的時候,還需要清空對應的緩存數據。

在上面注解中,key 中的 #id 指的是參數中的 id,在 IDEA 中會有自動提示。

這個方法上加的緩存還有問題,當刪除后,通過 selectAll() 返回的值已經發生了變化,但是這里沒有清除 all 的緩存值,會導致 selectAll() 出現臟數據,下面會通過 @Caching 注解改造這里。

4. 組合使用 @Caching

上面兩個注解中,都提到了臟數據,通過 @Caching 注解可以解決這個問題。

先修改第二個注解,來解決 save 時的臟數據:

@Override@Caching(put = @CachePut(value = "databaseDef", key = "#result.id.toString()"),     evict = @CacheEvict(value = "databaseDef", key = "'all'"))public DatabaseDefinition save(DatabaseDefinition definition, CurrentUser userModel)    throws ServiceException { //其他代碼 return definition;}

前面說明,新增或者修改的時候,all 緩存中的數據已經不對了,因此這里在 put 的同時,使用 evict 將 'all' 中的數據清除,這就保證了 selelctAll 下次調用時,會重新從庫中讀取數據。

對上面的刪除方法,也進行類似的修改:

@Override@Caching(evict = {  @CacheEvict(value = "databaseDef", key = "#id.toString()"),  @CacheEvict(value = "databaseDef", key = "'all'")})public void deleteByPrimaryKey(Long id) throws ServiceException { DatabaseDefinition definition = selectByPrimaryKey(id); if (definition.getLoadState().equals(DatabaseDefinition.LoadState.UP)) {  throw new ServiceException("請先卸載數據庫!"); } databaseDefinitionDao.deleteByPrimaryKey(id);}

注意這里的 evict 是個數組,里面配置了兩個清除緩存的配置。

5. 全局配置 @CacheConfig

在上面所有例子中,都指定了 value = "databaseDef",實際上可以通過在類上使用 @CacheConfig 注解配置當前類中的 cacheNames 值,配置后,如果和類上的 value 一樣就不需要在每個注解單獨配置。只有不同時再去指定,方法上的 value 值優先級更高。

@Service@CacheConfig(cacheNames = "databaseDef")public class DatabaseDefinitionServiceImpl implements      DatabaseDefinitionService, DatabaseSqlExecuteService, ApplicationListener {

有了上面配置后,其他緩存名字相同的地方可以簡化,例如刪除方法修改后如下:

@Override@Caching(evict = {  @CacheEvict(key = "#id.toString()"),  @CacheEvict(key = "'all'")})public void deleteByPrimaryKey(Long id) throws ServiceException { DatabaseDefinition definition = selectByPrimaryKey(id); if (definition.getLoadState().equals(DatabaseDefinition.LoadState.UP)) {  throw new ServiceException("請先卸載數據庫!"); } databaseDefinitionDao.deleteByPrimaryKey(id);}

其他例子

除了上面針對 databaseDef 的緩存外,還有 databaseMeta 的配置:

@Override@Cacheable(value = "databaseMeta", key = "#databaseId.toString()")public List<TableVo> selectTablesByDatabaseId(Long databaseId)   throws Exception { //代碼}@Override@Cacheable(value = "databaseMeta", key = "#databaseId + '_' + #tableName")public TableVo selectTableByDatabaseIdAndTableName(Long databaseId, String tableName)   throws Exception { //代碼}

這兩個方法是獲取數據庫元數據的,只有修改數據庫表的時候才會變化,因此不存在清除緩存的情況,但是萬一修改表后,想要新的元數據,該怎么辦?

因此增加了一個空的方法來清空數據,方法如下:

@Override@CacheEvict(value = "databaseMeta", allEntries = true)public void cleanTablesCache() {}

這里指定了 databaseMeta,通過 allEntries = true 清空所有 key 的緩存。通過這個方法可以保證在有需要的時候清空所有元數據的緩存。

實際上如果想要更精確的清除,可以傳入要清除的 databaseId 和 tableName 來更精確的清除。

增加緩存后的效果

調用 selectTablesByDatabaseId 多次時,輸出的日志如下:

INFO  c.n.d.u.DynamicDataSource - 當前數據源:默認數據源
DEBUG c.n.d.DataScopeContextProviderFilter - 服務調用返回前清除數據權限信息
INFO  c.n.d.u.DynamicDataSource - 當前數據源:默認數據源
DEBUG c.n.d.d.D.selectByPrimaryKey - ==>  Preparing: SELECT xxx (隱藏完整 SQL)
DEBUG c.n.d.d.D.selectByPrimaryKey - ==> Parameters: 1(Long)
DEBUG c.n.d.d.D.selectByPrimaryKey - <==      Total: 1
INFO  c.n.d.u.DynamicDataSource - 當前數據源:10.10.10.130/datareporting
DEBUG c.n.d.DataScopeContextProviderFilter - 服務調用返回前清除數據權限信息
INFO  c.n.d.u.DynamicDataSource - 當前數據源:默認數據源
DEBUG c.n.d.DataScopeContextProviderFilter - 服務調用返回前清除數據權限信息
INFO  c.n.d.u.DynamicDataSource - 當前數據源:默認數據源
DEBUG c.n.d.DataScopeContextProviderFilter - 服務調用返回前清除數據權限信息

從日志可以看出來,只有第一次進行了數據庫查詢,后續通過日志看不到數據庫操作。

調用清空緩存后,會再次查詢數據庫。

初次調用時,WEB請求花了 700多ms,后面再次調用時,平均不到 30 ms,這就是緩存最明顯的作用。

連接到 redis 服務后,查看所有 key,結果如下:

redis@redissvr:~$ redis-cli 
127.0.0.1:6379> keys *
1) "databaseMeta:1"
2) "databaseDef:all"
127.0.0.1:6379>

緩存中的數據都有 value 前綴,上面緩存了 all 和 id 為 1 的數據。

緩存注解是一種最簡單的緩存方式,但是需要配合 value 屬性的配置來使用,許多時候我們可能需要更精確的控制緩存,此時可以使用 RedisTemplate 來控制。

四、通過 RedisTemplate 使用緩存

有關這部分的詳細用法可以從網上搜索相關內容進行學習,這里列舉一個簡單的例子。

針對前面的 selectAll 我們換一種方式進行緩存。

首先注入下面的接口:

@Resource(name = "redisTemplate")private ValueOperations<String, List> valueOper;

修改 selectAll 方法如下:

@Override//@Cacheable(key = "'all'")public List<DatabaseDefinitionVo> selectAll() { List<DatabaseDefinitionVo> vos = valueOper.get("databaseDef:all"); if(vos != null){  return vos; } vos = databaseDefinitionDao.selectAllVo(); //緩存 1 小時 valueOper.set("databaseDef:all", vos, 1, TimeUnit.HOURS); return vos;}

首先通過valueOper.get("databaseDef:all") 嘗試獲取緩存信息,如果存在就直接返回。

如果不存在,就查詢數據庫,然后將查詢結果通過 set 進行緩存。

特別注意: 上面的 key,寫的是 "databaseDef:all",也就是前綴需要自己加上,如果直接寫成 all,在 Redis 中的 key 就是 all,不會自動增加前綴。

如果沒有前綴,那么當不同系統都使用 all 時,數據就會混亂!

五、Redis 服務器配置注意事項

內網使用的服務器,特殊配置如下:

# By default protected mode is enabled. You should disable it only if# you are sure you want clients from other hosts to connect to Redis# even if no authentication is configured, nor a specific set of interfaces# are explicitly listed using the "bind" directive.# protected-mode yesprotected-mode no

關閉了保護模式。

# IF YOU ARE SURE YOU WANT YOUR INSTANCE TO LISTEN TO ALL THE INTERFACES# JUST COMMENT THE FOLLOWING LINE.# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~# bind 127.0.0.1

注釋了綁定的 IP,這樣可以讓所有電腦訪問 Redis。

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,謝謝大家對VeVb武林網的支持。


注:相關教程知識閱讀請移步到JAVA教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲欧美日韩中文在线制服| 亚洲最大的免费| 亚洲精品成人av| 欧美黑人一区二区三区| 亚洲最新视频在线| www亚洲精品| 一区二区三区四区精品| 亚洲国产欧美一区二区三区久久| 欧美成人午夜剧场免费观看| 国产精国产精品| 久久中文字幕视频| 日韩欧美国产激情| 91精品国产乱码久久久久久蜜臀| 国产精品www色诱视频| 91精品国产乱码久久久久久久久| 欧美日韩一区二区在线| 色婷婷综合久久久久中文字幕1| 国产自摸综合网| 亚洲精品456在线播放狼人| 国产精品嫩草视频| 久久人91精品久久久久久不卡| 欧洲成人午夜免费大片| 精品久久久久久久久久久| 国产精品91视频| 97国产suv精品一区二区62| 欧美高清一级大片| 亚洲精品色婷婷福利天堂| 色妞久久福利网| 97色在线视频观看| 国产一区二区三区日韩欧美| 成人福利视频在线观看| 最新国产成人av网站网址麻豆| 亚洲а∨天堂久久精品9966| 色999日韩欧美国产| 国产精品久久久久久搜索| 国产欧美一区二区三区视频| 精品亚洲一区二区三区四区五区| 国产精品爽爽爽爽爽爽在线观看| 亚洲成人精品在线| 中日韩美女免费视频网站在线观看| 国产精品国语对白| 亚洲天堂第二页| 人人做人人澡人人爽欧美| 欧美日产国产成人免费图片| 亚洲精品美女久久久| 亚洲最大福利网站| 亚洲国产另类 国产精品国产免费| 日韩美女在线观看| 欧美午夜影院在线视频| 国产亚洲欧洲黄色| 最近2019年手机中文字幕| 成人女保姆的销魂服务| 亚洲国产精品福利| 奇米一区二区三区四区久久| 91成人天堂久久成人| 欧美亚洲国产另类| 亚洲高清久久久久久| 久久免费高清视频| 精品呦交小u女在线| 日韩免费电影在线观看| 久热精品视频在线观看一区| 欧美激情视频给我| 亚州欧美日韩中文视频| 国产国产精品人在线视| 久久99久久亚洲国产| 久久99国产精品自在自在app| 久久精品国产精品亚洲| 日韩欧美第一页| 国产在线观看精品| 日韩av电影手机在线观看| 日韩欧美在线视频日韩欧美在线视频| 国产精品video| 亚洲国产成人在线视频| 国产欧美精品va在线观看| 91国产美女在线观看| 91经典在线视频| 亚洲色图美腿丝袜| 欧美在线视频免费观看| 国产精品6699| 欧美激情一区二区三区成人| 日韩欧美在线视频日韩欧美在线视频| 国产精品入口夜色视频大尺度| 性欧美办公室18xxxxhd| 欧美国产日产韩国视频| 在线播放日韩av| 色婷婷综合久久久久中文字幕1| 精品国产31久久久久久| 国产精品欧美在线| 日韩在线观看免费网站| 丝袜一区二区三区| 色99之美女主播在线视频| 国产精品igao视频| 日韩电影中文字幕在线观看| 亚洲人午夜精品| 国产精品人成电影在线观看| 欧美激情三级免费| 欧美亚洲视频在线看网址| 欧美精品videos性欧美| 韩国国内大量揄拍精品视频| 欧美不卡视频一区发布| 久久久精品999| 欧美精品在线第一页| 日本精品免费一区二区三区| 国产午夜精品一区理论片飘花| 视频一区视频二区国产精品| 欧美剧在线观看| 欧美成人精品三级在线观看| 国产精品美女视频网站| 欧美激情在线一区| 91夜夜未满十八勿入爽爽影院| 2019中文字幕在线| 国产成人aa精品一区在线播放| 川上优av一区二区线观看| 中文字幕综合在线| 精品亚洲一区二区三区在线观看| 亚洲精品免费网站| 黑人极品videos精品欧美裸| 日韩电影免费在线观看| 上原亚衣av一区二区三区| 精品国产精品三级精品av网址| 欧美特黄级在线| 欧美激情精品久久久久久免费印度| 亚洲高清一区二| 日韩免费电影在线观看| 精品欧美激情精品一区| 日韩av在线最新| 裸体女人亚洲精品一区| 一本色道久久综合狠狠躁篇的优点| 国产日韩欧美影视| 91av在线不卡| 欧美日韩国产成人高清视频| 成人免费网站在线| 国产精品三级网站| 国产极品精品在线观看| www.亚洲男人天堂| 亚洲欧美激情视频| 欧美午夜激情小视频| 午夜精品99久久免费| 久久精品国产亚洲| 亚洲第一综合天堂另类专| 亚洲国产精品久久久久| 日本欧美在线视频| 欧美一区二区三区艳史| 成人激情视频在线播放| 亚洲精品日韩av| 色婷婷av一区二区三区久久| 亚洲欧美日韩国产中文专区| 欧美一级bbbbb性bbbb喷潮片| 久久琪琪电影院| 久久免费国产精品1| 欧美黄色成人网| 国产日韩在线亚洲字幕中文| 乱亲女秽乱长久久久| 亚洲国产精品久久久久久| 精品国产欧美一区二区三区成人| 久久全国免费视频| 国产精品大陆在线观看| 亚洲色图狂野欧美| 久久精品一本久久99精品| 欧美成人午夜激情在线| 成人中文字幕在线观看| 亚洲国产成人精品电影| 久久夜精品va视频免费观看| 国产成人精品一区|