java初学者指南_Java代理初学者指南

java初学者指南

尽管Java初学者很快学会了键入public static void main来运行他们的应用程序,但是即使是经验丰富的开发人员也常常不知道JVM对Java流程的两个附加入口点的支持: premainagentmain方法。 这两种方法都允许所谓的Java代理在驻留在其自己的jar文件中的同时对现有的Java程序做出贡献,即使没有被主应用程序显式链接。 这样做,有可能与托管它们的应用程序完全独立地开发,发行和发布Java代理,同时仍在同一Java进程中运行它们。

最简单的Java代理先于实际应用程序运行,例如执行一些动态设置。 代理可以例如安装特定的SecurityManager或以编程方式配置系统属性。 下面的类是一个不太有用的代理,但仍然可以作为一个很好的入门演示:在将控制权传递给实际应用程序的main方法之前,该类仅将一行打印到控制台:

 <pre class= "wp-block-syntaxhighlighter-code" >package sample;  public class SimpleAgent<?> { public static void premain(String argument) { System.out.println( "Hello " + argument); }  }< /pre > 

要将此类用作Java代理,需要将其包装在jar文件中。 除常规Java程序外,无法从文件夹中加载Java代理的类。 另外,需要指定一个清单条目,该清单条目引用包含premain方法的类:

 Premain-Class: sample.SimpleAgent 

通过此设置,现在可以在命令行上添加Java代理,方法是指向捆绑代理的文件系统位置,并可以选择在等号后添加单个参数,如下所示:

java -javaagent:/location/of/agent.jar=世界some.random.Program

现在在some.random.Program执行main方法之前,将打印出Hello World ,其中第二个单词是所提供的参数。

仪表API

如果抢占式代码执行是Java代理的唯一功能,那么它们当然将没有多大用处。 实际上,大多数Java代理仅是有用的,因为Java代理可以通过将类型为Instrumentation的第二个参数添加到代理的入口点方法来请求Java代理请求。 仪表API提供对JVM提供的较低级别功能的访问,JVM是Java代理独有的,而从未提供给常规Java程序。 工具API的核心是允许在Java类加载之前或之后对其进行修改。

任何已编译的Java类都将存储为.class文件,该文件在首次加载时将作为字节数组呈现给Java代理。 通过将一个或多个ClassFileTransformer注册到检测API来通知代理,该API会针对当前JVM进程的ClassLoader加载的任何类得到通知:

 package sample;  public class ClassLoadingAgent { public static void premain(String argument, Instrumentation instrumentation) { instrumentation.addTransformer(new ClassFileTransformer() { @Override public byte[] transform(Module module, ClassLoader loader, String name, Class<?> typeIfLoaded, ProtectionDomain domain, byte[] buffer) { System.out.println( "Class was loaded: " + name); return null; } }); }  } 

在上面的示例中,代理通过从转换器返回null来保持不运行状态,这将终止转换过程,但只会将带有最新加载类的名称的消息打印到控制台。 但是,通过转换buffer参数提供的字节数组,代理可以在加载任何类之前更改其行为。

转换已编译的Java类可能听起来很复杂。 但是幸运的是, Java虚拟机规范(JVMS)详细说明了代表类文件的每个字节的含义。 为了修改一种方法的行为,因此将识别该方法代码的偏移量,然后向该方法添加所谓的Java字节代码指令,以表示所需的已更改行为。 通常,这种转换不是手动应用,而是通过使用字节码处理器(最著名的是ASM库)将类文件拆分成其组件来应用。 这样,就可以孤立地查看字段,方法和注释,从而可以应用更有针对性的转换并节省一些记账。

无干扰的代理

尽管ASM使类文件转换更安全,更简单,但它仍然依赖于库用户对字节码及其特征的良好理解。 但是,其他通常基于ASM的库允许在更高级别上表达字节码转换,这使得这种理解成为偶然。 此类库的一个示例是Byte Buddy ,它由本文的作者开发和维护。 Byte Buddy旨在将字节码转换映射到大多数Java开发人员已经知道的概念,以使代理开发更容易进行。

