2016-07-05 117 views
2

我有两个jar文件(例如,可以将它们称为Updater.jar和Code.jar)。 Updater.jar启动,其主要方法,然后将其与倍美法再次启动本身:将jar文件添加到仪表路径

package Update; 

import java.io.File; 
import java.io.FileOutputStream; 
import java.io.IOException; 
import java.io.InputStream; 
import java.io.OutputStream; 
import java.util.ArrayList; 
import java.util.List; 

public class InstructionLauncher { 

    private List<UpdateInstruction> instructions = new ArrayList<UpdateInstruction>(); 
    private static InstructionLauncher instance; 
    private Process process; 

    public static InstructionLauncher initialise(){ 
     if(instance !=null) return instance; 
     else return new InstructionLauncher(); 
    } 

    public void registerPremain(UpdateInstruction inst){ 
     instructions.add(inst); 
    } 

    public void launchNext(){ 
     UpdateInstruction inst = instructions.get(0); 
     String cls = inst.getClassName() + "." + inst.getMethodName(); 
     String[] args = new String[]{"java", "-javaagent", "JOSUpdater.jar", "-jar", inst.getClassName() + "." + inst.getMethodName()}; 
     ProcessBuilder builder = new ProcessBuilder(args); 
     try { 
      exportResource(cls, cls); 
     } catch (Exception e) { 
      UpdateManager.revert(); 
     } 
     try { 
      Process p = builder.start(); 
      process = p; 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
     while(!process.isAlive())launchNext(); 
    } 

    private InstructionLauncher(){ 
     instance = this; 
    } 

    //From http://stackoverflow.com/questions/10308221/how-to-copy-file-inside-jar-to-outside-the-jar 
    private String exportResource(String resourceName, String clazz) throws Exception { 
     InputStream stream = null; 
     OutputStream resStreamOut = null; 
     String jarFolder; 
     try { 
      stream = Class.forName(clazz).getResourceAsStream(resourceName);//note that each/is a directory down in the "jar tree" been the jar the root of the tree 
      if(stream == null) { 
       throw new Exception("Cannot get resource \"" + resourceName + "\" from Jar file."); 
      } 

      int readBytes; 
      byte[] buffer = new byte[4096]; 
      jarFolder = new File(Class.forName(clazz).getProtectionDomain().getCodeSource().getLocation().toURI().getPath()).getParentFile().getPath().replace('\\', '/'); 
      resStreamOut = new FileOutputStream(jarFolder + resourceName); 
      while ((readBytes = stream.read(buffer)) > 0) { 
       resStreamOut.write(buffer, 0, readBytes); 
      } 
     } catch (Exception ex) { 
      throw ex; 
     } finally { 
      stream.close(); 
      resStreamOut.close(); 
     } 

     return jarFolder + resourceName; 
    } 

} 

的倍美力的方法是这样的时刻:

package Update; 

import java.lang.instrument.Instrumentation; 

public class PremainLauncher { 

    public static void premain(String args, Instrumentation inst){ 
     inst.addTransformer(new Transformer(), true); 
     System.out.println("Registered instruction for package: " + args); 
    } 

} 

什么我不知道,是如何将整个外部JAR(本例中为Code.jar)添加到仪器的路径中?

我知道Instrumentation.retransformClasses方法,但要使用该方法,我需要获取该jar中的所有类的List>,但我一直无法完成该方法。

可以说Code.jar有三个类文件:Main.class,writer.class和display.class。有没有办法获得他们每个类对象的列表,而不是他们的名字?

+0

只需在类路径中添加所需的jar。 – ManoDestra

+0

仅仅是通过修改java.library.path变量? – JD9999

+0

我的意思是简单地将所需的jar添加到类路径,例如java -cp path/to/your.jar; path/to/other.jar com.example.app.MainApp。不知道这是你的意思吗?除此之外,如果您希望获得更大的灵活性,则可以通过URLClassLoader类在运行时动态添加JAR。 – ManoDestra

回答

2

Java代理可以简单地通过它在启动方法中接收的接口Instrumentation添加jar文件,例如,

import java.io.IOException; 
import java.lang.instrument.Instrumentation; 
import java.util.jar.JarFile; 

public class PremainLauncher { 
    public static void premain(String args, Instrumentation inst) throws IOException{ 
     inst.appendToSystemClassLoaderSearch(new JarFile("Code.jar")); 
     inst.addTransformer(new Transformer(), true); 
     System.out.println("Registered instruction for package: " + args); 
    } 
} 

请参阅Instrumentation.appendToSystemClassLoaderSearch(…)。如果您打算使用JRE类来进行仪器化类的访问,那么您必须改为change the bootstrap path

注意,这必须尽可能早地发生:

的Java™虚拟机规范指定解析符号引用的后续尝试Java虚拟机先前已不成功地试图解决总是失败,因为初始解决方案尝试导致的错误。因此,如果JAR文件包含与Java虚拟机未成功尝试解析引用的类相对应的条目,则后续尝试解析该引用将失败,并显示与初始尝试相同的错误。

+0

所以,如果一个不同的同名类加载失败,那么这里的类会怎么样呢? – JD9999

+1

好吧,如果你将一个jar附加到类路径中,你认为没有其他类具有相同的类在该范围内的名称,否则,你的jar永远不会被搜索,但如果试图用这个类加载该类呃是在你添加你的jar之前创建的,失败会被记住。由于'premain'在任何应用程序代码之前运行,只要您在执行任何可能导致加载尝试的操作之前附加了jar,这应该不是问题。 – Holger

+0

有一个主要的应用程序启动。这个应用程序启动第一个jar(我们称它为start.jar)。在完成start.jar之后,应用程序将启动更新程序。更新程序然后自行启动premain方法。所以它在应用程序代码之前运行,这会有所作为吗? – JD9999