面向切面编程--加缓存

本文介绍了一种在JavaEE项目中对部分表实施缓存的方法,重点讨论了何时及如何使用缓存来减轻数据库访问压力,并通过实例展示了具体实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


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

       我们在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 block

                     e.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,不足之处,请指正,谢谢

 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值