springmvc controller 面向切面编程,实现数据查询的缓存功能
来源:互联网 发布:大数据毕业设计本科 编辑:程序博客网 时间:2024/06/16 05:01
应用场景:页面加载完成后异步ajax请求后台获取下拉框、列表数据等。后台controller层调用其他服务提供的接口实现数据查询。为了防止频繁的调用其他服务的接口,减少对其他服务的请求压力。在获取了数据以后把数据缓存到redis中,下次相同请求判断距离上次请求是否在有效期内,如果是,就直接从redis取数据返回。
难点:如何在最少更改现有系统的基础上实现此功能,用面向切面编程aop的思想切入controller方法,用注解的方式是比较理想的。把数据序列化和反序列化后实现数据在redis缓存中存取。
1. 定义一个注解
此注解作用在controller方法,指定方法是否使用缓存。由于是根据项目业务要求,此注解使用的http请求是get请求,而且是使用了@ResponseBody注解。
package com.lancy.annotation;import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;import org.springframework.web.bind.annotation.ResponseBody;/** * * 由于spring缓存注解不具备在方法上自定义缓存时间,而且如果在controller上异步请求的方法( @ResponseBody)上进行一些个性化的改造不好改,可以使用此注解 * * @version V1.0 */@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface DataCacheable { // 缓存的key,默认是方法签名和方法 参数做key String value() default ""; // 缓存的时间,默认30分钟 (秒为单位) int exp() default 1800;}
2. aop实现
package com.lancy.web.interceptor;import java.io.UnsupportedEncodingException;import javax.annotation.PostConstruct;import javax.inject.Inject;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import org.apache.commons.codec.digest.DigestUtils;import org.apache.commons.lang.StringUtils;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.After;import org.aspectj.lang.annotation.AfterReturning;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.aspectj.lang.annotation.Pointcut;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Scope;import org.springframework.stereotype.Component;import org.springframework.web.context.request.RequestAttributes;import org.springframework.web.context.request.RequestContextHolder;import org.springframework.web.context.request.ServletRequestAttributes;import com.lancy.web.constans.ResultConstans;import com.lancy.web.vo.Result;import com.lancy.annotation.dataCacheable;import com.lancy.util.io.ObjectTranscoder;import redis.clients.jedis.Jedis;import redis.clients.jedis.JedisPool;/** * 此方法应用场景,get方法请求,controller上返回的是Result对象* @version V1.0 */@Aspect@Componentpublic class CacheableInterceptor { private static final Logger LOGGER = LoggerFactory.getLogger(CacheableInterceptor.class); @Inject private JedisPool jedisPool; // 定义一个切入点,名称为pointCutMethod(),拦截类的所有controller方法。关于环绕方法的使用可以百度参考 @Pointcut("execution(* com.lancy..*.controller..*.*(..))") private void pointCutMethod() { } // 定义环绕通知, @annotation表示使用了此注解的才起作用 @Around("pointCutMethod() && @annotation(DataCacheable)") public Object aroundMethod(ProceedingJoinPoint pjp, dataCacheable dataCacheable) throws Throwable { long startTime = System.currentTimeMillis(); RequestAttributes ra = RequestContextHolder.getRequestAttributes(); ServletRequestAttributes sra = (ServletRequestAttributes) ra; HttpServletRequest request = sra.getRequest(); String method = request.getMethod(); if (method.equalsIgnoreCase("GET")){ String key = dataCacheable.value(); if (StringUtils.isBlank(key)){ String url = request.getRequestURL().toString(); String queryString = request.getQueryString(); queryString = (null == queryString) ? "" : queryString; key = url +"?"+queryString; LOGGER.debug("******CacheableInterceptor******url:"+key); /* key = DigestUtils.sha1Hex(key);*/ } Result cacheResult = null; Jedis jedis = null; try{ jedis = jedisPool.getResource();//连接redis byte[] infoList_srl = jedis.get(getKeyByteArray( key)); if (infoList_srl != null){//如果redis保存了数据 cacheResult = (Result) ObjectTranscoder.deserialize(infoList_srl);//把数据反序列化为实体 } if (cacheResult != null){ LOGGER.debug("******CacheableInterceptor Cache hit!\tMethod: {},use time: {}", pjp.getSignature().getName(),(System.currentTimeMillis()-startTime)); return cacheResult; } cacheResult = (Result) pjp.proceed(); if(null != cacheResult && cacheResult.getStatus() == ResultConstans.SUCEESS){ //第一次查询服务返回后把数据保存在redis中,先序列化 jedis.setex(getKeyByteArray( key), wbResultCacheable.exp(), ObjectTranscoder.serialize(cacheResult)); } return cacheResult; }catch (Exception e){ LOGGER.error("******CacheableInterceptor set key Exception: {}", e);/* throw new Exception(e);*/ }finally{ if (null != jedisPool && jedis != null){ jedis.close(); } } return pjp.proceed(); }else{ return pjp.proceed(); } } @PostConstruct public void postConstruct() {/* LOGGER.debug("=====CacheableInterceptor is OK!");*/ } private byte[] getKeyByteArray(String key) throws UnsupportedEncodingException{ return key.getBytes("UTF-8"); }}
3. 相关类
序列化和反序列化工具类
package com.lancy.util.io;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import org.apache.commons.io.IOUtils;import org.slf4j.Logger;import org.slf4j.LoggerFactory;public class ObjectTranscoder { private static final Logger logger = LoggerFactory.getLogger(ObjectTranscoder.class); public static byte[] serialize(Object value) { if (value == null) { throw new NullPointerException("ObjectTranscoder Can't serialize null"); } byte[] rv = null; ByteArrayOutputStream bos = null; ObjectOutputStream os = null; try { bos = new ByteArrayOutputStream(); os = new ObjectOutputStream(bos); os.writeObject(value); rv = bos.toByteArray(); } catch (IOException e) { throw new IllegalArgumentException("ObjectTranscoder Non-serializable object", e); } finally { IOUtils.closeQuietly(os); IOUtils.closeQuietly(bos); } return rv; } public static Object deserialize(byte[] in) { Object rv = null; ByteArrayInputStream bis = null; ObjectInputStream is = null; try { if (in != null) { bis = new ByteArrayInputStream(in); is = new ObjectInputStream(bis); rv = is.readObject(); } } catch (IOException e) { logger.warn("ObjectTranscoder Caught IOException decoding %d bytes of data", in == null ? 0 : in.length, e); } catch (ClassNotFoundException e) { logger.warn("ObjectTranscoder Caught CNFE decoding %d bytes of data", in == null ? 0 : in.length, e); } finally { IOUtils.closeQuietly(is); IOUtils.closeQuietly(bis); } return rv; }}
Result实体类
package com.lancy.web.vo;import java.io.Serializable;import com.sun.org.apache.regexp.internal.RE;import com.lancy.web.constans.ResultConstans;public class Result implements Serializable{ private static final long serialVersionUID = -4719595465158334529L; private int status = ResultConstans.FAIL; //200表示成功 500失败 private String description = ResultConstans.FAIL_SYSTEM_MSG;//失败提示语or成功提示语 private Object data;//结果 public void setDetail(int status,String msg){ this.status = status; this.description = msg; } public void setFailResult(){ this.status = ResultConstans.FAIL; this.description = ResultConstans.FAIL_SYSTEM_MSG; this.data=""; } public void setNotLoginMsg(String msg){ this.status = ResultConstans.NOT_LOGIN; this.description = msg; } public void setNotLoginMsg(){ this.status = ResultConstans.NOT_LOGIN; this.description = ResultConstans.NOT_LOGIN_MSG; } public void setNotChoseChildMsg(){ this.status = ResultConstans.NOT_CHOSE_CHILD; this.description = ResultConstans.NOT_CHOSE_CHILD_MSG; } public Result setFailResultMsg(String msg){ this.data=""; this.status = ResultConstans.FAIL; this.description = msg; return this; } public void setApiTokenFailResult(){ this.status = ResultConstans.FAIL; this.description = ResultConstans.API_TOKEN_FAIL_MSG; } public Result setSuccessResult(){ this.status = ResultConstans.SUCEESS; this.description = ResultConstans.SUCC_SYSTEM_MSG; return this; } public Result setSuccessResult(Object data){ this.status = ResultConstans.SUCEESS; this.description = ResultConstans.SUCC_SYSTEM_MSG; this.data = data; return this; } public Result(){} public int getStatus() { return status; } public Result setStatus(int status) { this.status = status; return this; } public String getDescription() { return description; } public Result setDescription(String description) { this.description = description; return this; } public Object getData() { return data; } public Result setData(Object data) { this.data = data; return this; } public static void main(String[] args) { }}
ResultConstans 类
package com.lancy.web.constans;public class ResultConstans { public final static int SUCEESS = 200; //成功 public final static int FAIL = 500; //失败 public final static int NOT_LOGIN = 300; //未登陆 public final static int NOT_CHOSE_CHILD = 400; //没有选择孩子 public final static String FAIL_SYSTEM_MSG = "网络出错,请稍后再试!"; //失败提示语 public final static String API_TOKEN_FAIL_MSG = "鉴权失败!"; //api token错误 public final static String SUCC_SYSTEM_MSG = "操作成功!"; //成功提示语 public final static String NOT_LOGIN_MSG = "请先登录!"; //还没登录提示语提示语 public final static String NOT_CHOSE_CHILD_MSG = "请选择孩子!"; //没有选择孩子提示语}
spring-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:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd" default-autowire="byName" default-lazy-init="false"> <description>spring redis配置</description> <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"> <property name="testWhileIdle" value="true" /> <property name="minEvictableIdleTimeMillis" value="60000" /> <property name="timeBetweenEvictionRunsMillis" value="30000" /> <property name="numTestsPerEvictionRun" value="-1" /> <property name="maxTotal" value="${redis.maxTotal}" /> <!-- 最大连接数 --> <property name="maxIdle" value="${redis.maxIdle}" /> <!-- 最大空闲连接数 --> <property name="minIdle" value="${redis.minIdle}" /><!-- 最小空闲连接数 --> </bean> <bean id="jedisPool" class="redis.clients.jedis.JedisPool" destroy-method="destroy"> <constructor-arg index="0" ref="jedisPoolConfig" /> <constructor-arg index="1" value="${redis.host}" type="java.lang.String" /> <constructor-arg index="2" value="${redis.port}" type="int" /> <constructor-arg index="3" value="360000" type="int" /> <!-- 超时时间 单位ms --> <constructor-arg index="4" value="${redis.password}" /> <constructor-arg index="5" value="${redis.db}" /> </bean></beans>
4. 控制层方法添加注解使用
@ResponseBody@RequestMapping(value="/test/getDept",method= RequestMethod.GET)@DataCacheable(exp=600)public Result getDept(HttpServletRequest request){ Result result = new Result(); //业务代码实现 return result;}
通过这个功能的实现,可以扩展到不同的需求,主要的还是学习这种编程方式。
阅读全文
0 0
- springmvc controller 面向切面编程,实现数据查询的缓存功能
- springmvc中aop对controller切面编程
- 面向切面编程的两种实现
- 面向切面编程的两种实现
- springmvc 在controller层使用aop切面编程
- Java利用动态代理模拟实现Spring的AOP(面向切面编程)功能的小实践
- Spring AOP的基本原理及面向切面编程的实现
- Spring面向切面的编程
- AOP-面向切面的编程
- 面向切面的AOP编程
- 使用java的Proxy实现AOP(面向切面编程)
- Aop_面向切面编程(2)_spring的aop实现
- js实现面向切面的编程(AOP)
- 面向切面编程的作用与实现简介
- spring AOP面向切面编程的四种实现方式
- 面向切面编程的作用与实现简介
- Java实现AOP面向切面编程的实例教程
- 面向切面编程的作用与实现简介
- Java中的equals和==区别
- spring mvc 前后台json交互问题
- ssh服务添加加密码算法
- 安卓学习日记(二)四大组件(一)活动——Activity详解
- Java反射机制与动态代理(二)
- springmvc controller 面向切面编程,实现数据查询的缓存功能
- Android Multimedia框架总结(二十三)MediaCodec补充及MediaMuxer引入(附案例)
- 百度网盟内容匹配广告和展示广告相关技术
- Android自定义边框背景颜色的Toast
- 《App后台开发运维和架构实践》读书笔记
- Hibernate Validator验证框架中@NotEmpty、@NotNull、@NotBlank 的区别
- 我的Kotlin For Android之路基础篇(一)
- vue 子组件向父组件传递参数
- 设计原则:组合复用原则