Javassist-如何将方法注入JAR中的类
我正在从Javassist-如何将方法注入JAR中的类,java,jar,Java,Jar,我正在从JAR文件中读取一个类并注入一个函数。如何将其写回JAR文件 // Load the class representation ClassPool pool = ClassPool.getDefault(); pool.insertClassPath( "c:/Test.jar" ); CtClass cc = pool.get("com.test.TestFunction"); CtMethod m = CtNewMethod.make("publ
JAR
文件中读取一个类并注入一个函数。如何将其写回JAR文件
// Load the class representation
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath( "c:/Test.jar" );
CtClass cc = pool.get("com.test.TestFunction");
CtMethod m = CtNewMethod.make("public void test2() { System.out.println(\"test2\"); }", cc);
cc.addMethod(m);
CtMethod cm = cc.getDeclaredMethod("test1", new CtClass[0]);
cm.insertBefore("{ test2();}");
cc.writeFile("c:/Test.jar"); // Fails here
线程“main”
java.io.FileNotFoundException中出现异常
:c:\Test.jar\com\Test\TestFunction.class(系统找不到指定的路径)我想参考StackOverflow Q&A中的以下内容:
当您阅读这些注释时,您会发现无法回写到JAR。这也由抛出的IOException(FNFE)表示。或者一个愚蠢的wisenheimer回答:如果我们将JavaDoc读入这个问题,我们会看到该方法需要一个directoryName
我建议解压JAR,进行操作,然后重新构建(压缩)JAR
请随时告诉我您的想法。我想Javassist中没有简单的方法可以更新JAR并用新类替换更新的类。所以我创建了一个JarHandler类,它只接收参数 这是执行注入的主类
public static void main(String args[]){
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath( "c:/Test.jar" );
CtClass cc = pool.get("com.test.TestFunction");
CtMethod m = CtNewMethod.make("public void test2() { System.out.println(\"test2\"); }", cc);
cc.addMethod(m);
CtMethod cm = cc.getDeclaredMethod("test1", new CtClass[0]);
cm.insertBefore("{ test2();}");
byte[] b = cc.toBytecode(); // convert the new class to bytecode.
pool.removeClassPath(cp); // need to remove the classpath to release connection to JAR file so we can update it.
JarHandler jarHandler = new JarHandler();
jarHandler.replaceJarFile("C:/Test.jar", b, "com/test/TestFunction.class");
}
这是JarHandler类
public class JarHandler{
public void replaceJarFile(String jarPathAndName,byte[] fileByteCode,String fileName) throws IOException {
File jarFile = new File(jarPathAndName);
File tempJarFile = new File(jarPathAndName + ".tmp");
JarFile jar = new JarFile(jarFile);
boolean jarWasUpdated = false;
try {
=
JarOutputStream tempJar =
new JarOutputStream(new FileOutputStream(tempJarFile));
// Allocate a buffer for reading entry data.
byte[] buffer = new byte[1024];
int bytesRead;
try {
// Open the given file.
try {
// Create a jar entry and add it to the temp jar.
JarEntry entry = new JarEntry(fileName);
tempJar.putNextEntry(entry);
tempJar.write(fileByteCode);
} catch (Exception ex) {
System.out.println(ex);
// Add a stub entry here, so that the jar will close without an
// exception.
tempJar.putNextEntry(new JarEntry("stub"));
}
// Loop through the jar entries and add them to the temp jar,
// skipping the entry that was added to the temp jar already.
InputStream entryStream = null;
for (Enumeration entries = jar.entries(); entries.hasMoreElements(); ) {
// Get the next entry.
JarEntry entry = (JarEntry) entries.nextElement();
// If the entry has not been added already, so add it.
if (! entry.getName().equals(fileName)) {
// Get an input stream for the entry.
entryStream = jar.getInputStream(entry);
tempJar.putNextEntry(entry);
while ((bytesRead = entryStream.read(buffer)) != -1) {
tempJar.write(buffer, 0, bytesRead);
}
}else
System.out.println("Does Equal");
}
if(entryStream!=null)
entryStream.close();
jarWasUpdated = true;
}
catch (Exception ex) {
System.out.println(ex);
// IMportant so the jar will close without an
// exception.
tempJar.putNextEntry(new JarEntry("stub"));
}
finally {
tempJar.close();
}
}
finally {
jar.close();
if (! jarWasUpdated) {
tempJarFile.delete();
}
}
if (jarWasUpdated) {
if(jarFile.delete()){
tempJarFile.renameTo(jarFile);
System.out.println(jarPathAndName + " updated.");
}else
System.out.println("Could Not Delete JAR File");
}
}
这个函数只是通过向其写入新的类字节码来创建一个临时JAR。
然后is遍历当前JAR的所有条目,并将其所有条目写入临时JAR
JAR文件,但正在更新的条目除外(上面已经写入了字节码)。然后它删除当前JAR,并用使用相同JAR名称的临时JAR替换它。是的,可以看出writeFile()需要一个目录名作为参数。我一辈子都无法理解为什么可以从JAR中检索类并用几个方法对其进行操作,而没有任何简单的方法将其写回JAR。我甚至找不到任何这样做的例子。我认为许多开发人员将处理JAR而不仅仅是类路径。我同意这种用法。我认为该包的作者严格遵循了OO原则“紧密内聚但松散耦合”(或者说是懒惰的):操作类的所有功能都可用(紧密),但JAR操作(耦合)缺失。-您介意发布您的解决方案以帮助他人更快地进步吗?我不太确定我是否同意紧密内聚/松散耦合的评论。这与类之间的接口以及它们如何交互有关。在这种情况下,我认为操作字节码有3个主要功能领域。1.)定位要操作的字节码/查看2.)实际操作/查看3.)编写新字节码。当涉及到向jar写入时,该包没有实现第三个功能。这个包完成了类文件方法的编写,我自己也可以轻松地完成。很好,编程/编码已经从一个基于事实的主题转变为一种哲学:)为什么要这样做?类通常部署在JAR中。因此,如果我想在指定的关键点将性能入口/出口代码注入到应用程序中,这就是如何对已部署的应用程序执行此操作。这是一个非常糟糕的主意。你不应该改变你正在测试的东西,即使是为了性能。如果您的perf monitor提取代码中存在错误,并且会对您的性能产生负面影响,那该怎么办?实现这一点的正确方法是对jvm进行测试,jvm有一个用于这类事情的api。看看jprofiler()。我同意,做一些盲目的事情是个坏主意。但我将做的不仅仅是性能监控,更多的是“开箱即用”功能。其中一些功能将使用更亲密/直接的方法来实现,例如使用代理。但是,对于我正在处理的环境来说,这是不可能的。我只是在探索阶段寻找其他方法。谢谢你的建议。你能提供一个可编译的例子吗?