java代理模式_Java代理

java代理模式

本文是我们名为“ 高级Java ”的学院课程的一部分。

本课程旨在帮助您最有效地使用Java。 它讨论了高级主题,包括对象创建,并发,序列化,反射等。 它将指导您完成Java掌握的旅程! 在这里查看 !

目录

1.简介 2. Java代理基础 3. Java代理和规范 4.编写您的第一个Java代理 5.运行Java代理 6.接下来 7.下载源代码

1.简介

在本教程的最后一部分中,我们将讨论Java代理,这对于在那里的常规Java开发人员是一个真正的魔咒。 通过执行字节码的直接修改,Java代理能够“侵入”运行时在JVM上运行的Java应用程序的执行。 Java代理的功能和危险一样强大:它们几乎可以执行所有操作,但是如果出现问题,它们很容易使JVM崩溃。

这部分的目的是通过解释Java代理如何工作,如何运行它们以及展示一些简单的示例来揭开Java代理的神秘面纱,Java代理显然具有优势。

2. Java代理基础

本质上,Java代理是遵循一组严格约定的常规Java类。 代理类必须实现一个public static void premain(String agentArgs, Instrumentation inst)方法,该方法成为代理的入口点(类似于常规Java应用程序的main方法)。

初始化Java虚拟机(JVM)后,将按照在JVM启动时指定代理的顺序调用每个代理的每个此类premain(String agentArgs, Instrumentation inst)方法。 完成此初始化步骤后,将调用真实的Java应用程序main方法。

但是,如果该类未实现public static void premain(String agentArgs, Instrumentation inst)方法,则JVM将尝试查找并调用另一个重载版本的public static void premain(String agentArgs) 。 请注意,每个premain方法必须返回才能启动阶段。

最后但并非最不重要的一点是,Java代理类还可以具有在JVM启动后启动代理时使用的public static void agentmain(String agentArgs, Instrumentation inst)public static void agentmain(String agentArgs)方法。

乍看之下看起来很简单,但Java代理实现还应提供其他一些内容作为其包装的一部分:清单。 清单文件通常位于META-INF文件夹中,名为MANIFEST.MF ,包含与包分发有关的各种元数据。

我们在本教程中并未讨论清单,因为大多数时候它们都不是必需的,但是Java代理不是这种情况。 为打包为Java归档(或简称JAR)文件的Java代理定义了以下属性:

清单属性 描述
初级班 在JVM启动时指定了代理时,此属性定义Java代理类:包含premain方法的类。 在JVM启动时指定代理时,此属性是必需的。 如果该属性不存在,JVM将中止。
代理级 如果实现支持在JVM启动后的某个时间启动Java代理的机制,则此属性指定代理类:包含agentmain方法的类。 此属性是必需的,如果不存在该代理,则不会启动代理。
引导类路径 引导类加载器要搜索的路径列表。 路径代表目录或库。
可以重新定义类 truefalse值,不区分大小写,并且定义是否具有重新定义此代理所需的类的能力。 此属性是可选的,默认值为false
可以重新转换类 truefalse值,不区分大小写,并且定义是否具有重新转换此代理所需的类的能力。 此属性是可选的,默认值为false
可以设置本机方法前缀 truefalse值,不区分大小写,并且定义是否可以设置此代理所需的本机方法前缀。 此属性是可选的,默认值为false

有关更多详细信息,请随时查阅专用于Java代理和工具的官方文档 。

3. Java代理和规范

Java代理的检测功能确实是无限的。 最引人注意的包括但不限于:

  • 能够在运行时重新定义类。 重新定义可能会更改方法主体,常量池和属性。 重新定义不得添加,删除或重命名字段或方法,更改方法的签名或更改继承。
  • 能够在运行时重新转换类。 重新转换可能会更改方法主体,常量池和属性。 重新转换不得添加,删除或重命名字段或方法,更改方法的签名或更改继承。
  • 通过允许重命名使用前缀来修改本机方法解析的失败处理的能力。

请注意,在应用转换或重新定义后,不会检查,验证和安装重新转换或重新定义的类字节码。 如果生成的字节码错误或不正确,则将引发异常,这可能会使JVM完全崩溃。

4.编写您的第一个Java代理

在本节中,我们将通过实现我们自己的类转换器来编写一个简单的Java代理。 话虽如此,使用Java代理的唯一缺点是,为了完成或多或少的有用转换,需要直接字节码操作技能。 而且,不幸的是,Java标准库没有提供任何API(至少是有文档的API)来使这些字节码操作成为可能。

