【原】通过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 ...
随机推荐
- python生产者和消费者模式实现(二)多进程方式
import timeimport randomfrom multiprocessing import Process, Queue # 生产者def producer(q, i): food = ' ...
- mssql sqlserver 使用SSMS运行sql脚本的六种方法分享
摘要: 下文讲述五种运行sql脚本的方法,如下所示: 实验环境:sql server 2008 R2 在一次会议讨论中,大家咨询我使用SSMS运行sql脚本的方法,下文我将依次举例讲述sql脚本的运行 ...
- MySQL 部署 MHA 高可用架构 (二)
实现 MHA VIP 功能 配置 master_ip_failover 脚本(db3) 把 master_ip_failover 上传到 /iba/software 上 master_ip_failo ...
- CodeForces - 1255C(构造+模拟)
题意 https://vjudge.net/problem/CodeForces-1255C 一个长度为n的序列,给你n-2个三元组,比如p=[1,4,2,3,5],那么三元组为[1,4,2],[4, ...
- vue--实现跑马灯效果
<div id="app"> <input type="button" value="开始" @click="l ...
- 使用 IDEA 翻译插件
使用 IDEA 翻译插件 1.安装 在IDEA插件中搜索 translation根据下载量排序有个完全匹配名称的插件,下载,重启 2.配置翻译插件 都是中文,就不说了
- Redis学习笔记(六、哨兵)
目录: 基本概念 环境部署 哨兵原理 哨兵命令 基本概念: 1.什么是哨兵 我们先从字面意思来了解哨兵,哨兵是对执行警戒任务的士兵的统称:在redis中哨兵也是一样,他监控着redis服务器的状态. ...
- LeetCode98. 验证二叉搜索树
验证二叉搜索树 * * https://leetcode-cn.com/problems/validate-binary-search-tree/description/ * * algor ...
- Java LinkedList用法
本想找队列Queue,发现那是一个接口,LinkedList实现了Queue接口,可以当作队列来用. 一.概述 Java的LinkedList是一种常用的数据容器,与ArrayList相比,Linke ...
- oracle用户管理, 授权与回收权限
一. 用户管理参数, 0.删除用户: drop user 用户名 [cascade] 当我们删除用户时, 如改用户已创建过数据对象, 那么删除用户时必须加cascade参数, 用来同步删除 改用户的所 ...