Javassist - 如何将方法注入JAR中的类

时间:2014-03-23 14:04:11

标签: 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("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(系统找不到指定的路径)

2 个答案:

答案 0 :(得分:8)

我想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。 然后通过当前JAR的所有条目并将其所有条目写入临时 JAR文件除了正在更新的条目(字节码已经写入上面)。然后它删除当前的JAR并使用相同的JAR名称将其替换为临时JAR。

答案 1 :(得分:1)

我想参考以下StackOverflow Q& A:Link

当您阅读评论时,您会发现无法回写JAR。抛出的IOException(FNFE)也表明了这一点。或者是一个愚蠢的wisenheimer回答:如果我们将JavaDoc读入该问题,我们会发现该方法需要 directoryName JavaDoc

我建议解压缩JAR,进行操作,然后重新构建(压缩)JAR。

让我发布你的想法。