淺談SpringCache與redis集成實現(xiàn)緩存解決方案
緩存可以說是加速服務(wù)響應(yīng)速度的一種非常有效并且簡單的方式。在緩存領(lǐng)域,有很多知名的框架,如EhCache 、Guava、HazelCast等。Redis作為key-value型數(shù)據(jù)庫,由于他的這一特性,Redis也成為一種流行的數(shù)據(jù)緩存工具。
在傳統(tǒng)方式下對于緩存的處理代碼是非常臃腫的。
例如:我們要把一個查詢函數(shù)加入緩存功能,大致需要三步。
一、在函數(shù)執(zhí)行前,我們需要先檢查緩存中是否存在數(shù)據(jù),如果存在則返回緩存數(shù)據(jù)
二、如果不存在,就需要在數(shù)據(jù)庫的數(shù)據(jù)查詢出來。
三、最后把數(shù)據(jù)存放在緩存中,當下次調(diào)用此函數(shù)時,就可以直接使用緩存數(shù)據(jù),減輕了數(shù)據(jù)庫壓力。
那么實現(xiàn)上面的三步需要多少代碼呢?下面是一個示例:
上圖中的紅色部分都是模板代碼,真正與這個函數(shù)有關(guān)的代碼卻只占了1/5,對于所有需要實現(xiàn)緩存功能的函數(shù),都需要加上臃腫的模板代碼??芍^是一種極不優(yōu)雅的解決方案。
那么如何讓臃腫的代碼重回清新的當初呢?
AOP不就是專門解決這種模板式代碼的最佳方案嗎,幸運的是我們不需要再自己實現(xiàn)切面了,SpringCache已經(jīng)為我們提供好了切面,我們只需要進行簡單的配置,就可以重回當初了,像下面這樣:
只需要加一個注解就可以了,對于原來的代碼連改都不需要改,是不是已經(jīng)躍躍欲試了?
對于配置SpringCache只需要三步:
第一步:加入相關(guān)依賴:
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> <version>1.6.0.RELEASE</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.3.2</version> </dependency>
第二步:配置SpringCache,Redis連接等信息
applicationContext-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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache-4.2.xsd">
<!-- 配置文件加載 -->
<context:property-placeholder location="classpath:*.properties"/>
<cache:annotation-driven cache-manager="cacheManager"/>
<!-- redis連接池 -->
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="${redis.maxIdle}" />
<property name="maxWaitMillis" value="${redis.maxWait}" />
<property name="testOnBorrow" value="${redis.testOnBorrow}" />
</bean>
<!-- 連接工廠 -->
<bean id="JedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
p:host-name="${redis.host}" p:port="${redis.port}" p:password="${redis.pass}" p:pool-config-ref="poolConfig"/>
<!-- redis模板 -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="JedisConnectionFactory" />
</bean>
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches">
<set>
<!-- 這里可以配置多個redis -->
<bean class="com.cky.rest.utils.RedisCache">
<property name="redisTemplate" ref="redisTemplate" />
<property name="name" value="content"/>
<!-- name對應(yīng)的名稱要在類或方法的注解中使用 -->
</bean>
</set>
</property>
</bean>
</beans>
redis.properties文件:
# Redis settings # server IP redis.host=192.168.100.55 # server port redis.port=6379 # server pass redis.pass= # use dbIndex redis.database=0 #max idel instance of jedis redis.maxIdle=300 #if wait too long ,throw JedisConnectionException redis.maxWait=3000 #if true,it will validate before borrow jedis instance,what you get instance is all usefull redis.testOnBorrow=true
第三步,編寫Cache接口實現(xiàn)類
Spring對于緩存只是提供了抽象的接口,并且通過接口來調(diào)用功能,沒有具體的實現(xiàn)類,所以需要我們自己實現(xiàn)具體的操作。
在上面配置中可知,每個實現(xiàn)類都會注入一個redisTemplate實例,我們就可以通過redisTemplate來操作redis
package com.cky.rest.utils;
import java.io.Serializable;
import org.apache.commons.lang3.SerializationUtils;
import org.springframework.cache.Cache;
import org.springframework.cache.support.SimpleValueWrapper;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
public class RedisCache implements Cache {
private RedisTemplate<String, Object> redisTemplate;
private String name;
@Override
public void clear() {
System.out.println("-------緩存清理------");
redisTemplate.execute(new RedisCallback<String>() {
@Override
public String doInRedis(RedisConnection connection) throws DataAccessException {
connection.flushDb();
return "ok";
}
});
}
@Override
public void evict(Object key) {
System.out.println("-------緩存刪除------");
final String keyf=key.toString();
redisTemplate.execute(new RedisCallback<Long>() {
@Override
public Long doInRedis(RedisConnection connection) throws DataAccessException {
return connection.del(keyf.getBytes());
}
});
}
@Override
public ValueWrapper get(Object key) {
System.out.println("------緩存獲取-------"+key.toString());
final String keyf = key.toString();
Object object = null;
object = redisTemplate.execute(new RedisCallback<Object>() {
@Override
public Object doInRedis(RedisConnection connection) throws DataAccessException {
byte[] key = keyf.getBytes();
byte[] value = connection.get(key);
if (value == null) {
System.out.println("------緩存不存在-------");
return null;
}
return SerializationUtils.deserialize(value);
}
});
ValueWrapper obj=(object != null ? new SimpleValueWrapper(object) : null);
System.out.println("------獲取到內(nèi)容-------"+obj);
return obj;
}
@Override
public void put(Object key, Object value) {
System.out.println("-------加入緩存------");
System.out.println("key----:"+key);
System.out.println("key----:"+value);
final String keyString = key.toString();
final Object valuef = value;
final long liveTime = 86400;
redisTemplate.execute(new RedisCallback<Long>() {
@Override
public Long doInRedis(RedisConnection connection) throws DataAccessException {
byte[] keyb = keyString.getBytes();
byte[] valueb = SerializationUtils.serialize((Serializable) valuef);
connection.set(keyb, valueb);
if (liveTime > 0) {
connection.expire(keyb, liveTime);
}
return 1L;
}
});
}
@Override
public <T> T get(Object arg0, Class<T> arg1) {
// TODO Auto-generated method stub
return null;
}
@Override
public String getName() {
return this.name;
}
@Override
public Object getNativeCache() {
return this.redisTemplate;
}
@Override
public ValueWrapper putIfAbsent(Object arg0, Object arg1) {
// TODO Auto-generated method stub
return null;
}
public RedisTemplate<String, Object> getRedisTemplate() {
return redisTemplate;
}
public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
public void setName(String name) {
this.name = name;
}
}
在配置過程中曾經(jīng)出現(xiàn)過兩次錯誤:
1.Xxxx.ClassNotFoundException 最后發(fā)現(xiàn)是jar下載不完整,把maven本地倉庫的對應(yīng)jar包文件夾刪除完從新下載就好了
2.Xxxx.MethodNotFoundException 這種情況是版本不對,換成第一步中的版本就可以了
SpringCache中常見注解的使用:
@Cacheable注解
最常用的注解,會把被注解方法的返回值緩存。工作原理是:首先在緩存中查找,如果沒有執(zhí)行方法并緩存結(jié)果,然后返回數(shù)據(jù)。此注解的緩存名必須指定,和cacheManager中的caches中的某一個Cache的name值相對應(yīng)??梢允褂胿alue或cacheNames指定。
如果沒有指定key屬性,spring會使用默認的主鍵生成器產(chǎn)生主鍵。也可以自定義主鍵,在key中可以使用SpEL表達式。如下:
@Cacheable(cacheNames=”content”,key=”#user.userId”)
Public User getUser(User user){
xxxxx
}
可以使用condition屬性,來給緩存添加條件,如下:
@Cacheable(cacheNames=”content”,key=”#user.userId”,condition=”#user.age<40”)
Public User getUser(User user){xxxxx}
@CachePut注解
先執(zhí)行方法,然后將返回值放回緩存。可以用作緩存的更新。
@CacheEvict注解
該注解負責從緩存中顯式移除數(shù)據(jù),通常緩存數(shù)據(jù)都有有效期,當過期時數(shù)據(jù)也會被移除。
此注解多了兩個屬性:
allEntries是否移除所有緩存條目。
beforeInvocation:在方法調(diào)用前還是調(diào)用后完成移除操作。true/false
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
使用kotlin編寫spring cloud微服務(wù)的過程
這篇文章主要介紹了使用kotlin編寫spring cloud微服務(wù)的相關(guān)知識,本文給大家提到配置文件的操作代碼,給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-09-09
java 數(shù)據(jù)結(jié)構(gòu)之刪除鏈表中的元素實例代碼
這篇文章主要介紹了java 數(shù)據(jù)結(jié)構(gòu)之刪除鏈表中的元素實例代碼的相關(guān)資料,需要的朋友可以參考下2017-01-01
解決Mybatis中foreach嵌套使用if標簽對象取值的問題
這篇文章主要介紹了解決Mybatis中foreach嵌套使用if標簽對象取值的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-02-02
Java 多線程等待優(yōu)雅的實現(xiàn)方式之Phaser同步屏障
在JAVA 1.7引入了一個新的并發(fā)API:Phaser,一個可重用的同步barrier。在此前,JAVA已經(jīng)有CyclicBarrier、CountDownLatch這兩種同步barrier,但是Phaser更加靈活,而且側(cè)重于 重用2021-11-11