为了填补这一空白,富有创造力的Java社区提出了一些优秀的,非常成熟的库,例如Javassist和ASM ,仅举几例。 在这两种方法中,Javassist使用起来更简单,这就是为什么它成为我们将要用作字节码操作解决方案的原因。 到目前为止,这是我们第一次无法在Java标准库中找到合适的API,除了使用社区提供的API之外别无选择。

我们将要处理的示例相当简单,但它取自于实际的用例。 假设我们要捕获Java应用程序打开的每个HTTP连接的URL。 有很多方法可以通过直接修改Java源代码来做到这一点,但让我们假设由于许可证策略或其他原因导致源代码不可用。 打开HTTP连接的类的典型示例如下所示:

public class SampleClass {public static void main( String[] args ) throws IOException {fetch("http://www.google.com");fetch("http://www.yahoo.com");}private static void fetch(final String address) throws MalformedURLException, IOException {final URL url = new URL(address);                final URLConnection connection = url.openConnection();try( final BufferedReader in = new BufferedReader(new InputStreamReader( connection.getInputStream() ) ) ) {String inputLine = null;final StringBuffer sb = new StringBuffer();while ( ( inputLine = in.readLine() ) != null) {sb.append(inputLine);}       System.out.println("Content size: " + sb.length());}}
}

Java代理非常适合解决此类挑战。 我们只需要定义一个转换器,即可通过注入代码以将输出生成到控制台来稍微修改sun.net.www.protocol.http.HttpURLConnection构造函数。 听起来很吓人,但是使用ClassFileTransformer和Javassist非常简单。 让我们看一下这样的转换器实现:

public class SimpleClassTransformer implements ClassFileTransformer {@Overridepublic byte[] transform( final ClassLoader loader, final String className,final Class<?> classBeingRedefined, final ProtectionDomain protectionDomain,final byte[] classfileBuffer ) throws IllegalClassFormatException {if (className.endsWith("sun/net/www/protocol/http/HttpURLConnection")) {try {final ClassPool classPool = ClassPool.getDefault();final CtClass clazz = classPool.get("sun.net.www.protocol.http.HttpURLConnection");for (final CtConstructor constructor: clazz.getConstructors()) {constructor.insertAfter("System.out.println(this.getURL());");}byte[] byteCode = clazz.toBytecode();clazz.detach();return byteCode;} catch (final NotFoundException | CannotCompileException | IOException ex) {ex.printStackTrace();}}return null;}
}

ClassPool和所有CtXxx类( CtClassCtConstructor )都来自Javassist库。 我们所做的转换是非常幼稚的,但此处仅用于演示目的。 首先,由于我们仅对HTTP通信感兴趣,因此sun.net.www.protocol.http.HttpURLConnection是来自标准Java库的类。

请注意,而不是“。” 分隔符, className带有“ /”。 其次,我们寻找HttpURLConnection类,并通过注入System.out.println(this.getURL());修改其所有构造函数System.out.println(this.getURL()); 最后的声明。 最后,我们返回了该类转换后的版本的新字节码,因此它将由JVM使用,而不是原始版本。

这样,Java代理的premain方法的作用就是将SimpleClassTransformer类的实例SimpleClassTransformer到检测上下文中:

public class SimpleAgent {public static void premain(String agentArgs, Instrumentation inst) {final SimpleClassTransformer transformer = new SimpleClassTransformer();inst.addTransformer(transformer);}
}

而已。 看起来很容易,同时又有些令人恐惧。 为了完成Java代理,我们必须提供适当的MANIFEST.MF,以便JVM能够选择正确的类。 这是必需属性的相应最小集合(有关更多详细信息,请参阅Java Agent Basics部分):

Manifest-Version: 1.0
Premain-Class: com.javacodegeeks.advanced.agent.SimpleAgent

这样一来,首先的Java代理就准备好进行一场真正的战斗。 在本教程的下一部分中,我们将介绍一种与Java应用程序一起运行Java代理的方法。

5.运行Java代理

从命令行运行时,可以使用具有以下语义的-javaagent参数将Java代理传递到JVM实例:

-javaagent:<path-to-jar>[=options]

其中<path-to-jar>是查找Java代理JAR归档文件的路径,而options包含可以通过agentArgs参数更准确地传递给Java代理的其他选项。 例如,从编写您的第一个Java代理 (使用Java 7版本)部分运行我们的Java代理的命令行如下所示(假定代理JAR文件位于当前文件夹中):

java -javaagent:advanced-java-part-15-java7.agents-0.0.1-SNAPSHOT.jar

advanced-java-part-15-java7.agents-0.0.1-SNAPSHOT.jar Java代理一起运行SampleClass类时,该应用程序将在控制台上打印所有URL( Google和Yahoo! )。尝试使用HTTP协议进行访问(其次是Google和Yahoo!搜索主页的内容大小):

