前言:

  之前做的一个项目,虽然是查询ES,但内部有大量的逻辑计算,非常耗时,而且经常收到JVM峰值告警邮件。分析了一下基础数据每天凌晨更新一次,但查询和计算其实在第一次之后就可以写入缓存,这样后面直接从缓存拿数据,避免了大对象创建和网络开销,最后采用了Spring+Cglib进行处理。

遇到的问题:

  这个项目采用的是spring boot,里面的基本都是类的调用,没有做接口层, 所以无法使用Jdk的动态代理。我们都知道Jdk动态代理是基于接口层的代理,但基于的类的代理只能通过字节码层面代理,在这个项目中,很多方法调用是基于类方法的调用,如果要加入代理,可以采用字节码代理框架,最简单的实现方式无非如下:

CglibCacheProxy cacheMethodInterceptor = new CglibCacheProxy();
AgreementHotelPercentService proxyAgreementHotelPercentService = (AgreementHotelPercentService)cacheMethodInterceptor.createProxyObject(agreementHotelPercentService);
AgreementAndMemberHotelPercent agreementAndMemberHotelPercent = proxyAgreementHotelPercentService.getHotelPercent(filterList);  

上面的代码就是通过new一个Cglib工具类,然后需要代理的类丢进去,这么看起来是没什么问题,如果一个项目里有上百个这样的代码需要改造,效率以及问题出现因素都很不确定 。于是想到采用aop,把公共的代理模块抽取出来。问题是如何才能知道哪个类哪个方法要代理?如何代理?

基于Spring实现后置处理

大致思路就是在Spring加载完后, 再通过srping的后置处理器(BeanPostProcessor)拿出需要代理的Bean,然后通过注解方式给这个bean创建代理。

1. BeanPostProcessor简介

该接口我们也叫后置处理器,作用是在Bean对象在实例化和依赖注入完毕后,在显示调用初始化方法的前后添加我们自己的逻辑。注意是Bean实例化完毕后及依赖注入完成后触发的。接口的源码如下

 
public interface BeanPostProcessor {
//bean初始化方法调用前被调用
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
//bean初始化方法调用后被调用
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
方法 说明
postProcessBeforeInitialization 实例化、依赖注入完毕,
在调用显示的初始化之前完成一些定制的初始化任务
postProcessAfterInitialization 实例化、依赖注入、初始化完毕时执行

  

2.自定义 CglibCachePostBeanProcessor

public class CglibCachePostBeanProcessor implements BeanPostProcessor{
@Override
public Object postProcessAfterInitialization(Object bean,String beanName) throws BeansException{
if(bean.getClass().isAnnotationPresent(CglibCache.class)){
//判断是代理的类
CglibCache cglibCache = bean.getClass().getAnnotation(CglibCache.class);
if(cglibCache.isScan()){
          //创建代理
return CglibCacheProxy.createProxy(bean);
}
}else{
return bean;
}
return bean;
} @Override
public Object postProcessBeforeInitialization(Object bean, String beanNames) throws BeansException{
return bean;
}
}

代理工具类:

 public static Object createCacheProxy(Object bean){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(bean.getClass());//被代理的类
enhancer.setCallback(new CacheMethodInterceptor(bean));
return enhancer.create();
}

  

类级别注解,用于判断是否需要加入代理

@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface CglibCache {
//是否启用扫描
boolean isScan() default true;
}

  

3.Cglib代理类

public class CacheMethodInterceptor implements MethodInterceptor{
CacheStorageService cacheStorageService;
//代理对象
private Object target;
public CacheMethodInterceptor (Object target){
this.target = target;
}
@Override
public Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) throws Throwable{
Object result = null;
//方法上打了Cache的注解则说明需要执行缓存切入
Cache cacheable = method.getAnnotation(Cache.class);
if(cacheable!=null){
//具体的缓存业务逻辑
}
return method.invoke(target,args);
}
}

4.方法级别注解

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Cache {
/**
* 缓存key的名称
*
* @return
*/
String key(); /**
* key是否转换成md5值,有的key是整个参数对象,有的大内容的,比如一个大文本,导致redis的key很长
* 需要转换成md5值作为redis的key
*
* @return
*/
boolean keyTransformMd5() default true; /**
* key 过期日期 秒
*
* @return
*/
int expireTime() default 60; /**
* 时间单位,默认为秒
*
* @return
*/
TimeUnit dateUnit() default TimeUnit.SECONDS;
}

 

总结: 

以上就是核心代码,通过代码可以发现只需要在需要代理的类加上@CglibCache注解,并且在对应的方法加上@Cache 注解,结合缓存处理类就能完美的实现数据从缓存拉取。

												

