【原】通过Spring结合Cglib处理非接口代理
前言:
之前做的一个项目,虽然是查询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处理非接口代理的更多相关文章
- Spring AOP 源码分析 - 创建代理对象
1.简介 在上一篇文章中,我分析了 Spring 是如何为目标 bean 筛选合适的通知器的.现在通知器选好了,接下来就要通过代理的方式将通知器(Advisor)所持有的通知(Advice)织入到 b ...
- 死磕Spring之AOP篇 - 初识JDK、CGLIB两种动态代理
该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读. Spring 版本:5.1 ...
- Spring AOP 介绍与基于接口的实现
热烈推荐:超多IT资源,尽在798资源网 声明:转载文章,为防止丢失所以做此备份. 本文来自公众号:程序之心 原文地址:https://mp.weixin.qq.com/s/vo94gVyTss0LY ...
- Spring Boot整合MyBatis(非注解版)
Spring Boot整合MyBatis(非注解版),开发时采用的时IDEA,JDK1.8 直接上图: 文件夹不存在,创建一个新的路径文件夹 创建完成目录结构如下: 本人第一步习惯先把需要的包结构创建 ...
- spring ----> 事务:传播机制和接口TransactionDefinition
spring事务: 编程式事务(细粒度) 声明式事务(粗粒度,xml或者注解格式) spring接口TransactionDefinition: TransactionDefinition接口定义了事 ...
- Spring源码解析 - BeanFactory接口体系解读
不知道为什么看着Spring的源码,感触最深的是Spring对概念的抽象,所以我就先学接口了. BeanFactory是Spring IOC实现的基础,这边定义了一系列的接口,我们通过这些接口的学习, ...
- Spring Boot实现通用的接口参数校验
Spring Boot实现通用的接口参数校验 Harries Blog™ 2018-05-10 2418 阅读 http ACE Spring App API https AOP apache IDE ...
- JAVA高级架构师基础功:Spring中AOP的两种代理方式:动态代理和CGLIB详解
在spring框架中使用了两种代理方式: 1.JDK自带的动态代理. 2.Spring框架自己提供的CGLIB的方式. 这两种也是Spring框架核心AOP的基础. 在详细讲解上述提到的动态代理和CG ...
- 【Spring】Spring的事务管理 - 1、Spring事务管理概述(数据库事务、Spring事务管理的核心接口)
Spring事务管理概述 文章目录 Spring事务管理概述 数据库事务 什么是Spring的事务管理? Spring对事务管理的支持 Spring事务管理的核心接口 Platform Transac ...
随机推荐
- nginx 配置实例-负载均衡
1.实现效果 (1)浏览器地址栏输入地址 http://www.123.com/edu/a.html,负载均衡效果,平均 8080 和 8081 端口中 2.准备工作 (1)准备两台 tomcat 服 ...
- python+requests+re匹配抓取猫眼上映电影信息
python+requests抓取猫眼中上映电影,re正则匹配获取对应电影的排名,图片地址,片名,主演及上映时间和评分 import requests import re, json def get_ ...
- ARM的堆栈方式
当堆栈指针指向最后压入堆栈的数据时,称为满堆栈(Full Stack): 当堆栈指针指向下一个将要放入数据的空位置时,称为空堆栈(Empty Stack): 根据对战的生成方式分为:递增堆栈(Asce ...
- c# 第39节 抽象类、抽象方法
本节内容: 1:抽象类的说明 2:抽象类的实例 1:抽象类的说明 抽象类定义:方法前有abstract就称为抽象类.抽象方法,抽象方法不提供任何实际实现. 注意点1: 抽象方法必须在抽象类中声明: 不 ...
- UVA11374 Airport Express 正反两次最短路
问题描述 洛谷(有翻译) 吐槽 一道坑题. 如何对待商务票 因为商务票只有一张,所以在\(k\)条边中只有一条边会被选中,很显然,最后这条边会被枚举. 如何选择使用商务票的边 假设我们正在枚举这条边, ...
- luoguP1791 [国家集训队]人员雇佣
题意 考虑先将所有价值加上,之后用最小割求最小代价. 考虑每个点对\((i,j)\),我们这样建边: 1.源点向每个点i连\(\sum\limits E_{i,j}\)容量的边. 2.每个点向汇点连雇 ...
- CF-1175 B.Catch Overflow!
题目大意:有一个初始变量,值为0,三种操作 for x 一个循环的开始,循环x次 end 一个循环的结束 add 将变量值加一 问最后变量的值是否超过2^32-1,若超过,输出一串字符,不超过则输出变 ...
- Java网络传输数据加密算法
算法可逆,具有跨平台特性 import java.io.IOException; import java.io.UnsupportedEncodingException; import java.se ...
- 使用VisualVM 进行性能分析及调优
概述 开发大型 Java 应用程序的过程中难免遇到内存泄露.性能瓶颈等问题,比如文件.网络.数据库的连接未释放,未优化的算法等.随着应用程序的持续运行,可能会造成整个系统运行效率下降,严重的则会造成系 ...
- 解决node fs.writeFile 生成csv 文件乱码问题
解决node fs.writeFile 生成csv 文件乱码问题: fs.writeFile('xxx.csv', '\ufeff' + 要传入的数据, {encoding: 'utf8'}); \u ...