http://www.google.com
Content size: 20349
http://www.yahoo.com
Content size: 1387

在未指定Java代理的情况下运行相同的SampleClass类将仅在控制台上输出内容大小,而不输出URL(请注意,内容大小可能会有所不同):

Content size: 20349
Content size: 1387

JVM使运行Java代理变得简单。 但是,请注意,任何错误或不正确的字节码生成都可能使JVM崩溃,并可能丢失此时您的应用程序可能保存的重要数据。

6.接下来

到最后,高级Java教程也结束了。 希望您发现它是有用,实用和有趣的。 有许多主题尚未涵盖,但是非常欢迎您继续深入探讨Java语言,平台,生态系统和不可思议的社区的奇妙世界。 祝好运!

7.下载源代码

您可以在此处下载本课程的源代码: advanced-java-part-15

翻译自: https://www.javacodegeeks.com/2015/09/java-agents.html

java代理模式

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

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

相关文章

如何用python抢课_名额不够,技术来凑,利用Python实现教务系统强制性抢课

这个不是一个点击脚本&#xff0c;而是属于扩容性质的脚本。名额不够咱们利用技术来解决&#xff01;最近一学期一次的抢课大戏又来了&#xff0c;几家欢乐几家愁。O(∩_∩)O哈哈~(l我每次一选就过了hah&#xff0c;我还是有欧的时候滴)。看着他们盯着教务系统就着急&#xff0…

C++ 利用硬件加速矩阵乘法

点击蓝字关注我们来源于网络&#xff0c;侵删1.矩阵乘法定义2.矩阵类封装我们用 C封装了一个n m 的矩阵类&#xff0c;用二维数组来存储数据&#xff0c;定义如下&#xff1a;#define MAXN 1000 #define LL __int64class Matrix { private:int n, m;LL** pkData; public:Matri…

redis分片_Redis分片

redis分片本文是我们学院课程的一部分&#xff0c;标题为Redis NoSQL键值存储 。 这是Redis的速成课程。 您将学习如何安装Redis和启动服务器。 此外&#xff0c;您还会在Redis命令行上乱七八糟。 接下来是更高级的主题&#xff0c;例如复制&#xff0c;分片和集群&#xff0c…

python tkinter窗口切换_tkinter--实现简单的页面切换

[Python] 纯文本查看 复制代码import tkinter as tkindex_num 4def up_page():info frame_root.place_info()x int(info[x]) 100y info[y]frame_root.place(xx, yy)global index_numindex_num 1index[text] "第 {} 页".format( index_num)def down_page():inf…

解析C++全排列

点击蓝字关注我们来源于网络&#xff0c;侵删1.C实现全排列的函数next_permutation(start,end)这个函数在暴力解决问题方面有很大作用&#xff0c;使用时需要引入头文件 < algorithm >&#xff0c;当当前序列不存在下一个序列时就会结束&#xff0c;若想得到一个序列的全…

python读写文件代码_Python 读写文件的操作代码

Python读写文件模式1、r 打开只读文件&#xff0c;该文件必须存在。2、r 打开可读写的文件&#xff0c;该文件必须存在。3、w 打开只写文件&#xff0c;若文件存在则文件长度清为0&#xff0c;即该文件内容会消失。若文件不存在则建立该文件。4、w 打开可读写文件&#xff0c;若…

redis开启redis_Redis聚类

redis开启redis本文是我们学院课程的一部分&#xff0c;标题为Redis NoSQL键值存储 。 这是Redis的速成课程。 您将学习如何安装Redis和启动服务器。 此外&#xff0c;您还会在Redis命令行上乱七八糟。 接下来是更高级的主题&#xff0c;例如复制&#xff0c;分片和集群&#…

C++ 读取文件操作

点击蓝字关注我们来源于网络&#xff0c;侵删1.先上代码&#xff1a;#include <fstream> #include<iostream> using namespace std;//文本文件读文件 void test01() {//1、包含头文件//2、创建流对象ifstream ifs;//3、打开文件并且判断是否打开成功ifs.open("…

python调用simulink_使用Python从dbc文件中提取simulink建模数据定义

使用dbc文件建模完成CAN通讯是一种比较高效的开发模式&#xff0c;不过在建模的过程中dbc文件中描述的数据需要自己去定义。使用文本编辑工具打开dbc文件可以看到&#xff0c;实际上dbc文件是一个可以进行语义解析的文本。这样&#xff0c;通过脚本语言便可以轻松的实现simulin…

