本文主要介绍在实际使用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()方法,而是直接返回缓存中的值。
至此完成。
原文:https://www.cnblogs.com/sunshineshen/p/13223459.html