为了编写Java代理,Byte Buddy提供了AgentBuilder API,该API在ClassFileTransformer创建并注册ClassFileTransformer 。 字节好友ClassFileTransformer直接注册ClassFileTransformer ,而是允许指定ElementMatcher首先识别感兴趣的类型。 对于每种匹配类型,然后可以指定一个或多个转换。 然后,Byte Buddy将这些指令转换为可以安装到Instrumentation API中的转换器的高性能实现。 例如,以下代码在Byte Buddy的API中重新创建了先前的非运行转换器:

 package sample;  public class ByteBuddySampleAgent { public static void premain(String argument, Instrumentation instrumentation) { new AgentBuilder.Default() . type (ElementMatchers.any()) .transform((DynamicType.Builder<?> builder, TypeDescription type , ClassLoader loader, JavaModule module) -> { System.out.println( "Class was loaded: " + name); return builder; }).installOn(instrumentation); }  } 

应该提到的是,与前面的示例相反,Byte Buddy将转换所有发现的类型,而无需应用更改,而效率的降低则是完全忽略那些不需要的类型。 另外,如果没有另外指定,默认情况下它将忽略Java核心库的类。 但是实质上,可以实现相同的效果,从而可以使用上述代码演示使用Byte Buddy的简单代理。

使用Byte Buddy建议测量执行时间

字节伙伴不是将类文件公开为字节数组,而是尝试将常规Java代码编织或链接到已检测类中。 这样,Java代理的开发人员无需直接生成字节码,而可以依赖于Java编程语言及其已与之建立关系的现有工具。 对于使用Byte Buddy编写的Java代理,行为通常由建议类表示,其中带注释的方法描述了添加到现有方法的开头和结尾的行为。 例如,以下建议类用作模板,该模板将方法的执行时间打印到控制台:

 public class TimeMeasurementAdvice { @Advice.OnMethodEnter public static long enter() { return System.currentTimeMillis(); } @Advice.OnMethodExit(onThrowable = Throwable.class) public static void exit (@Advice.Enter long start, @Advice.Origin String origin) { long executionTime = System.currentTimeMillis() - start; System.out.println(origin + " took " + executionTime + " to execute" ); }  } 

在上面的建议类中,enter方法仅记录当前时间戳,并返回该时间戳以使其在方法末尾可用。 如图所示,输入建议在实际方法主体之前执行。 在方法结束时,将应用退出建议,在该建议中,将从当前时间戳中减去所记录的值,以确定该方法的执行时间。 然后将执行时间打印到控制台。