cov/cor中有遗漏值_协调遗漏的效果–使用简单的NIO客户端/服务器测量回送延迟...

cov/cor中有遗漏值在这篇文章中&#xff0c;我演示了许多想法和技术&#xff1a; 如何编写一个简单的非阻塞NIO客户端/服务器 协同遗漏的影响 如何测量百分位数的延迟&#xff08;相对于简单平均&#xff09; 如何在计算机上计时延迟回送 我最近正在为客户端服务器应用程序…

C/C++,判断变量的类型

点击蓝字关注我们来源于网络&#xff0c;侵删出于某个奇葩需求&#xff0c;研究了一下c/c如何判断变量类型&#xff0c;整理总结在此&#xff0c;分享给大家&#xff0c;也避免自己以后绕弯。一、c判断变量类型c中&#xff0c;可以利用typeid()来判断变量类型。第一步&#xff…

python访问文件下载地址_用Python脚本去获得skydrive上文件的真实地址链接 + 提供脚本下载v2012-01-18...

之前得知微软提供的免费在线云存储空间Skydrive提供的空间大小达25GB的时候&#xff0c;就像其他人一样想到了可以用来存储音视频和图片&#xff0c;作为文件存储器&#xff0c;以便和别人分享文件。但是后来发现&#xff0c;skydrive中上面文件连接&#xff0c;通过系统提供的…

c/c++语言实现登陆界面

点击蓝字关注我们来源自网络&#xff0c;侵删一.整体功能介绍实现一个登陆界面1 输出一个登陆界面2 用户名能够实现邮箱验证&#xff0c;regex库&#xff0c;密码要不可见3 进度条的模拟实现4 音乐播放二.分步实现1.输出一个登陆界面首先对此功能使用到的函数进行简单的介绍。s…

spark restful_Spark入门:也可以用Java创建轻量级的RESTful应用程序

spark restful最近&#xff0c;我一直在使用Spark &#xff08;一种Java的Web框架&#xff0c;与Apache Spark 不相关&#xff09;编写RESTful服务。 当我们计划写这篇文章时&#xff0c;我已经做好了不可避免的接口&#xff0c;样板代码和深层层次结构的Java风格的准备。 令我…

C++的get()函数与getline()函数使用详解

点击蓝字关注我们来源自网络&#xff0c;侵删一.C的get()函数使用详解1.C get()函数get()函数是cin输入流对象的成员函数&#xff0c;它有3种形式&#xff1a;无参数的&#xff1b;有一个参数的&#xff1b;有3个参数的。1) 无参数的其调用形式为cin.get()用来从指定的输入流中…

电脑所有程序里有不一样颜色_12个好玩的电脑屏保,让你成为别人眼中最靓的仔。...

Hello 大家好&#xff0c;这里是工具狂人。作为一个靠打字(哦不&#xff0c;搬砖)为生的新媒体小编&#xff0c;每天多数时候都是对着电脑屏幕&#xff0c;中途有时会拿起手机回复消息、查看短信、刷起微博。刷手机的时间一长&#xff0c;眼前的电脑会自动打开系统的屏保程序&a…

java8 函数式编程_如何使用Java 8函数式编程生成字母序列

java8 函数式编程我偶然发现了用户“ mip”一个有趣的堆栈溢出问题 。 问题是&#xff1a; 我正在寻找一种生成字母序列的方法&#xff1a; A, B, C, ..., Z, AA, AB, AC, ..., ZZ.可以很快将其识别为Excel电子表格的标题&#xff0c;它确实做到了&#xff1a; 到目前为止&a…

C++判断变量/对象/枚举类型的简单方式

点击蓝字关注我们来源于网络&#xff0c;侵删1.关键点<typeinfo>使用typeid()操作符所需包含的头文件。typeid()获取变量类型信息的操作符&#xff0c;其返回值类型为std::typeinfo。我们可使用typeid(n) typeid(int)的方式来判断变量n是否为类型int。注&#xff1a;可以…

python循环输出三角形图案的画_python循环输出三角形图案的例子

{"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],"search_count":[{"count_phone":4,"count":4}]},"card":[{"des":"阿里技术人对外发布原创技术内容的最大平台&…

终端查看命令有哪些功能命令_从命令式功能到纯粹功能式功能,再返回:Monads与范围内的延续...

终端查看命令有哪些功能命令这段视频附带了这篇文章&#xff0c;没有它就不会太有意义 上个月&#xff0c;我在Curry On会议上做了演讲&#xff0c;该会议是与学术&#xff0c;编程语言会议ECOOP共同举办的新会议。 Curry On旨在弥合学术界之间的鸿沟。 我的学术兴趣不包括编程…