Java缓存实现

Author Avatar
Serical 3月 21, 2017
  • 在其它设备中阅读本文章

问题背景

由于报表需要长时间的查询展现,无疑是个致命的客户体验。

初步思路

key-value数据库redis做缓存,查询过的结果缓存到redis中,同样的条件查询时直接去redis中读取缓存数据展现即可。
初步想法:使用Java自定义注解实现,通过Spring-AOP处理缓存逻辑。

自定义注解

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoCache {

}

Spring-AOP实现

@Aspect
@Component
public class SystemCacheAspect {

    @Resource
    private JedisPool jedisPool; 

    @Pointcut("@annotation(com.cache.annotation.AutoCache)")
    public void pointCut() {

    }

    @Before("pointCut()")
    public void doBefore(JoinPoint jp) {
        System.out.println("doBefore..." + jp);
    }

    @Around(value="pointCut()")
    public Object doAround(ProceedingJoinPoint pjp) {
        System.out.println(pjp.getKind());
        System.out.println(pjp.getTarget());
        System.out.println(pjp.getThis());
        System.out.println(pjp.getClass());
        System.out.println(pjp.getSignature().getDeclaringTypeName());
        System.out.println(pjp.getSignature().getName());
        System.out.println(pjp.getSourceLocation());
        System.out.println(pjp.getStaticPart());

        System.out.println("doAround start ");
        Object result = null;
        try {
            System.out.println();
            System.out.println(genearteCacheKey(pjp.getArgs()));

            System.out.println();

            result = pjp.proceed();
        } catch (Throwable e) {
            result = "{\"total\":0,\"rows\":[]}";
            e.printStackTrace();
        }
        System.out.println("doAround end " + result);
        return result;
    }

    /**
     * 根据参数值生成唯一缓存key
     * @方法名:genearteCacheKey 
     * @参数 @param objs
     * @参数 @return
     * @参数 @throws Exception  
     * @返回类型 String
     */
    private String genearteCacheKey(Object[] objs) throws Exception {
        StringBuffer cacheKey = new StringBuffer();
        if (null != objs && objs.length > 0) {
            for (Object obj : objs) {
                BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
                PropertyDescriptor[] properties = beanInfo.getPropertyDescriptors();
                for (PropertyDescriptor desc : properties) {
                    if (!StringUtils.equals("class", desc.getName())) {
                        String value = BeanUtils.getProperty(obj,
                                desc.getName());
                        if (StringUtils.isNotBlank(value)) {
                            cacheKey.append(value);
                        }
                    }
                }
            }
        }

        return cacheKey.toString();
    }

    @After("pointCut()")
    public void doAfter(JoinPoint jp) {
        System.out.println("doAfter..." + jp);
    }

    @AfterReturning(pointcut="pointCut()", argNames="result", returning="result")
    public void doAfterReturn(JoinPoint jp, Object reuslt) {
        System.out.println("AfterReturning resutl :" + reuslt);
    }

    @AfterThrowing(value="pointCut()", throwing="e")
    public void doAfterThrowing(JoinPoint jp, Throwable e) {
        System.out.println("doAfterThrowing..." + jp);
    }
}

Spring配置文件

<!-- 缓存AOP配置 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>

碰到问题如下:

  • 缺少jar包:aspectjrt-1.6.9.jar,aspectjweaver-1.6.9.jar,去http://cn.jarfire.org/ 查找下载即可
  • @AfterReturning写法:
    @AfterReturning正确写法,3个必须都要有
    @AfterReturning(pointcut="pointCut()", argNames="result", returning="result")
    缺少returning="result"错误如下:
    java.lang.IllegalArgumentException: error at ::0 formal unbound in pointcut 
    缺少argNames="result"错误如下:
    java.lang.IllegalStateException: Returning argument name 'result' was not bound in advice arguments
    

AOP实现类中的执行顺序:

// 未抛出Exception时
doAround start 
doBefore...execution(Object com.power.bank.khjldj.cl.action.OperateItemAction.getOperateItemInfo(ColumnParamBean))
doAround end {"total":0,"rows":[]}
doAfter...execution(Object com.power.bank.khjldj.cl.action.OperateItemAction.getOperateItemInfo(ColumnParamBean))
AfterReturning resutl :{"total":0,"rows":[]}

// 抛出Exception时

doAround start 
doBefore...execution(Object com.power.bank.khjldj.cl.action.OperateItemAction.getOperateItemInfo(ColumnParamBean))
doAfter...execution(Object com.power.bank.khjldj.cl.action.OperateItemAction.getOperateItemInfo(ColumnParamBean))
doAfterThrowing...execution(Object com.power.bank.khjldj.cl.action.OperateItemAction.getOperateItemInfo(ColumnParamBean))