这里要分享的是:给数据库的部分表加缓存的实现

我们在javaee项目开发的时,当业务逻辑已实现,会着手去优化性能。减少数据库的访问压力,是优化的一个方面。在这个方面,就可能会用到缓存。

什么时候用缓存?当某些数据不是经常变化,以查询为主,增删改很少的数据,就可以考虑加缓存。

缓存的种类有很多,我所了解到的简单易用的有:ehcache、google提供的guavachace。

切面的好处:不侵入原来的代码,松藕合,在切面类中实现对缓存的管理,易维护,当有一天需要换另一个缓存技术方案的时候,只需要维护这个切面即可。(比如换成redis)

实现思路:

1.    分析出哪些需要加缓存的。

这边以一个积分模块为例,积分等级数据,积分规则数据,是很少变化的,可以加缓存。用户积分记录、用户积分获取和使用记录,都是经常变化的,所以不适合加缓存。

而对于后台管理的多条件分页查询,也是不适合加缓存的。

2.    写相应的切入点表达式,切入在相应的dao层方法,做相应的处理。

切入到查询方法,第一次查询,把数据以一定规则的key放入缓存。

当数据有更新的时候,需要同步更新缓存。而更新缓存最简单的,就是把它们全部清掉。清掉后,查询的时候发现缓存中找不到,又会把最新查出来的数据,放进去。这是缓存的数据就是最新的。

3.    key的设计。

通过主健查询的时候,key为:  “实体类名:{id:"id的值"}”例:TPointSetting:{"id":44}

当组合条件查询的时候,因为这次项目是用的是mybaits逆向工程生成的Mapper和实体类,非主键查询是这样的

TPointLevelExample example=new TPointLevelExample();

Criteria c = example.createCriteria();

c.andNameEqualTo(tPointLevel.getName());

List<TPointLevel> selectByExample = tPointLevelMapper.selectByExample(example);

那么就需要将example对象转成字符串,作为key的一部分。

key是这样的(有点长):TPointSetting:{"distinct":"false","conditions":[{"betweenValue":false,"condition":"tag =","listValue":false,"noValue":false,"secondValue":null,"singleValue":true,"typeHandler":"","value":1}]}

我这里写了一个工具类:

