自定义缓存注解

本文主要介绍在实际使用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()方法,而是直接返回缓存中的值。

至此完成。

posted @ 2020-07-02 11:30  papa虫  阅读(727)  评论(0编辑  收藏  举报