亚洲视频二区_亚洲欧洲日本天天堂在线观看_日韩一区二区在线观看_中文字幕不卡一区

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.430618.com 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

背景

在查詢類(lèi)開(kāi)發(fā)中我們有使用緩存的場(chǎng)景,一般可以使用redis作為緩存,來(lái)緩解數(shù)據(jù)庫(kù)如MySQL的壓力。使用緩存的步驟為:

(1)從Redis緩存中獲取數(shù)據(jù),如果存在數(shù)據(jù),直接返回值。

(2)如果不存在,執(zhí)行數(shù)據(jù)庫(kù)的查詢方法

(3)將數(shù)據(jù)庫(kù)中的值放入緩存

NO CODE NO BB,代碼如下

//a.從緩存中獲取
String value = redisTemplate.opsForValue().get(key);
if (value != null) {
    log.info("從緩存中讀取到值:{}", value);
    return value;
}

//b.從數(shù)據(jù)庫(kù)中查詢
List<Member> members = memberMApper.listByName(name);

//c.同步value到緩存
value = JSONArray.toJSONString(members);
redisTemplate.opsForValue().set(key, value, expireTimes, TimeUnit.SECONDS);
return value;

如上代碼,這里有個(gè)問(wèn)題,我們只是要做個(gè)查詢而已,也就是只要b行的代碼,其他代碼不是業(yè)務(wù)代碼,不應(yīng)該由開(kāi)發(fā)人員去操心。那我們何不用注解的形式代替a和c代碼呢。

使用SpringBoot的緩存注解

SpringBoot提供了現(xiàn)成可用的緩存注解@Cacheble。

配置類(lèi)開(kāi)啟緩存注解

@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
    @Bean
    public RedisTemplate<Object,Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        ...
     }
     ...
         
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(60))      //緩存過(guò)期時(shí)間
                .disableCachingNullValues();

        return RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .transactionAware()
                .build();
    }
 }

使用@Cacheable注解

@Cacheable(value = "member",key = "#name")
public List<Member> listByName(String name) {
    return memberMapper.listByName(name);
}

測(cè)試

@Test
void listByName() {
    String name = "zhouzhou";
    List<Member> members = memberController.listByName(name);
    log.info("members:{}",members);
}

控制臺(tái)結(jié)果

members:[Member(id=1805590839001216, name=zhouzhou, code=109, annotationParam=null)]
利用AOP自定義Redis緩存注解

 

上面代碼發(fā)現(xiàn)使用Spring緩存注解的緩存失效時(shí)間還要在配置類(lèi)中進(jìn)行配置。于是我在想為什么這個(gè)失效時(shí)間不做成注解的這一項(xiàng)屬性呢,這樣自定義失效時(shí)間就比較方便了。

自定義緩存注解

注解定義

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomizeCache {

    String key();

    String value();

    long expireTimes() default 120L; //默認(rèn)過(guò)期時(shí)間120s

    int semaphoreCount() default Integer.MAX_VALUE;  //默認(rèn)限制線程并發(fā)數(shù)
}

參數(shù)說(shuō)明如下:

key():緩存的key,一般是一個(gè)動(dòng)態(tài)的參數(shù)值

vaule():緩存的value,value::key為Redis緩存中拼接的KEY

expireTimes():緩存失效時(shí)間,默認(rèn)

semaphoreCount():共享鎖,并發(fā)下允許訪問(wèn)的線程數(shù),用于保護(hù)數(shù)據(jù)庫(kù)。默認(rèn)為Integer.MAX_VALUE

AOP注解開(kāi)發(fā)

首先創(chuàng)建切面類(lèi)

@Component
@Aspect
@Slf4j
public class CacheAspect {
   ...
}

創(chuàng)建橫切面,為注解CustomizeCache添加功能。

@Pointcut("@annotation(com.lvshen.demo.redis.cache.CustomizeCache)")
    public void cachePointcut() {
    }

開(kāi)發(fā)緩存功能,定義@Around

 @Around("cachePointcut()")
    public Object doCache(ProceedingJoinPoint point) {
    ...
    }

獲取方法上注解的內(nèi)容

Method method = point.getTarget().getClass().getMethod(signature.getName(), signature.getMethod().getParameterTypes());
CustomizeCache annotation = method.getAnnotation(CustomizeCache.class);
String keyEl = annotation.key();
String prefix = annotation.value();
long expireTimes = annotation.expireTimes();
int semaphoreCount = annotation.semaphoreCount();

解析SpringEL表達(dá)式

Object[] args = point.getArgs();
DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
String[] parameterNames = discoverer.getParameterNames(method);
for (int i = 0; i < parameterNames.length; i++) {
    context.setVariable(parameterNames[i], args[i].toString());
}

拼接Redis KEY

//解析
String key = prefix + "::" + expression.getValue(context).toString();

判斷緩存中是否存在

value = redisTemplate.opsForValue().get(key);
if (value != null) {
    log.info("從緩存中讀取到值:{}", value);
    return value;
}

自定義組件-創(chuàng)建限流令牌

semaphore = new Semaphore(semaphoreCount);
boolean tryAcquire = semaphore.tryAcquire(3000L, TimeUnit.MILLISECONDS);
if (!tryAcquire) {
    //log.info("當(dāng)前線程【{}】獲取令牌失敗,等待其他線程釋放令牌",   Thread.currentThread().getName());
    throw new RuntimeException(String.format("當(dāng)前線程【%s】獲取令牌失敗,等帶其他線程釋放令牌", Thread.currentThread().getName()));
}

如果緩存沒(méi)有數(shù)據(jù),則執(zhí)行原本方法。

 value = point.proceed();

同步value到緩存

redisTemplate.opsForValue().set(key, value, expireTimes, TimeUnit.SECONDS);

最后釋放令牌

} finally {
    if (semaphore == null) {
     return value;
    } else {
     semaphore.release();
    }
}

調(diào)用注解

@CustomizeCache(value = "member", key = "#name")
public List<Member> listByNameSelfCache(String name) {
 return memberMapper.listByName(name);
}

測(cè)試

@Test
void testCache() {
    String name = "lvshen99";
    List<Member> members = memberService.listByNameSelfCache(name);
    log.info("members:{}",members);
}

測(cè)試結(jié)果

members:[Member(id=15, name=lvshen99, code=200, annotationParam=null)]

Redis上顯示

利用AOP自定義Redis緩存注解

 

我們也可以自定義緩存失效時(shí)間,如設(shè)置失效時(shí)間

 @CustomizeCache(value = "member", key = "#name",expireTimes = 3600)
 public List<Member> listByNameSelfCache(String name) {
  return memberMapper.listByName(name);
 }
利用AOP自定義Redis緩存注解

 

失效時(shí)間為,如圖,顯示是因?yàn)榻貓D的時(shí)候過(guò)了。

源碼地址如下:

完整源碼Github地址:https://github.com/lvshen9/demo/tree/lvshen-dev/src/main/JAVA/com/lvshen/demo/redis/cache

分享到:
標(biāo)簽:緩存 Redis
用戶無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定