package com.ice.util.f3.util;import java.lang.reflect.Field;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import net.sf.json.JSONObject;/*** 查询参数 转成json字符串*  xuanhu  @date 2018年7月7日 上午11:06:37**/public class ParamUtil {public static String toString(Object param) {try {HashMap<String, Object> map = new HashMap<String,Object>();Class<? extends Object> class1 = param.getClass();String orderByClause=null;String distinct=null;List<JSONObject> conditions = new ArrayList<JSONObject>();if(class1.getSimpleName().endsWith("Example")) {Object example=param;Field orderByClauseField = class1.getDeclaredField("orderByClause");orderByClauseField.setAccessible(true);if(orderByClauseField.get(example)!=null)orderByClause=orderByClauseField.get(example).toString();Field distinctField = class1.getDeclaredField("distinct");distinctField.setAccessible(true);if(distinctField.get(example)!=null)distinct=distinctField.get(example).toString();Field oredCriteriatField = class1.getDeclaredField("oredCriteria");oredCriteriatField.setAccessible(true);Object object = oredCriteriatField.get(example);List<?> oredCriteria =null;if(oredCriteriatField.get(example)!=null)oredCriteria=(List)oredCriteriatField.get(example);Class<?> criteriaClass=null;Class<?> criterionClass=null;Class<?>[] declaredClasses = class1.getDeclaredClasses();if(declaredClasses.length>0)for (Class<?> class2 : declaredClasses) {if(class2.getSimpleName().endsWith("Criteria")) {criteriaClass= class2;}if(class2.getSimpleName().endsWith("Criterion")) {criterionClass= class2;}}if(criteriaClass!=null && criterionClass!=null) {for (Object criteria : oredCriteria) {Field field = criteriaClass.getDeclaredField("criteria");field.setAccessible(true);if(field.get(criteria)!=null) {List<?> criterions = (List)field.get(criteria);for (Object criterion : criterions) {JSONObject json = JSONObject.fromObject(criterion);conditions.add(json);}}}}//得到最终字符串if(orderByClause!=null)map.put("orderByClause", orderByClause);if(distinct!=null)map.put("distinct", distinct);if(conditions.size()>0)map.put("conditions",conditions);JSONObject json2 = JSONObject.fromObject(map);return json2.toString();}else{return "{\"id\":\""+param.toString()+"\"}";}} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}return "";}}

下面是切面类的代码:

package com.ice;import java.util.Map.Entry;import java.util.Set;import java.util.concurrent.TimeUnit;import javax.servlet.http.HttpServletRequest;import org.apache.log4j.Logger;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Pointcut;import org.springframework.core.annotation.Order;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.github.pagehelper.Page;import com.github.pagehelper.SqlUtil;import com.google.common.cache.CacheBuilder;import com.google.common.cache.CacheLoader;import com.google.common.cache.LoadingCache;import com.ice.util.f3.pojo.ReturnModel;import com.ice.util.f3.util.ParamUtil;@Aspect@Component@Order(1)public class CacheAspect {public static Logger log = Logger.getLogger(CacheAspect.class);//EhCacheUtil cacheUtil = EhCacheUtil.getInstance();private static final ThreadLocal<ProceedingJoinPoint> tl = new ThreadLocal<ProceedingJoinPoint>();private static LoadingCache<String, Object> cache = CacheBuilder.newBuilder().refreshAfterWrite(24, TimeUnit.HOURS)// 给定时间内没有被读/写访问,则回收。.expireAfterAccess(3, TimeUnit.HOURS)// 缓存过期时间.maximumSize(1000).// 设置缓存个数build(new CacheLoader<String, Object>() {@Override/** 当本地缓存命没有中时,调用load方法获取结果并将结果缓存 **/public Object load(String appKey) throws Exception {return getObject(appKey);}/** 数据库进行查询 **/private Object getObject(String appKey) throws Exception {log.debug("从数据库查询数据.....");Object proceed=null;try {proceed = tl.get().proceed();} catch (Throwable e) {e.printStackTrace();}return proceed;}});@Pointcut("execution(* com.ice.dao.f3.TPointLevelMapper.insert*(..))"+ " || execution(* com.ice.dao.f3.TPointLevelMapper.update*(..))"+ " || execution(* com.ice.dao.f3.TPointLevelMapper.delete*(..))"+ " || execution(* com.ice.dao.f3.TPointSettingMapper.insert*(..))"+ " || execution(* com.ice.dao.f3.TPointSettingMapper.update*(..))"+ " || execution(* com.ice.dao.f3.TPointSettingMapper.delete*(..))"+ " || execution(* com.ice.dao.f3.TSpecialPointSettingMapper.insert*(..))"+ " || execution(* com.ice.dao.f3.TSpecialPointSettingMapper.update*(..))"+ " || execution(* com.ice.dao.f3.TSpecialPointSettingMapper.delete*(..))")private void pointCutMethod() {}@Pointcut("execution(* com.ice.dao.f3.TPointLevelMapper.select*(..))"+ " || execution(* com.ice.dao.f3.TPointSettingMapper.select*(..))"+ " || execution(* com.ice.dao.f3.TSpecialPointSettingMapper.select*(..))")private void pointCutForMapper() {}@Around("pointCutMethod()")//guava cache清缓存public Object afterMethod4(ProceedingJoinPoint pjp) throws Throwable {// 业务方法执行Object result = pjp.proceed();String classname = pjp.getSignature().getDeclaringTypeName();String entityName= classname.substring(classname.lastIndexOf(".")+1).replace("Mapper", "");String methodname = pjp.getSignature().getName();if(methodname.contains("insert")||methodname.contains("update")||methodname.contains("delete")) {Set<Entry<String, Object>> entrySet = cache.asMap().entrySet();for (Entry<String, Object> entry : entrySet) {if(entry.getKey()!=null && entry.getKey().indexOf(entityName) != -1) {cache.invalidate(entry.getKey());}}log.debug("清缓存:"+entityName);}return result;}@Around("pointCutForMapper()")public Object afterMethod3(ProceedingJoinPoint pjp) throws Throwable {Object result;try {tl.remove();tl.set(pjp);result = null;boolean is_page=false;String classname = pjp.getSignature().getDeclaringTypeName();String entityName= classname.substring(classname.lastIndexOf(".")+1).replace("Mapper", "");Page<Object> page = SqlUtil.getLocalPage();if(page!=null ) is_page=true;System.out.println("是否分页了:"+is_page);String key = ParamUtil.toString(pjp.getArgs()[0]);key=entityName+":"+key;System.out.println(key);// 业务方法执行if(!is_page) {//没有分页的,从缓存中取result = cache.get(key);}else {result = pjp.proceed();//分页的不放缓存}} catch (Exception e) {throw e;}finally {tl.remove();}return result;}}

over,不足之处,请指正,谢谢

面向切面编程--加缓存相关推荐

  1. 面向切面编程实现Nestjs接口Api数据缓存

    一.业务场景 在后端接口开发过程中,我们经常会谈论的话题,提高接口响应速度,前端接口调用后端接口响应时间的缩短,我们抛开数据库设计及后端代码的业务逻辑等问题.我们经常会听到说用redis做数据缓存,直 ...

  2. 服务端第三次课程:面向切面编程AOP

    3:面向切面编程AOP 1:回顾 bean的组装方式 规划的装配 component autowired sacn是在configuration底下的 Java config 使用configurat ...

  3. Spring AOP 面向切面编程

    AOP 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件 ...

  4. AOP(面向切面编程)大概了解一下

    前言 上一篇在聊MemoryCache的时候,用到了Autofac提供的拦截器进行面向切面编程,很明显能体会到其优势,既然涉及到了,那就趁热打铁,一起来探探面向切面编程. 正文 1. 概述 在软件业, ...

  5. 面向切面编程AspectJ在Android埋点的实践

    在项目开发中,对 App 客户端重构后,发现用于统计用户行为的友盟统计代码和用户行为日志记录代码分散在各业务模块中,比如在某个模块,要想实现对用户的行为一和行为二进行统计,因此按照OOP面向对象编程思 ...

  6. AOP就是面向切面编程,我们可以从几个层面来实现AOP。 ![](https://upload-im

    AOP就是面向切面编程,我们可以从几个层面来实现AOP. 在编译器修改源代码,在运行期字节码加载前修改字节码或字节码加载后动态创建代理类的字节码,以下是各种实现机制的比较. spring AOP是Sp ...

  7. Spring AOP(面向切面编程)

    AOP(Aspect Oriented Programming),也就是面向切面编程,作为面向对象编程的一种补充,AOP已经成为一种比较成熟的编程方式.可以这样理解:OOP是从静态角度考虑程序结构,而 ...

  8. AOP (面向切面编程)

    AOP (面向切面编程) 编辑 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AO ...

  9. AOP—面向切面编程

    前言 上一篇在聊MemoryCache的时候,用到了Autofac提供的拦截器进行面向切面编程,很明显能体会到其优势,既然涉及到了,那就趁热打铁,一起来探探面向切面编程. 正文 概述 在软件业,AOP ...

  10. Spring | 深入理解面向切面编程(AOP)

    个人主页:BoBooY的CSDN博客_Java领域博主 前言:这节我们来学习什么是面向切面编程(AOP),为了方便大家理解,我们先了解代理模式再学习AOP. 文章目录 代理模式 7.1.静态代理 7. ...

最新文章

  1. Objective - C基础: 第一天 - 5.对象和类
  2. 父亲节用计算机给惊喜,2015父亲节的惊喜作文:给爸爸特殊的礼物
  3. 如何在linux下判断web服务是否开启?
  4. 怎么在linux上装java,如何在Ubuntu Linux上安装Java
  5. cv2 画多边形不填充_你不知道的4种方法:python方法绘制扇形
  6. C++primer第八章 IO库 8.3string流
  7. linux系统中文件的特性,linux文件系统特性
  8. Android 自动化测试框架简介
  9. easyswoole数据库连接池_Swoole Redis 连接池的实现
  10. 组态软件实现RFID设备数据采集
  11. Netty自定义数据包
  12. Discuz模板制作教程
  13. 计算机论文参考文献范文,计算机文类论文参考文献 计算机文参考文献有哪些...
  14. 【Autoware】之ndt_mapping理论公式及代码对比
  15. 2020 GKCTF
  16. Android 后台保活,这里有你需要的所有姿势。2019,最新版本。
  17. iOS接入Google登录
  18. html中移动端遮罩层,移动端微信分享弹出遮罩层js效果_蓝戒的博客
  19. 艾里光束matlab_减速圆艾里光束的产生及其聚焦特性
  20. 三种将list转换为map的方法

热门文章

  1. win10如何扩大c盘空间【系统天地】
  2. 解析Hl7消息,将消息可视化
  3. AES前端flutter加密与后端java解密pad block corrupted问题
  4. Java 接收OutLook 微软邮箱邮件
  5. iOS常见崩溃以及总结
  6. 安装虚拟机后无法获取 vmci 驱动程序版本句柄无效解决方法!
  7. 传感,驱动,控制-第十章quiz2复习笔记(UTS-41081)
  8. 用matlab求系统幅度频率响应,matlab频率响应
  9. 哪款投影仪做家庭影院效果好?家用投影仪哪个好
  10. linux 命令 cups,linux cups 打印机命令说明