自定義緩存注解


本文主要介紹在實際使用memcached緩存時,自定義一個用於方法注解,基於AOP實現緩存存取策略。實現目的:在方法(如查詢數據庫的某方法)上加入該自定義注解后,執行方法前先查詢緩存,如果緩存存在則直接返回緩存結果不在執行該方法,提交系統效率。

1.memcached緩存配置

memcached緩存配置文件:

#配置Memcached屬性
memcached:
  servers: 127.0.0.1:11344
  poolSize: 10
  sanitizeKeys: false

作案工具需引用pom依賴

<dependency>
       <groupId>com.googlecode.xmemcached</groupId>
       <artifactId>xmemcached</artifactId>
       <version>2.4.6</version>
</dependency>

配置類如下:

package com.yacol.presale.web.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/
@Component
@ConfigurationProperties(prefix = "memcached")

public class XMemcachedProperties {
    /* 連接的服務 */
    private String servers;

    /* NIO連接池連接數量 */
    private int poolSize;

    /* 是否開啟對URL進行 encode */
    private boolean sanitizeKeys;

    public String getServers() {
        return servers;
    }

    public void setServers(String servers) {
        this.servers = servers;
    }

    public int getPoolSize() {
        return poolSize;
    }

    public void setPoolSize(int poolSize) {
        this.poolSize = poolSize;
    }

    public boolean isSanitizeKeys() {
        return sanitizeKeys;
    }

    public void setSanitizeKeys(boolean sanitizeKeys) {
        this.sanitizeKeys = sanitizeKeys;
    }
}

 

package com.yacol.presale.web.config;

import net.rubyeye.xmemcached.MemcachedClient;
import net.rubyeye.xmemcached.MemcachedClientBuilder;
import net.rubyeye.xmemcached.XMemcachedClientBuilder;
import net.rubyeye.xmemcached.impl.KetamaMemcachedSessionLocator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.IOException;


@Configuration
public class MemcachedConfig {

    @Autowired
    private XMemcachedProperties properties;

    //構建builder
    @Bean
    public MemcachedClientBuilder getXmBuilder(){
        MemcachedClientBuilder builder;
        //設置連接的服務
        builder = new XMemcachedClientBuilder(properties.getServers());

        //設置NIO連接池連接數量
        builder.setConnectionPoolSize(properties.getPoolSize());
        //開啟對URL進行encode
        builder.setSanitizeKeys(properties.isSanitizeKeys());
        //設置分布式算法為一致性Hash
        builder.setSessionLocator(new KetamaMemcachedSessionLocator());

        return builder;
    }

    //構建client
    @Bean
    public MemcachedClient getXmClient(MemcachedClientBuilder builder){
        MemcachedClient client;
        try {
            client = builder.build();
            return client;
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }
}

2.自定義緩存注解

  新建一個注解@CacheResult,如下:

package com.yacol.presale.common.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface CacheResult {
    String key() ;

    int expireSecond() default -1;
}
key表示實際緩存的key,expireSecond代表緩存的過期時間

  為了實現緩存的存取策略,還需要新建一個切面類,事先准備好作案工具,引用pom依賴

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

切面類如下:

package com.yacol.presale.common.annotation.advice;

import com.yacol.presale.common.annotation.CacheResult;
import net.rubyeye.xmemcached.exception.MemcachedException;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import net.rubyeye.xmemcached.MemcachedClient;

import org.aspectj.lang.annotation.Aspect;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.concurrent.TimeoutException;


@Aspect
@Component
public class CacheResultAdvice {
    private Logger log = LoggerFactory.getLogger(CacheResultAdvice.class);
    @Autowired
    private MemcachedClient memcachedClient;