为了利用建议,需要将其应用在先前示例中仍未运行的变压器中。 为避免打印任何方法的运行时,我们将建议的应用程序条件MeasureTime自定义的,保留了运行时的注释MeasureTime ,应用程序开发人员可以将其添加到其类中。

 package sample;  public class ByteBuddyTimeMeasuringAgent { public static void premain(String argument, Instrumentation instrumentation) { Advice advice = Advice.to(TimeMeasurementAdvice.class); new AgentBuilder.Default() . type (ElementMatchers.isAnnotatedBy(MeasureTime.class)) .transform((DynamicType.Builder<?> builder, TypeDescription type , ClassLoader loader, JavaModule module) -> { return builder.visit(advice.on(ElementMatchers.isMethod()); }).installOn(instrumentation); }  } 

给定上述代理程序的应用程序之后,如果通过MeasureTime注释了一个类,则现在将所有方法执行时间打印到控制台。 实际上,以更结构化的方式收集此类指标当然更有意义,但是在已经完成打印输出之后,这不再是一项复杂的任务。

动态代理附件和类重新定义

在Java 8之前,这要归功于JDK的tools.jar中存储的实用程序,该实用程序可以在JDK的安装文件夹中找到。 从Java 9开始,此jar已分解到jdk.attach模块中,该模块现在可在任何常规JDK发行版中使用。 使用包含的工具API,可以使用以下代码将JAR文件附加到具有给定进程ID的JVM:

 VirtualMachine vm = VirtualMachine.attach(processId);  try { vm.loadAgent( "/location/of/agent.jar" );  } finally { vm.detach();  } 

调用上述API时,JVM将使用给定的ID定位进程,并在该远程虚拟机内的专用线程中执行agent agentmain方法。 此外,此类代理可能会要求有权在其清单中重新转换类,以更改已加载的类的代码:

 Agentmain-Class: sample.SimpleAgent  Can-Retransform-Classes: true 

给定这些清单条目后,代理现在可以请求考虑将任何已加载的类进行重新转换, ClassFileTransformer可以使用附加的布尔参数来注册先前的ClassFileTransformer ,这表明需要在重新转换尝试时得到通知:

 package sample;  public class ClassReloadingAgent { public static void agentmain(String argument, Instrumentation instrumentation) { instrumentation.addTransformer(new ClassFileTransformer() { @Override public byte[] transform(Module module, ClassLoader loader, String name, Class<?> typeIfLoaded, ProtectionDomain domain, byte[] buffer) { if (typeIfLoaded == null) { System.out.println( "Class was loaded: " + name); } else { System.out.println( "Class was re-loaded: " + name); } return null; } }, true ); instrumentation.retransformClasses( instrumentation.getAllLoadedClasses()); }  } 

为了指示已经加载了一个类,现在将已加载类的实例提供给转换器,对于之前未加载的类,该实例为null 。 在以上示例的末尾,请求仪表API获取所有已加载的类,以提交任何此类类进行重新转换,从而触发转换器的执行。 和以前一样,出于演示工具API的目的,将类文件转换器实现为不可操作。

当然,Byte Buddy还通过注册重新转换策略在其API中涵盖了这种转换形式,在这种情况下,Byte Buddy还将考虑所有要进行转换的类。 这样做,可以调整先前的时间测量代理程序,使其在动态附加的情况下也考虑加载的类:

 package sample;  public class ByteBuddyTimeMeasuringRetransformingAgent { public static void agentmain(String argument, Instrumentation instrumentation) { Advice advice = Advice.to(TimeMeasurementAdvice.class); new AgentBuilder.Default() .with(AgentBuilder.RetransformationStrategy.RETRANSFORMATION) .disableClassFormatChanges() . type (ElementMatchers.isAnnotatedBy(MeasureTime.class)) .transform((DynamicType.Builder<?> builder, TypeDescription type , ClassLoader loader, JavaModule module) -> { return builder.visit(advice.on(ElementMatchers.isMethod()); }).installOn(instrumentation); }  } 

为了最终方便,Byte Buddy还提供了一个用于附加到JVM的API,该API对JVM版本和供应商进行了抽象,以使附加过程尽可能地简单。 给定一个进程ID,Byte Buddy可以通过执行一行代码将代理附加到JVM:

 ByteBuddyAgent.attach(processId, "/location/of/agent.jar" ); 

此外,甚至可以将当前正在运行的同一虚拟机进程附加到测试代理时特别方便的进程:

 Instrumentation instrumentation = ByteBuddyAgent. install (); 

此功能可作为其自己的工件byte-buddy-agent使用 ,由于使用Instrumentation实例可以直接(例如,从一个单元中直接调用premain或agentmain方法)成为可能,因此自己尝试尝试自定义代理很简单。测试,无需任何其他设置。

翻译自: https://www.javacodegeeks.com/2019/12/a-beginners-guide-to-java-agents.html

java初学者指南

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

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

相关文章

java txt html格式_java中xml(txt/html等格式)解析问题,请教java高手,请勿粘贴其他网页上的内容(能查的都看了)。...

目的&#xff1a;通过对网页内容解析&#xff0c;获得需要的内容&#xff0c;如网页的标题Title&#xff0c;主要内容&#xff0c;描述信息&#xff1b;而里面的广告、超链接、无关紧要的信息统统不要&#xff0c;从而达到对用户上网行为的分析的目的。下面是我...目的&#xf…

java泛型程序设计——定义简单泛型类+泛型方法

【0】README 0.1&#xff09; 本文描述源代码均 转自 core java volume 1&#xff0c; 旨在理解 java泛型程序设计 的 定义泛型类泛型方法的知识&#xff1b; 【1】一个泛型类&#xff1a; 就是具有一个或多个类型变量的类&#xff1b; 1.1&#xff09;看个荔枝&#xff1a; …

分布式 虚拟时间和虚拟同步_分布式虚拟跟踪

分布式 虚拟时间和虚拟同步跟踪提供了对系统的可见性&#xff0c;使开发人员和操作人员可以在运行时观察应用程序。 当系统不断增长并与更多微服务进行交互时&#xff0c;跟踪变得非常有价值。 在这样的环境中&#xff0c;这些痕迹非常棒&#xff0c;可以定位导致性能下降的故障…

java泛型程序设计——类型变量限定 + 泛型代码和虚拟机

【0】README 0.1&#xff09; 本文描述源代码均 转自 core java volume 1&#xff0c; 旨在理解 java泛型程序设计 的 类型变量限定 泛型代码和虚拟机 的知识&#xff1b; 【1】类型变量的限定 1.1&#xff09;类和方法需要对类型变量加以限定 1.1.1&#xff09;看个荔枝&a…

java中装饰器_Java设计模式12:装饰器模式

装饰器模式装饰器模式又称为包装(Wrapper)模式。装饰器模式以多客户端透明的方式扩展对象的功能&#xff0c;是继承关系的一个替代方案。装饰器模式的结构通常给对象添加功能&#xff0c;要么直接修改对象添加相应的功能&#xff0c;要么派生子类来扩展&#xff0c;抑或是使用对…

selenium 4_Selenium4 Alpha –期望什么?

selenium 4Selenium4 Alpha-期望什么&#xff1f; 早在2018年8月&#xff0c;整个测试自动化社区就受到了一个重大新闻的打击&#xff1a;Selenium的创始成员Simon Stewart在班加罗尔Selenium会议上正式确认了Selenium 4的发布日期和一些重大更新。 世界最受欢迎的Web测试自动化…

英文论文中“such as, for example, e.g., i.e., etc., et al. ”的用法分析

【1】README 本文转自&#xff1a; http://www.cnblogs.com/lanke_2009/archive/2010/12/07/1899185.html &#xff0c; 旨在学习 英文论文中的 如 “such as, for example, e.g., i.e., etc., et al. ”的用法&#xff1b; 【2】正文如下&#xff1a; 黄龙旺  龚汉忠 (上…

mysql8.0云时代_8.0.22Mysql的详细安装

Mysql8.0.22的安装和常见问题前言提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考一、MySQL的下载和安装&#xff1f;1.mysql官网下载&#xff1a;https://dev.mysql.com/downloads/mysql/2.下载步骤&#xff1a;可以点击Go to download page 去下载32位的…

java泛型程序设计——翻译泛型表达式+翻译泛型方法

【0】README 0.1&#xff09; 本文描述源代码均 转自 core java volume 1&#xff0c; 旨在理解 java泛型程序设计 的 翻译泛型表达式翻译泛型方法 的知识&#xff1b; 【1】翻译泛型表达式 1.1&#xff09;当程序调用泛型方法时&#xff0c; 如果擦除了泛型返回类型&#xf…

redis 受攻击怎么办?_最受欢迎的6个最常用的Redis库

redis 受攻击怎么办?Redis当前是世界上最受欢迎的键值商店&#xff0c; 它通过提供高速度和低延迟以及针对应用程序开发人员的灵活功能集&#xff0c;赢得了广泛的采用率 。 Redis是一个内存中的数据结构存储&#xff0c;用作根据BSD许可分发的数据库&#xff0c;缓存和消息代…

java泛型程序设计——调用遗留代码

【0】README 0.1&#xff09; 本文描述源代码均 转自 core java volume 1&#xff0c; 旨在理解 java泛型程序设计 的 调用遗留代码 的知识&#xff1b; 【1】调用遗留代码相关 1.1&#xff09;设计java 泛型的目的&#xff1a; 允许泛型代码和遗留代码间能够相互操作&#…

java oracle 字符_Oracle转义字符

1、oracle 特殊字符 转义关键词&#xff1a; oracle 转义环境&#xff1a;oracle 9i plsql在plsql里边执行:update userinfo set pageurlmyjsp?page1&pagesize10 where idtest这条sql语句往数据库的pageurl字段放进去了一个url地址&#xff0c;但是执行的时候却并非那…

jmeter进行性能测试_使用JMeter进行性能测试

jmeter进行性能测试在开发复杂的高可用性软件项目时&#xff0c;性能至关重要。 在当今这样的时代尤其如此&#xff0c;除了闪电般的实时数据可访问性之外&#xff0c;其他任何事情都受到惩罚。 当谈论有时需要的大量数据时&#xff0c;这并非总是一件容易的事。 在本文中&…

java泛型程序设计——约束与局限性

【0】README 0.1&#xff09; 本文描述源代码均 转自 core java volume 1&#xff0c; 旨在理解 java泛型程序设计 的 约束与局限性 的知识&#xff1b; 【1】 不能用基本类型实例化类型参数 1.1&#xff09;不能用类型参数代替基本类型&#xff0c; 因此&#xff0c;没有 Pa…

python简单菜单_创建一个简单的python菜单

修正和评论&#xff0c;它们主要是语法错误。menulist 1. Print the list,2. Add a name to the list,3. Remove a name from the list,4. Change an item in the list,9. Quit #assuming you want to display menulist, having it as a tuple is uselesslst("johny"…

郎溪 溪流_到无限(溪流)和超越!

郎溪 溪流Java允许您处理集合或流中的数据。 将流视为将一个集合转换为另一个集合的技术非常容易。 这可能会导致一些相当随意的代码&#xff0c;其中流数据被重复收集到某种类型的集合中&#xff0c;作为一个整体集合传递&#xff0c;然后再进行更多处理。 对于6个元素&#…

java泛型程序设计——Varargs 警告+不能实例化类型变量

【0】README 0.1&#xff09; 本文描述源代码均 转自 core java volume 1&#xff0c; 旨在理解 java泛型程序设计 的 Varargs 警告不能实例化类型变量 的知识&#xff1b; 【1】 Varargs 警告 1.1&#xff09;一个相关问题&#xff1a; 向参数个数可变的方法传递一个泛型类型…

rabbitmq怎么停止_Windows环境下RabbitMQ的启动和停止命令

Windows环境下RabbitMQ的启动和停止命令原创lockie_zou 最后发布于2018-05-24 15:34:21 阅读数 36514 收藏展开首先windows下安装好了erlang和rabbitmq。如下地址同时下载和安装&#xff1a;Erlang&#xff1a;http://www.erlang.org/download.htmlRabbitMQ &#xff1a;http:…

elk入门_ELK堆栈入门

elk入门朋友不允许朋友写用户身份验证。 厌倦了管理自己的用户&#xff1f; 立即尝试Okta的API和Java SDK。 数分钟之内即可在任何应用程序中对用户进行身份验证&#xff0c;管理和保护。 好的设计原则要求微服务架构是可观察的&#xff0c;并提供集中的监视工具。 该工具使开…

java泛型程序设计——泛型类的静态上下文中类型变量无效+不能抛出或捕获泛型类的实例

【0】README 0.1&#xff09; 本文描述源代码均 转自 core java volume 1&#xff0c; 旨在理解 java泛型程序设计 的 泛型类的静态上下文中类型变量无效不能抛出或捕获泛型类的实例 的知识&#xff1b; 【1】泛型类的静态上下文中类型变量无效 1.1&#xff09;不能在静态域或…