当前位置: 首页 > news >正文

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 也可以定义在构造方法上,定义在构造方法就为该类实现了单实例缓存

字段名默认值说明
debugOutputfalse是否输出调试信息
debugOutputDetailfalse是否输出详细调试信息
weakKeysfalse指定存储在缓存中的每个键(而不是值)都应封装在WeakReference中(默认情况下,使用强引用)。
weakValuesfalse指定存储在缓存中的每个值(而不是键)都应包装在SoftReference中(默认情况下,使用强引用)。软引用的对象将以全局最近最少使用的方式进行垃圾收集,以响应内存需求。
softValuesfalse指定存储在缓存中的每个值(而不是键)都应包装在SoftReference中(默认情况下,使用强引用)。软引用的对象将以全局最近最少使用的方式进行垃圾收集,以响应内存需求。
initialCapacity-1设置内部哈希表的最小总大小。例如,如果初始容量为60,并发级别为8,则创建八个段,每个段都有一个大小为8的哈希表。在构建时提供足够大的估计值可以避免以后进行昂贵的调整大小操作,但不必设置此值—过高会浪费内存。
concurrencyLevel0指定更新操作之间允许的并发性。用作内部大小调整的提示。该表在内部进行了分区,以尝试在没有争用的情况下允许指定数量的并发更新。
maximumSize-1指定缓存可以包含的最大条目数。请注意,缓存可能会在超过此限制之前收回一个条目。随着缓存大小接近最大值,缓存会收回不太可能再次使用的条目。例如,缓存可能会因为最近或不经常使用而被逐出。
maximumWeight-1指定缓存中可能包含的项的最大权重。重量是使用地磅指定的秤来确定的,使用这种方法需要在调用构建之前对地磅进行响应调用。
expireAfterWrite-1指定在创建条目或最近替换其值后经过固定的持续时间后,应自动从缓存中删除每个条目。
expireAfterWriteTimeUnitMINUTES(分钟)expireAfterWrite的时间单位
expireAfterAccess-1指定在创建条目、最近一次替换其值或最后一次访问之后经过固定的持续时间后,应自动从缓存中删除每个条目。访问时间由所有缓存读取和写入操作(包括cache.asMap().get(Object)和cache.asMop().put(K,V))重置,但不由cache.asMap的集合视图上的操作重置。
expireAfterAccessTimeUnitMINUTES(分钟)expireAfterAccess的时间单位
refreshAfterWrite-1指定活动条目的创建或其值的最新替换后经过固定的持续时间后,活动条目的自动刷新条件。刷新的语义在LoadingCache.refresh中指定,并通过调用CacheLoader.reload来执行。
由于CacheLoader.reload的默认实现是同步的,建议此方法的用户使用异步实现覆盖CacheLoader.reload;否则将在不相关的缓存读取和写入操作期间执行刷新。
当前自动刷新是在对条目的第一个过时请求发生时执行的。触发刷新的请求将对CacheLoader.reload进行阻塞调用,如果返回的future完成,则立即返回新值,否则返回旧值。
refreshAfterWriteTimeUnitMINUTES(分钟)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实现的方法缓存工具

背景 最近一直在做一些服务端的设计&#xff0c;经常遇到常量计算的问题&#xff0c;比如获取查找一个类的所有方法&#xff0c;获取有指定注解(Annnotation)的方法并查找注解的上特定的元注解是否有特定的值 。。。。总之逻辑很复杂&#xff0c;而且会频繁调用。 比如在服务端…...

UE4_材质_湿度着色器及Desaturation算法_ben材质教程

学习笔记&#xff0c;不喜勿喷&#xff01;侵权立删&#xff0c;祝愿美好生活越来越好。 效果图&#xff1a; 原图&#xff1a; 1、使用初学者内容包的材质 我们这里使用虚幻自带的材质M_Brick_Clay_Old,复制一个更名为M_Brickclayoldwet材质。 2、添加去饱和度Desaturation节…...

AI问答-ERP:理解 ERP / 我国ERP发展现状 / ERP软件有哪些 / 华为自研ERP

一、理解ERP 1.1、定义 ERP&#xff08;Enterprise Resource Planning&#xff09;是企业资源计划的缩写&#xff0c;它集成了企业各个业务领域&#xff0c;包括采购、销售、库存、生产制造、财务等多个方面&#xff0c;进行全面管理、智能决策的一种企业管理系统。 1.2、功…...

C语言 | Leetcode C++题解之第199题二叉树的右视图

题目&#xff1a; 题解&#xff1a; #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的基本使用&#xff0c; 介绍AoCacheable注解时说过&#xff0c;AoCacheable可以定义在构造方法上&#xff0c;定义在构造方法&#xff0c;该构建方法就成了单实例模式。 也就是说&#xff0c;只要构建…...

c++11 abi 兼容性

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

获取个人免费版Ubuntu Pro

首先上官网地址&#xff1a;Ubuntu Pro | Ubuntu 点击页面中的"Get Ubuntu Pro now" 将用途选为“Myself”&#xff0c;在此页面中Ubuntu说明了该版本只面向个人开发者&#xff0c;且最终只允许5台设备免费使用&#xff1b;因而部署设备的抉择就不得不慎重考虑了&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:将您的桌面变成生动的画布

您是否厌倦了静态的桌面背景&#xff1f;Stardock 的 DeskScapes 软件赋予您将任何图片或视频动画化的能力&#xff0c;让您的 Windows 桌面焕发活力。 动画桌面&#xff0c;艺术生活 使用 DeskScapes 您可以将任何静态图片或视频转化为桌面背景。不仅如此&#xff0c;通过 60 …...

OpenCV cv::Mat到 Eigen 的正确转换——cv2eigen

在进行计算机视觉项目时&#xff0c;我们经常需要处理相机位姿的变换。最近&#xff0c;我在项目中遇到了一个看似简单但实际上颇具挑战性的问题&#xff1a;从 OpenCV 的 cv::Mat 格式转换到 Eigen 库的格式。这个过程中遇到了一些问题&#xff0c;但最终找到了一个稳健的解决…...

PostgreSQL的扩展(extensions)-常用的扩展-pg_pathman

PostgreSQL的扩展&#xff08;extensions&#xff09;-常用的扩展-pg_pathman pg_pathman 是一个用于 PostgreSQL 的分区管理扩展。它提供了一种高效的方式来管理和使用数据库分区&#xff0c;可以显著提升查询性能&#xff0c;特别是在处理大规模数据集时。 安装 pg_pathman…...

数据结构之树

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

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事务&#xff08;Long Lived Transaction&#xff09;可以分成若干个小的事务执行单元&#xff0c;这些小执行单元就是saga事务。Saga方案更适合用于长事务场景。Saga模型将一个分布式事务拆分为多个本…...

深度神经网络:解锁智能的密钥

深度神经网络&#xff1a;解锁智能的密钥 在人工智能的浩瀚星空中&#xff0c;深度神经网络&#xff08;Deep Neural Networks, DNNs&#xff09;无疑是最耀眼的那颗星。它以其强大的学习能力、高度的适应性和广泛的应用场景&#xff0c;成为了我们解锁智能世界的一把密钥。本…...

国际现货黄金最新价格如何分析?结合较高的时间周期

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

微服务和kafka

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

Jetpack架构组件_Navigaiton组件_1.Navigaiton切换Fragment

1.Navigation主要作用 方便管理Fragment &#xff08;1&#xff09;方便我们管理Fragment页面的切换 &#xff08;2&#xff09;可视化的页面导航图&#xff0c;便于理清页面间的关系。 &#xff08;3&#xff09;通过destination和action完成页面间的导航 &#xff08;4&a…...

[计算机网络] 虚拟局域网

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

LabVIEW遇到无法控制国外设备时怎么办

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

<6>-MySQL表的增删查改

目录 一&#xff0c;create&#xff08;创建表&#xff09; 二&#xff0c;retrieve&#xff08;查询表&#xff09; 1&#xff0c;select列 2&#xff0c;where条件 三&#xff0c;update&#xff08;更新表&#xff09; 四&#xff0c;delete&#xff08;删除表&#xf…...

线程与协程

1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指&#xff1a;像函数调用/返回一样轻量地完成任务切换。 举例说明&#xff1a; 当你在程序中写一个函数调用&#xff1a; funcA() 然后 funcA 执行完后返回&…...

linux arm系统烧录

1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 &#xff08;忘了有没有这步了 估计有&#xff09; 刷机程序 和 镜像 就不提供了。要刷的时…...

Matlab | matlab常用命令总结

常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...

Ascend NPU上适配Step-Audio模型

1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统&#xff0c;支持多语言对话&#xff08;如 中文&#xff0c;英文&#xff0c;日语&#xff09;&#xff0c;语音情感&#xff08;如 开心&#xff0c;悲伤&#xff09;&#x…...

Python如何给视频添加音频和字幕

在Python中&#xff0c;给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加&#xff0c;包括必要的代码示例和详细解释。 环境准备 在开始之前&#xff0c;需要安装以下Python库&#xff1a;…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列&#xff0c;以便知晓哪些列包含有价值的数据&#xff0c;…...

【Java学习笔记】BigInteger 和 BigDecimal 类

BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点&#xff1a;传参类型必须是类对象 一、BigInteger 1. 作用&#xff1a;适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...

#Uniapp篇:chrome调试unapp适配

chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器&#xff1a;Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...