    @Pointcut("@annotation(com.yacol.presale.common.annotation.CacheResult)")
    public void cacheResult() {
        //it will execute aroundCacheResult() method defined below
    }
    @Around("cacheResult()")
    public Object aroundCacheResult(ProceedingJoinPoint pjp) throws Throwable {
        Method currentMethod = getCurrentMethod(pjp);
        Annotation annotation = getMethodAnnotation(currentMethod, CacheResult.class);
        String key=(String) quietGetFromAnnotation("key",annotation);
        int expireSecond=(int) quietGetFromAnnotation("expireSecond",annotation);
        Object obj = getCache(key);
        if (obj != null) {
            //如果緩存里有值,則直接返回
            return obj;
        }
        obj = pjp.proceed();
        //記得將結果存入緩存中
        setCache(key,expireSecond,obj);
        return obj;
    }
    private Object getCache(String key){
        Object object=null;
        if(StringUtils.isBlank(key)){
            return null;
        }
        try {
             object =memcachedClient.get(key);
        } catch (TimeoutException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (MemcachedException e) {
            e.printStackTrace();
        }
        return object;
    }
    private void setCache(String key,int expireSecond,Object obj){
        try {
            memcachedClient.add(key,expireSecond,obj);
        } catch (TimeoutException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (MemcachedException e) {
            e.printStackTrace();
        }
    }

    protected Method getCurrentMethod(ProceedingJoinPoint pjp) throws Throwable {
        Signature sig = pjp.getSignature();
        MethodSignature msig = null;
        if (!(sig instanceof MethodSignature)) {
            throw new IllegalArgumentException("該注解只能用於方法");
        }
        msig = (MethodSignature) sig;
        Object target = pjp.getTarget();
        Method currentMethod = target.getClass()
                .getMethod(msig.getName(), msig.getParameterTypes());
        return currentMethod;
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    protected Annotation getMethodAnnotation(Method method, Class annotationClass) {
        return method.getAnnotation(annotationClass);
    }

    protected Object quietGetFromAnnotation(String methodName, Annotation annotation) {
        if (annotation == null) {
            return null;
        }
        try {
            return annotation.annotationType().getDeclaredMethod(methodName).invoke(annotation);
        } catch (Exception e) {
            log.debug(e.getMessage());
            // ignore
        }
        return null;
    }
}

    好了,完成了memcached配置和自定義注解配置及實現后,接下來就使用新建的注解來看看效果,測試方法如下:

@CacheResult(key = "ordersInfoKey", expireSecond = 180)
    public String getOrdersInfo(String orderBatchId){
        logger.info("實際方法獲取orders信息orderBatchId:"+orderBatchId);
        String orderInfo="Orders[orderBatchId=111222,orgType=測試]";
        logger.info("實際方法獲取orders信息結束,orders:{}",orderInfo);
        return orderInfo;
    }

  調用方法如下:

public void   getOrderInfo(){
String batchId="111222";
logger.info("開始查詢訂單信息:");
String ordersInfo=getOrdersInfo(batchId);
logger.info("查詢訂單信息結果:{}",ordersInfo);
}

第一次調用結果

2020-07-02 11:19:44.443 [] [http-nio-8080-exec-1] INFO  c.y.p.w.controller.CachedController :開始查詢訂單信息:
2020-07-02 11:19:44.477 [] [http-nio-8080-exec-1] INFO  c.y.p.d.s.impl.OrdersServiceImpl :實際方法獲取orders信息orderBatchId:111222
2020-07-02 11:19:44.478 [] [http-nio-8080-exec-1] INFO  c.y.p.d.s.impl.OrdersServiceImpl :實際方法獲取orders信息結束,orders:Orders[orderBatchId=111222,orgType=測試]
2020-07-02 11:19:44.490 [] [http-nio-8080-exec-1] INFO  c.y.p.w.controller.CachedController :查詢訂單信息結果:Orders[orderBatchId=111222,orgType=測試]

第二次調用結果

2020-07-02 11:20:21.254 [] [http-nio-8080-exec-4] INFO  c.y.p.w.controller.CachedController :開始查詢訂單信息:
2020-07-02 11:20:21.276 [] [http-nio-8080-exec-4] INFO  c.y.p.w.controller.CachedController :查詢訂單信息結果:Orders[orderBatchId=111222,orgType=測試]

即在過期時間內,第二次調用並沒有執行getOrdersInfo()方法,而是直接返回緩存中的值。

至此完成。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM