聊聊那些年我们实现java AOP几种常见套路

news2024/10/7 20:31:07

前言

有一定开发经验的同学对AOP应该很了解吧,如果不了解,可以先查看如下文章进行科普一下https://baike.baidu.com/item/AOP/1332219?fr=aladdin,再来阅读本文。

示例前置准备

注: 本示例基于springboot进行演示

1、在项目pom引入aop的GAV

   <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

2、编写业务服务

@Service
public class EchoService {

    @CostTimeRecoder
    public void echo(String message){
        System.out.println("echo ->" + message);
    }

}

3、编写aspect切面

@Aspect
public class EchoAspect {

    @Before(value = "execution(* com.github.lybgeek.aop.service.EchoService.echo(..))")
    public void before(JoinPoint joinPoint){
        System.out.println("USE AOP BY ASPECT WITH ARGS: " + Arrays.toString(joinPoint.getArgs()));

    }
}

实现AOP的常见套路

1、在编译期阶段实现AOP

方法一:通过aspectj-maven-plugin插件在编译期进行织入

在项目的pom引入如下内容

<build>
  <plugins>
    <plugin>
      <artifactId>maven-surefire-plugin</artifactId>
      <version>2.22.2</version>
    </plugin>
    <plugin>
      <groupId>com.nickwongdev</groupId>
      <artifactId>aspectj-maven-plugin</artifactId>
      <version>1.12.6</version>
      <configuration>
        <complianceLevel>${java.version}</complianceLevel>
        <source>${java.version}</source>
        <target>${java.version}</target>
        <encoding>${project.encoding}</encoding>
      </configuration>
      <executions>
        <execution>
          <goals>
            <goal>compile</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>
<dependencies>
  <dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.9.5</version>
  </dependency>
</dependencies>

通过执行如下maven命令 ,进行项目编译

mvn clean compile

执行测试类

public class AspectMavenPluginMainTest {

    public static void main(String[] args) {
      EchoService.echo("aspectMavenPlugin");
    }
}

发现切面已经执行。我们在查看下生成的EchoService.class文件有没有发生什么变化

public class EchoService {
    public EchoService() {
    }

    public static final void echo(String message) {
        JoinPoint var1 = Factory.makeJP(ajc$tjp_0, (Object)null, (Object)null, message);
        EchoAspect.aspectOf().before(var1);
        System.out.println("echo ->" + message);
    }

    static {
        ajc$preClinit();
    }
}

发现多了一些切面的内容。

注: 本示例利用别人重新封装的插件,而非Codehaus的官方提供的插件,Codehaus的官方提供的插件只能支持JDK8(包含JDK8)以下的版本,而本示例的插件可以支持到JDK13

本示例的插件github地址:https://github.com/nickwongdev/aspectj-maven-plugin

Codehaus的官方插件地址:https://github.com/mojohaus/aspectj-maven-plugin
以及相应介绍:https://www.mojohaus.org/aspectj-maven-plugin/index.html

方法二:利用APT + JavaPoet 在编译期实现切面逻辑

如果对于APT不了解的小伙伴,可以查看我之前的文章聊聊如何运用JAVA注解处理器(APT)

而JavaPoet是JavaPoet 是生成 .java 源文件的 Java API,具体查看官方文档
https://github.com/square/javapoet
或者查看此博文
https://weilu.blog.csdn.net/article/details/112429217

不过JavaPoet 只能生产新的代码,无法对原有的代码进行修改。因此在演示此方法时,本文就通过生成一个继承EchoService的子类,来实现AOP功能

生成的子类如下

public final class LybGeekEchoServiceCostTimeRecord extends EchoService {
    public LybGeekEchoServiceCostTimeRecord() {
    }

    public final void echo(String message) {
        long startTime = System.currentTimeMillis();
        super.echo(message);
        long costTime = System.currentTimeMillis() - startTime;
        System.out.println("costTime : " + costTime + "ms");
    }
    }

注: 因为JavaPoet 是通过生成新代码,而非进行在源代码进行插桩,因此也不是很符合我们我要求

方法三:利用APT+AST在编译期进行织入

AST抽象语法树,可以在编译期对字节码进行修改,达到插桩的效果。因之前我有写过一篇文章
聊聊如何通过APT+AST来实现AOP功能

本示例就不贴相应的代码了

2、在JVM进行类加载时进行AOP

核心是用利用aspectjweaver在JVM进行类加载时进行织入。具体实现步骤如下

1、在项目的POM引入aspectjweaver GAV

<dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.5</version>
        </dependency>

2、创建切面类和需要被织入的目标类

即示例前置准备的内容

3、在src/main/resource目录下创建META-INF/aop.xml文件

<aspectj>
    <weaver options="-XnoInline -Xset:weaveJavaxPackages=true -Xlint:ignore -verbose -XmessageHandlerClass:org.springframework.aop.aspectj.AspectJWeaverMessageHandler">
        <!--在编织时导入切面类和需要被切入的目标类-->
        <include within="com.github.lybgeek.aop.aspect.EchoAspect"/>
        <include within="com.github.lybgeek.aop.service.EchoService"/>
    </weaver>
    <aspects>
        <!--指定切面类-->
        <aspect name="com.github.lybgeek.aop.aspect.EchoAspect"/>
    </aspects>
</aspectj>

4、指定VM参数

-javaagent:aspectjweaver.jar的路径
示例:
-javaagent:D:\repository\org\aspectj\aspectjweaver\1.9.5\aspectjweaver-1.9.5.jar

5、测试

public class AspectjweaverMainTest {

    public static void main(String[] args) {
        EchoService echoService = new EchoService();
        echoService.echo("Aspectjweaver");
    }
}


查看控制台

3、在运行时进行AOP

我们以spring aop为例

1、手动代理(直接使用底层API)

主要是利用AspectJProxyFactory 、ProxyFactoryBean 、ProxyFactory

public class AopApiTest {

    @Test
    public void testAopByAspectJProxyFactory(){
        AspectJProxyFactory aspectJProxyFactory = new AspectJProxyFactory(new EchoService());
        aspectJProxyFactory.addAspect(EchoAspect.class);
        EchoService echoService = aspectJProxyFactory.getProxy();
        echoService.echo("AspectJProxyFactory");
    }

    @Test
    public void testAopByProxyFactoryBean(){
        ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
        proxyFactoryBean.setTarget(new EchoService());

        AspectJExpressionPointcutAdvisor aspectJExpressionPointcutAdvisor = new AspectJExpressionPointcutAdvisor();
        aspectJExpressionPointcutAdvisor.setExpression("execution(* com.github.lybgeek.aop.service.EchoService.echo(..))");
        aspectJExpressionPointcutAdvisor.setAdvice((MethodBeforeAdvice) (method, args, target) -> System.out.println("USE AOP BY ASPECT WITH ARGS: " + Arrays.toString(args)));
        proxyFactoryBean.addAdvisor(aspectJExpressionPointcutAdvisor);

        EchoService echoService = (EchoService) proxyFactoryBean.getObject();
        echoService.echo("ProxyFactoryBean");

    }

    @Test
    public void testAopByProxyFactory(){
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTarget(new EchoService());

        AspectJExpressionPointcutAdvisor aspectJExpressionPointcutAdvisor = new AspectJExpressionPointcutAdvisor();
        aspectJExpressionPointcutAdvisor.setExpression("execution(* com.github.lybgeek.aop.service.EchoService.echo(..))");
        aspectJExpressionPointcutAdvisor.setAdvice((MethodBeforeAdvice) (method, args, target) -> System.out.println("USE AOP BY ASPECT WITH ARGS: " + Arrays.toString(args)));
        proxyFactory.addAdvisor(aspectJExpressionPointcutAdvisor);

        EchoService echoService = (EchoService) proxyFactory.getProxy();
        echoService.echo("ProxyFactory");



    }


2、自动代理

这个是我们平时用得最多的。自动代理常见实现手段就是在spring bean ioc阶段的后置处理器阶段进行增强

示例

@Configuration
public class AopConfig {

    @Bean
    public EchoAspect echoAspect(){
        return new EchoAspect();
    }


}

因为自动代理太常见了,java开发必备技能,就不多做介绍了

总结

本文主要从编译期,JVM加载器期、运行期这三个环节,来讲述如何进行AOP。如果对性能有强烈要求的话,推荐在编译期或者JVM加载期进行织入。如果想对方法修饰符为final、static、private进行织入,也可以考虑在编译期进行实现。不过在编译期或者JVM加载期进行织入有个弊端就是,出现问题不好排查。如果不是对性能有极致要求的话,推荐在运行时,进行AOP进行切入,主要是出现问题,相对好排查。有时候基于业务角度而非技术角度,进行权衡,可能会得出意想不到的效果

demo链接

https://github.com/lyb-geek/springboot-learning/tree/master/springboot-aop

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/509484.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

将ABC文件 通过BlendShape导出为FBX>

将ABC文件 通过BlendShape导出为FBX 一、应用场景&#xff1a; 此项目为高中化学实验案例&#xff0c;为实现保鲜膜 模拟动画&#xff0c;这里通过使用MarvelousDesigner来结算出动画效果&#xff0c;导出ABC格式带动画后&#xff0c;导入到maya当中&#xff0c;这里因为需要…

SSM框架学习-注解开发定义bean

注解开发定义的bean和xml文件定义的bean有以下区别&#xff1a; 配置方式不同&#xff1a;注解方式是直接在Java类中使用注解来定义bean&#xff0c;而XML方式则是在XML文件中配置bean。 配置信息不同&#xff1a;注解方式在注解内配置bean的属性&#xff0c;如Value&#xff…

springboot整合ES

也可以直接看到最后&#xff0c;直接看到最后&#xff0c;中间都是废话废话废话&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;好气啊 1.刚下载完成,输入localhost:9200就报错&#xff0c;整个人都不太好了 [2023-05-10T14:35:59,002][WARN ][o.…

DC域控服务器与辅助DC域控服务器创建

DC域控服务器与辅助DC域控服务器创建 一、准备条件 在虚拟机上准备三台Windows Server 2008 R2 ,一台作为主域控&#xff0c;一台作为额外域控辅域控&#xff0c;一台作为客户端。 主域控 的IP地址为--192.168.1.190, 注意&#xff1a;(Windows Server 2003 需要 设置 DNS为127…

烽火HG680KA-Hi3798MV310-当贝纯净桌面-卡刷固件包

烽火HG680KA-Hi3798MV310-当贝纯净桌面-卡刷固件包-内有教程 特点&#xff1a; 1、适用于对应型号的电视盒子刷机&#xff1b; 2、开放原厂固件屏蔽的市场安装和u盘安装apk&#xff1b; 3、修改dns&#xff0c;三网通用&#xff1b; 4、大量精简内置的没用的软件&#xff…

pytorch搭建EfficientnetV2网络

文章目录 前言一、EfficientnetV2二、网络结构1.Fused_MBConv2.MBConv 三、整体代码总结 论文地址&#xff1a;https://arxiv.org/abs/2104.00298 官方代码&#xff1a;https://github.com/google/automl/tree/master/efficientnetv2 参考链接&#xff1a;https://blog.csdn.ne…

经典:DotNetBar Suite UI 7.9 for WPF Crack

创建专业的 WPF 应用程序 DotNetBar Suite for WPF 是超过 38 个本机 Windows Presentation Foundation 控件的工具箱&#xff0c;用于创建专业的 WPF 应用程序。 Office 2016 类样式添加到功能区、日程安排和其他控件... 我们痴迷于控制性能和像素级细节。我们很自豪地说&…

2023年杭州助理工程师职称申报评审流程是什么呢?社保单位不一致怎么办?

助理工程师证&#xff0c;又称为初级工程证或者初级职称。助理工程师&#xff0c;是指初级工程技术人员的职务名称。有了助理工程师证你可以评中级工程师证&#xff0c;也可以应聘、在职、上岗、加薪、企业升资质和招投标都用的到。助理工程师证是评审获得的&#xff0c;评审报…

如何正确使用 Facebook 反链,增强网站在搜索引擎中的曝光度

在当今数字化时代&#xff0c;拥有一个强大的在线存在感是企业成功的关键之一。而在建立有效的在线存在感时&#xff0c;搜索引擎优化&#xff08;SEO&#xff09;扮演着重要的角色。而其中一个重要的SEO策略是利用反链来增强网站在搜索引擎中的曝光度。 然而&#xff0c;许多…

【服务器数据恢复】HP双循环Raid5磁盘阵列数据恢复案例

服务器数据恢复环境&#xff1a; 一台HP DL系列服务器&#xff0c;通过hp smart array控制器挂载一台磁盘阵列设备&#xff0c;作为公司内部的文件服务器使用&#xff1b; 该磁盘阵列设备中有一组由十几块SCSI硬盘组建的RAID5&#xff1b; 上层安装LINUX操作系统并部署了NFSFTP…

结算更高效,成本更节省,风控更全面,用友银企联助力万家企业加速数字变革

数字经济蓬勃发展的当下&#xff0c;在业绩增长和管理提效的双重压力下&#xff0c;企业纷纷投身于数字化、智能化转型升级&#xff0c;通过大数据及新一代人工智能技术寻求产业变革&#xff0c;以实现企业业务创新与管控升级。银企联作为企业与银行信息交互的通道&#xff0c;…

SpringBoot——pom文件:parent

先看一看&#xff1a; 本次我们主要介绍SpringBoot的文件&#xff0c;先来看一看里面都有什么内容&#xff1a; <?xml version"1.0" encoding"UTF-8"?> <!--XML文件的抬头--> <!--一些约束以及明明空间信息--> <project xmlns&qu…

知识图谱实现全域数据资产智能管理与运营

案例名称 基于知识图谱的全域数据资产智能管理与运营 案例简介 该方案通过数据资产元数据构建引擎、列算子血缘引擎、关系挖掘引擎和数据资产目录挂载引擎的部署&#xff0c;可快速实现金融机构数据资产的业务目录分类以及数据资产标签集合建设。通过可视化引擎管…

Ubuntu配置Samba服务

Ubuntu配置Samba服务 一、安装samba二、配置samba服务器三、win系统配置四、检查你的虚拟机五、注意 一、安装samba 前提&#xff1a;已经换好源&#xff0c;不然下载很慢或者不成功 未换执行以下命令 sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak sudo vim/etc…

虹科案例 | 使用PRP协议解决广播行业中实现高可用性和亚微秒同步难题

案例背景 BROADCASTING PUBLIC ENTITY主要为用户提供电视频道和广播频道&#xff0c;为了带来更好的视听体验&#xff0c;该公司必须更新其无线电前端系统的基础设施。前端的主要功能是接收来自广播电台的现场音频并将其编码为压缩格式&#xff0c;例如AAC&#xff0c;然后将多…

unity 完全复刻flappy bird

文章目录 一、 介绍制作bird向右移动的效果基本动画转场渐隐效果dotween 平滑摇头效果柱子控制器碰撞检测下载项目文件 一、 介绍 Flappy Bird是一款由越南开发者Dong Nguyen于2013年发布的2D跳跃游戏。玩家需要控制一只小鸟躲避障碍物&#xff0c;通过不断飞行获得分数。游戏…

如果把ChatGPT和“挖呀挖”的黄老师结合起来,她可以为你做什么事情?

ChatGPT曾经2个月用户过亿的事情已成为过去&#xff0c;虽然我也成为了其中的一份子&#xff0c;感受着他的无所不能&#xff0c;但从中也的确发现了他的一些不能做的事情。而近期爆火的“挖呀挖”的黄老师&#xff0c;几天粉丝疯涨几百万&#xff0c;也的确值得我们思考。 那么…

kafka安装及环境搭建

1. 下载 下载地址&#xff1a;Apache Kafka 我这里下载的是 3.2.1 版本。 2. 上传并解压 上传到 linux 下的 /home/software/ 目录下&#xff0c;然后解压 kafka_2.13-3.2.1.tgz 包到/usr/local/ cd /home/software tar -zxvf kafka_2.13-3.2.1.tgz -C /usr/local # -C 选…

odoo的一些基础概念

概述 三层体系结构&#xff0c;表示层是HTML5、JavaScript和CSS的组合&#xff0c;逻辑层专门用Python编写&#xff0c;而数据层只支持PostgreSQL作为RDBMS。 服务器和客户端扩展都打包为模块&#xff0c;可选地加载到数据库中。模块是针对单一目的的函数和数据的集合。Odoo中…

拉取远程分支到本地修改后上传

在git之前最好保证网络通畅 如果之前本地有项目且有git控制&#xff0c;直接更新就行了 git pull 1、拉取仓库 git clone https://github.com/用户名/仓库名.git 2、对项目进行修改(略) 3、将所有更改添加到暂存区 git add . git add .命令将所有更改添加到暂存区&#…