【原】通过Spring结合Cglib处理非接口代理的更多相关文章

  1. Spring AOP 源码分析 - 创建代理对象

    1.简介 在上一篇文章中,我分析了 Spring 是如何为目标 bean 筛选合适的通知器的.现在通知器选好了,接下来就要通过代理的方式将通知器(Advisor)所持有的通知(Advice)织入到 b ...

  2. 死磕Spring之AOP篇 - 初识JDK、CGLIB两种动态代理

    该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读. Spring 版本:5.1 ...

  3. Spring AOP 介绍与基于接口的实现

    热烈推荐:超多IT资源,尽在798资源网 声明:转载文章,为防止丢失所以做此备份. 本文来自公众号:程序之心 原文地址:https://mp.weixin.qq.com/s/vo94gVyTss0LY ...

  4. Spring Boot整合MyBatis(非注解版)

    Spring Boot整合MyBatis(非注解版),开发时采用的时IDEA,JDK1.8 直接上图: 文件夹不存在,创建一个新的路径文件夹 创建完成目录结构如下: 本人第一步习惯先把需要的包结构创建 ...

  5. spring ----> 事务:传播机制和接口TransactionDefinition

    spring事务: 编程式事务(细粒度) 声明式事务(粗粒度,xml或者注解格式) spring接口TransactionDefinition: TransactionDefinition接口定义了事 ...

  6. Spring源码解析 - BeanFactory接口体系解读

    不知道为什么看着Spring的源码,感触最深的是Spring对概念的抽象,所以我就先学接口了. BeanFactory是Spring IOC实现的基础,这边定义了一系列的接口,我们通过这些接口的学习, ...

  7. Spring Boot实现通用的接口参数校验

    Spring Boot实现通用的接口参数校验 Harries Blog™ 2018-05-10 2418 阅读 http ACE Spring App API https AOP apache IDE ...

  8. JAVA高级架构师基础功:Spring中AOP的两种代理方式:动态代理和CGLIB详解

    在spring框架中使用了两种代理方式: 1.JDK自带的动态代理. 2.Spring框架自己提供的CGLIB的方式. 这两种也是Spring框架核心AOP的基础. 在详细讲解上述提到的动态代理和CG ...

  9. 【Spring】Spring的事务管理 - 1、Spring事务管理概述(数据库事务、Spring事务管理的核心接口)

    Spring事务管理概述 文章目录 Spring事务管理概述 数据库事务 什么是Spring的事务管理? Spring对事务管理的支持 Spring事务管理的核心接口 Platform Transac ...

随机推荐

  1. 【LeetCode】650. 只有两个键的键盘

    只有两个键的键盘 最初在一个记事本上只有一个字符 'A'.你每次可以对这个记事本进行两种操作: 1.Copy All (复制全部) : 你可以复制这个记事本中的所有字符(部分的复制是不允许的). 2. ...

  2. [转][读书笔记]深入理解java虚拟机

    原文地址:http://blog.csdn.net/hanekawa/article/details/51972259 第二章 Java内存区域与内存溢出异常 一,运行时数据区域: 1.        ...

  3. 【原创】CentOS 7 安装解压版mysql5.7

    1.将mysql压缩包解压至 /usr/local 文件夹下,将其改名为mysql #解压 tar -zxf mysql-5.7.27-el7-x86_64.tar.gz -C /usr/local/ ...

  4. pycharm 字符编码错误处理

    如果在MySQL创建表空间时指定了他的字符集为utf8 但是插入数据后 在pycharm查看却出现字符乱码 怎么解决呢 ? 首先进入cmd 页面 输入 chcp 65001  (意思是指定cmd所有字 ...

  5. CodeForces-1253B(贪心+模拟)

    题意 https://vjudge.net/problem/CodeForces-1253B 把一个序列划成几段,使得每一段都是+x在-x前面,二者均要有. 问划成几段,每一段的大小是多少. 思路 用 ...

  6. 封装查找元素以及集成日志输出,Base模块

    关于查找元素封装,Base模块 """ Selenium 封装基类 封装查找元素以及集成日志输出 """ from HTMLReport i ...

  7. 【BZOJ2437】[Noi2011]兔兔与蛋蛋(博弈+二分图)

    传送门 题意: 给出一个\(n*m\)的棋盘,上面有若干个黑色棋子,若干个白色棋子,还有一个空格. 每次先手选择一个空格,将它与相邻的某个白色棋子交换:后手则选择一个空格,与相邻的某个黑色棋子交换. ...

  8. c# WF 第2节 窗体的添加与删除

    本节内容: 1: 窗体的添加 2: 窗体的删除 1: 窗体的添加 2: 窗体的删除 3:窗口的运行,发现只有一个form1 是因为

  9. HTTP 与HTTPS 简单理解

      HTTP协议,即超文本传输协议(Hypertext transfer protocol).是一种详细规定了浏览器和万维网(WWW = World Wide Web)服务器之间互相通信的规则,通过因 ...

  10. zz阿里妈妈深度树检索技术(TDM)及应用框架的探索实践

    分享嘉宾:何杰 阿里妈妈 高级算法专家 编辑整理:孙锴 内容来源:DataFun AI Talk 出品社区:DataFun 注:欢迎转载,转载请注明出处 导读:阿里妈妈是阿里巴巴集团旗下数字营销的大中 ...