java:aocache:基于aspectJ实现的方法缓存工具
背景
最近一直在做一些服务端的设计,经常遇到常量计算的问题,比如获取查找一个类的所有方法,获取有指定注解(Annnotation)的方法并查找注解的上特定的元注解是否有特定的值 。。。。总之逻辑很复杂,而且会频繁调用。
比如在服务端方法拦截器上经常执行这样的计算,事实上在运行时,对于一个类(Class)或方法(Method),它在运行时就是个常量,上述复杂计算返回结果也是一个恒定的值不会改变,称之为常量计算。
如果每次请求同一个服务方法都要重复执行这样的计算,无疑是对性能的浪费。
FunctionCached
为此,我很久之前就实现了一个解决个问题的工具类FunctionCached
完整代码参见 :https://gitee.com/l0km/common-java/blob/master/common-base2/src/main/java/net/gdface/cache/FunctionCached.java
相关说明参见之前的博客:《java:基于guava缓存(LoadingCache)实现结果缓存避免重复计算》
《java:基于弱引用(WeakReference)的FunctionCached实现》
有了FunctionCached
,实现一个方法的缓存就比较方便了,大概是如下的样子:
/** 缓存对象 */private final FunctionCached<String, Class<?>> CACHED_CLASS_FORNAME = FunctionCached.builder()/** 弱引用值模式 */.weakValues().nullable().getterFunction(new Function<String, Class<?>>() {@Overridepublic Class<?> apply(String suffix) {/** 执行计算 */return classFormName0(suffix);}}).build();/** 真正负责计算的方法 */private Class<?> classFormName0(String className){// DO SOMETHING}/** * 对外提供的计算方法,* 方法实现就是从缓存获取参数为key的值,* 如果值不存在缓存自动执行 classFormName0,将结果再存入缓存,避免下次再计算。 */public Class<?> classFormName(String className){return CACHED_CLASS_FORNAME.get(className);}
如上的模式大概就是一内一外两个方法,内部方法负责真正的计算由缓存对象调用,外部方法则直接从缓存读取数据提供给调用方。
烦恼
话说这个FunctionCached
我用了好长时间,一直觉得还不错。上个月写了一个新项目casban,项目中用FunctionCached
写了十几个不同的缓存方法,写得我不胜其烦。
两周前才结束的项目beanfilter又涉及到大量的方法缓存。
这境遇,让我深感在大量需要计算结果缓存提高性能的场景下,FunctionCached
用起来还是不顺手。要是能更简单就好了。
不满的种子开始发芽----必须要做点什么改变现状。
设计目标
为了彻底解决这个麻烦,前阵子断断续续的开始构思一个新的工具,上周开始设计,目标设计是一个更方便使用且通用的方法(Method)缓存工具,如果也能实现单实例(Constructor)缓存则更好。
项目定名aocache
(Aspect Oriented Cache),定下了如下的设计目标:
- 支持 1.7及以上JDK版本
这样才有更广泛的通用性,相对底层的通用工具设计时也能用得上。
- 依赖库少
最好没有依赖库,目的同上。
- 方便使用
以注解(Annotaion)标记为使用主要方式。
- 支持对不同方法个性化配置缓存,
比如支持弱引用模式(WeakReference),软引用模式(SoftReference)以减少不必要的资源占用。
效果
经过一周的设计,总算完成,发布第一个版本0.2.0,达到预期设计目标。以前面的classFormName
方法的例子来对比的话,最终的使用效果如果就是这样:
/** 注解定义该方法由aocache提供结果缓存 */@AoCacheablepublic Class<?> classFormName(String className){// DO SOMETHING}
是不是很简单?十几行代码缩减为一个注解@AoCacheable
aocache
现在开始正式介绍 aocache:
aocache(Aspect Oriented Cache)是一个基于aspectJ实现的方法(Method/Constructor)缓存工具。
设计用于对方法计算返回的结果缓存,以实现同的输入参数只计算一次,第二次以后调用法则返回第一次计算的结果,以减少重复计算提高系统运行效率,适用于运行时(runtime)输入参数与输出结果保持恒定映射的场景。
aocache基于切面编程(Aspect-Oriented Programming)框架实现方法调用拦截,保存计算结果的缓存基于guava的LoadingCache
缓存实现。
快速入门
pom.xml配置
引入依赖
<dependency><groupId>com.gitee.l0km</groupId><artifactId>aocache</artifactId><version>0.2.0</version></dependency>
aocache不会传递依赖任何其他依赖库
增加maven插件aspectj-maven-plugin
用于项目编译时织入(Compile Time Weaving)。
Compile Time Weaving:
编译时织入也叫静态织入,是aspectj最简单的织入形式。aspectj-maven-plugin插件执行aspectj编译器AJC 在源码编译过程中将切面拦截代码添加到Java编译器生成的的.class。
<build><plugins><plugin><groupId>org.codehaus.mojo</groupId><artifactId>aspectj-maven-plugin</artifactId><version>1.10</version><configuration><source>1.7</source><target>1.7</target><encoding>UTF-8</encoding><complianceLevel>1.7</complianceLevel><verbose>true</verbose><showWeaveInfo>true</showWeaveInfo><aspectLibraries><aspectLibrary><groupId>com.gitee.l0km</groupId><artifactId>aocache</artifactId></aspectLibrary></aspectLibraries></configuration><executions><execution><goals><goal>compile</goal><goal>test-compile</goal></goals></execution></executions></plugin></plugins></build>
源码注解
如下在AocacheTest.hello(String)
方法上增加注解@AoCacheable
就激活了该方法的aocache缓存能力。
如果IDE环境没有配置aspectj的合适环境,下面的单元测试可能无法在IDE中正确运行。我的eclipse目前还不行。
示例如下:
AocacheTest.java
import static org.junit.Assert.*;import org.junit.Test;import com.gitee.l0km.aocache.MemberCache;
import com.gitee.l0km.aocache.annotations.AoCacheable;public class AocacheTest {@AoCacheableprivate String hello(String name) {return "hello,"+String.valueOf(name);}@Testpublic void test() {try {String s = hello("jerry");System.out.println(s);/** 第二次以后调用返回结果与第一次是同一个对象 */System.out.printf("%s %b\n",hello("jerry"),hello("jerry")==s);System.out.printf("%s %b\n",hello("jerry"),hello("jerry")==s);System.out.printf("%s %b\n",hello("jerry"),hello("jerry")==s);System.out.printf("%s %b\n",hello("jerry"),hello("jerry")==s);/** 获取方法实际的的执行次数 */long hitCount = MemberCache.INSTANCE.invokeCountOf(this, "hello",new Class<?>[]{String.class},new Object[]{"jerry"});System.out.printf("调用次数 %d\n", hitCount);/** 断言:hello(String)方法对于参数'jerry'的调用只有一次 */assertEquals(1, hitCount);} catch (Throwable e) {e.printStackTrace();fail(e.getMessage());}}}
编译
mvn clean install
maven插件aspectj-maven-plugin
输出显示hello
方法已经被增加了拦截点(Join Point)
[INFO] --- aspectj-maven-plugin:1.10:test-compile (default) @ aocache-example-ctw ---
[INFO] Showing AJC message detail for messages of types: [error, warning, fail]
[INFO] Join point 'method-execution(void com.gitee.l0km.aocache.example.ctw.AocacheCtwTest.hello())' in Type 'com.gitee.l0km.aocache.example.ctw.AocacheCtwTest' (AocacheCtwTest.java:24) advised by around advice from 'com.gitee.l0km.aocache.aop.AocacheAnnotatedAspect' (aocache-0.0.0-SNAPSHOT.jar!AocacheAnnotatedAspect.class(from AocacheAnnotatedAspect.java))
执行单元测试:
mvn -Dtest=AocacheTest -DskipTests=false test
输出:
T E S T S
-------------------------------------------------------
Running com.gitee.l0km.aocache.example.ctw.AocacheTest
hello,jerry
hello,jerry
hello,jerry
hello,jerry
hello,jerry
调用次数 1
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.084 secResults :Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
@AoCacheable
aocache为每个方法定义一个独立的缓存对象。aocache允许为每个方法配置不同的缓存策略, 最简单的方法就是通过@AoCacheable
注解实现缓存配置
@AoCacheable注解定义在方法指定缓存该方法的的返回值。通过@AoCacheable
上定义的字段,可以精确配置每个方法缓存。
@AoCacheable 也可以定义在构造方法上,定义在构造方法就为该类实现了单实例缓存
字段名 | 默认值 | 说明 |
---|---|---|
debugOutput | false | 是否输出调试信息 |
debugOutputDetail | false | 是否输出详细调试信息 |
weakKeys | false | 指定存储在缓存中的每个键(而不是值)都应封装在WeakReference中(默认情况下,使用强引用)。 |
weakValues | false | 指定存储在缓存中的每个值(而不是键)都应包装在SoftReference中(默认情况下,使用强引用)。软引用的对象将以全局最近最少使用的方式进行垃圾收集,以响应内存需求。 |
softValues | false | 指定存储在缓存中的每个值(而不是键)都应包装在SoftReference中(默认情况下,使用强引用)。软引用的对象将以全局最近最少使用的方式进行垃圾收集,以响应内存需求。 |
initialCapacity | -1 | 设置内部哈希表的最小总大小。例如,如果初始容量为60,并发级别为8,则创建八个段,每个段都有一个大小为8的哈希表。在构建时提供足够大的估计值可以避免以后进行昂贵的调整大小操作,但不必设置此值—过高会浪费内存。 |
concurrencyLevel | 0 | 指定更新操作之间允许的并发性。用作内部大小调整的提示。该表在内部进行了分区,以尝试在没有争用的情况下允许指定数量的并发更新。 |
maximumSize | -1 | 指定缓存可以包含的最大条目数。请注意,缓存可能会在超过此限制之前收回一个条目。随着缓存大小接近最大值,缓存会收回不太可能再次使用的条目。例如,缓存可能会因为最近或不经常使用而被逐出。 |
maximumWeight | -1 | 指定缓存中可能包含的项的最大权重。重量是使用地磅指定的秤来确定的,使用这种方法需要在调用构建之前对地磅进行响应调用。 |
expireAfterWrite | -1 | 指定在创建条目或最近替换其值后经过固定的持续时间后,应自动从缓存中删除每个条目。 |
expireAfterWriteTimeUnit | MINUTES(分钟) | expireAfterWrite的时间单位 |
expireAfterAccess | -1 | 指定在创建条目、最近一次替换其值或最后一次访问之后经过固定的持续时间后,应自动从缓存中删除每个条目。访问时间由所有缓存读取和写入操作(包括cache.asMap().get(Object)和cache.asMop().put(K,V))重置,但不由cache.asMap的集合视图上的操作重置。 |
expireAfterAccessTimeUnit | MINUTES(分钟) | expireAfterAccess的时间单位 |
refreshAfterWrite | -1 | 指定活动条目的创建或其值的最新替换后经过固定的持续时间后,活动条目的自动刷新条件。刷新的语义在LoadingCache.refresh中指定,并通过调用CacheLoader.reload来执行。 由于CacheLoader.reload的默认实现是同步的,建议此方法的用户使用异步实现覆盖CacheLoader.reload;否则将在不相关的缓存读取和写入操作期间执行刷新。 当前自动刷新是在对条目的第一个过时请求发生时执行的。触发刷新的请求将对CacheLoader.reload进行阻塞调用,如果返回的future完成,则立即返回新值,否则返回旧值。 |
refreshAfterWriteTimeUnit | MINUTES(分钟) | refreshAfterWrite的时间单位 |
弱引用模式
默认配置使用强引用模式保存Key和Value,这种情况下,计算结果一旦产生常驻内存。
弱引用模式允许在JVM自动清理不再被引用的计算结果(VALUE)或其对应的KEY。下次调用时再重新计算。适用于不需要长期保存的数据。
所以用户可以根据自己的场景选择使用强引用还是弱引用配置缓存。
@Cacheable Of Spring
spring也使用 @Cacheable
注解也提供了类似的方法缓存功能,那么aocache与之有什么不同?
显然,spring的方法缓存功能只能用于spring环境下,而aocache只要求JDK 1.7以上任何Java开发环境。
项目地址
码云仓库位置:https://gitee.com/l0km/aocache
请访问git仓库获取完整说明
示例项目
aocach-example 为aocache的示例项目,
项目中实践了aocache在aspectJ的三种织入模式(CTW/LTW/PCW
)下的应用。
码云仓库位置:https://gitee.com/l0km/aocache-example
参考资料
《Chapter 5. Load-Time Weaving》
《The AspectJTM 5 Development Kit Developer’s Notebook》
相关文章:
java:aocache:基于aspectJ实现的方法缓存工具
背景 最近一直在做一些服务端的设计,经常遇到常量计算的问题,比如获取查找一个类的所有方法,获取有指定注解(Annnotation)的方法并查找注解的上特定的元注解是否有特定的值 。。。。总之逻辑很复杂,而且会频繁调用。 比如在服务端…...

UE4_材质_湿度着色器及Desaturation算法_ben材质教程
学习笔记,不喜勿喷!侵权立删,祝愿美好生活越来越好。 效果图: 原图: 1、使用初学者内容包的材质 我们这里使用虚幻自带的材质M_Brick_Clay_Old,复制一个更名为M_Brickclayoldwet材质。 2、添加去饱和度Desaturation节…...
AI问答-ERP:理解 ERP / 我国ERP发展现状 / ERP软件有哪些 / 华为自研ERP
一、理解ERP 1.1、定义 ERP(Enterprise Resource Planning)是企业资源计划的缩写,它集成了企业各个业务领域,包括采购、销售、库存、生产制造、财务等多个方面,进行全面管理、智能决策的一种企业管理系统。 1.2、功…...

C语言 | Leetcode C++题解之第199题二叉树的右视图
题目: 题解: #define MAX_NODE_NUM 100 int* rightSideView(struct TreeNode* root, int* returnSize){if (root NULL) {*returnSize 0;return NULL;}int *res (int *)malloc(sizeof(int) * MAX_NODE_NUM);int cnt 0;struct TreeNode **record (st…...
java:aocache的单实例缓存
上一篇博客《java:aocache:基于aspectJ实现的方法缓存工具》介绍了aocache的基本使用, 介绍AoCacheable注解时说过,AoCacheable可以定义在构造方法上,定义在构造方法,该构建方法就成了单实例模式。 也就是说,只要构建…...

c++11 abi 兼容性
理解 _GLIBCXX_USE_CXX11_ABI: 兼容性与现代化之间的平衡 随着 C 标准的不断演进,编译器和标准库实现也在不断更新,以支持新的语言特性和库功能。然而,这些更新有时会引入不兼容的更改,特别是应用程序二进制接口(ABI&…...

获取个人免费版Ubuntu Pro
首先上官网地址:Ubuntu Pro | Ubuntu 点击页面中的"Get Ubuntu Pro now" 将用途选为“Myself”,在此页面中Ubuntu说明了该版本只面向个人开发者,且最终只允许5台设备免费使用;因而部署设备的抉择就不得不慎重考虑了&am…...

Pinia的基本用法
Pinia的安装和引入 1.安装Pinia npm install pinia2. 在vue项目的main.js文件中引入pinia import { createApp } from vue import { createPinia } from pinia import App from ./App.vueconst pinia createPinia() const app createApp(App)app.use(pinia) app.mount(#ap…...

正版软件 | DeskScapes:将您的桌面变成生动的画布
您是否厌倦了静态的桌面背景?Stardock 的 DeskScapes 软件赋予您将任何图片或视频动画化的能力,让您的 Windows 桌面焕发活力。 动画桌面,艺术生活 使用 DeskScapes 您可以将任何静态图片或视频转化为桌面背景。不仅如此,通过 60 …...
OpenCV cv::Mat到 Eigen 的正确转换——cv2eigen
在进行计算机视觉项目时,我们经常需要处理相机位姿的变换。最近,我在项目中遇到了一个看似简单但实际上颇具挑战性的问题:从 OpenCV 的 cv::Mat 格式转换到 Eigen 库的格式。这个过程中遇到了一些问题,但最终找到了一个稳健的解决…...
PostgreSQL的扩展(extensions)-常用的扩展-pg_pathman
PostgreSQL的扩展(extensions)-常用的扩展-pg_pathman pg_pathman 是一个用于 PostgreSQL 的分区管理扩展。它提供了一种高效的方式来管理和使用数据库分区,可以显著提升查询性能,特别是在处理大规模数据集时。 安装 pg_pathman…...

数据结构之树
基础知识: 树是一种非线性结构,其严格的数学定义是:如果一组数据中除了第一个节点(第一个节点称为根节点,没有直接前驱节点)之外,其余任意节点有且仅有一个直接前驱,有零个或多个直接…...

6毛钱SOT-23封装28V、400mA 开关升压转换器,LCD偏置电源和白光LED应用芯片TPS61040
SOT-23-5 封装 TPS61040 丝印PHOI 1 特性 • 1.8V 至 6V 输入电压范围 • 可调节输出电压范围高达 28V • 400mA (TPS61040) 和 250mA (TPS61041) 内部开关电流 • 高达 1MHz 的开关频率 • 28μA 典型空载静态电流 • 1A 典型关断电流 • 内部软启动 • 采用 SOT23-5、TSOT23…...
saga模型
Saga源于Hector Garcaa-Molrna和Kenneth Salem发表的论文Sagas。一个LLT事务(Long Lived Transaction)可以分成若干个小的事务执行单元,这些小执行单元就是saga事务。Saga方案更适合用于长事务场景。Saga模型将一个分布式事务拆分为多个本…...
深度神经网络:解锁智能的密钥
深度神经网络:解锁智能的密钥 在人工智能的浩瀚星空中,深度神经网络(Deep Neural Networks, DNNs)无疑是最耀眼的那颗星。它以其强大的学习能力、高度的适应性和广泛的应用场景,成为了我们解锁智能世界的一把密钥。本…...

国际现货黄金最新价格如何分析?结合较高的时间周期
国际现货黄金投资是一种24小时交易的品种,这意味着,在交易日我们打开电脑图表,分析完走势之后就有机会做交易了。但问题也出在这里,如果对国际现货黄金最新价格把握不住,分析和交易就无从谈起了,下面我们就…...

微服务和kafka
一、微服务简介 1.单体架构 分布式--微服务--云原生 传统架构(单机系统),一个项目一个工程:比如商品、订单、支付、库存、登录、注册等等,统一部署,一个进程 all in one的架构方式,把所有的…...

Jetpack架构组件_Navigaiton组件_1.Navigaiton切换Fragment
1.Navigation主要作用 方便管理Fragment (1)方便我们管理Fragment页面的切换 (2)可视化的页面导航图,便于理清页面间的关系。 (3)通过destination和action完成页面间的导航 (4&a…...

[计算机网络] 虚拟局域网
虚拟局域网 VLAN(Virtual Local Area Network,虚拟局域网)是将一个物理的局域网在逻辑上划分成多个广播域的技术。 通过在交换机上配置VLAN,可以实现在同一个VLAN 内的用户可以进行二层互访,而不同VLAN 间的用户被二…...

LabVIEW遇到无法控制国外设备时怎么办
当使用LabVIEW遇到无法控制国外产品的问题时,解决此类问题需要系统化的分析和处理方法。以下是详细的解决思路和具体办法,以及不同方法的分析和比较,包括寻求代理、国外技术支持、国内用过的人请教等内容。 1. 了解产品的通信接口和协议 思路…...

深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...
vue3 字体颜色设置的多种方式
在Vue 3中设置字体颜色可以通过多种方式实现,这取决于你是想在组件内部直接设置,还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法: 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...

10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...

JVM 内存结构 详解
内存结构 运行时数据区: Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器: 线程私有,程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 每个线程都有一个程序计数…...

七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...

永磁同步电机无速度算法--基于卡尔曼滤波器的滑模观测器
一、原理介绍 传统滑模观测器采用如下结构: 传统SMO中LPF会带来相位延迟和幅值衰减,并且需要额外的相位补偿。 采用扩展卡尔曼滤波器代替常用低通滤波器(LPF),可以去除高次谐波,并且不用相位补偿就可以获得一个误差较小的转子位…...
comfyui 工作流中 图生视频 如何增加视频的长度到5秒
comfyUI 工作流怎么可以生成更长的视频。除了硬件显存要求之外还有别的方法吗? 在ComfyUI中实现图生视频并延长到5秒,需要结合多个扩展和技巧。以下是完整解决方案: 核心工作流配置(24fps下5秒120帧) #mermaid-svg-yP…...

ui框架-文件列表展示
ui框架-文件列表展示 介绍 UI框架的文件列表展示组件,可以展示文件夹,支持列表展示和图标展示模式。组件提供了丰富的功能和可配置选项,适用于文件管理、文件上传等场景。 功能特性 支持列表模式和网格模式的切换展示支持文件和文件夹的层…...