即时编译Java代码

Java使得它可以在运行时...... 任何 Java代码编译Java代码。

编译的入口点是ToolProvider类。 从其Javadoc:

提供用于定位工具提供者(例如,编译器的提供者)的方法。 此类补充了ServiceLoader的功能。

从10年前发布的1.6版开始,此类就可以在Java中使用,但似乎已被很大程度上忽略。

编码

这是一个允许的片段:

publicclassEvilExecutor{

    privateStringreadCode(StringsourcePath)throwsFileNotFoundException{
        InputStreamstream=newFileInputStream(sourcePath);
        Stringseparator=System.getProperty("line.separator");
        BufferedReaderreader=newBufferedReader(newInputStreamReader(stream));
        returnreader.lines().collect(Collectors.joining(separator));
    }

    privatePathsaveSource(Stringsource)throwsIOException{
        StringtmpProperty=System.getProperty("java.io.tmpdir");
        PathsourcePath=Paths.get(tmpProperty,"Harmless.java");
        Files.write(sourcePath,source.getBytes(UTF_8));
        returnsourcePath;
    }

    privatePathcompileSource(PathjavaFile){
        JavaCompilercompiler=ToolProvider.getSystemJavaCompiler();
        compiler.run(null,null,null,javaFile.toFile().getAbsolutePath());
        returnjavaFile.getParent().resolve("Harmless.class");
    }

    privatevoidrunClass(PathjavaClass)
            throwsMalformedURLException,ClassNotFoundException,IllegalAccessException,InstantiationException{
        URLclassUrl=javaClass.getParent().toFile().toURI().toURL();
        URLClassLoaderclassLoader=URLClassLoader.newInstance(newURL[]{classUrl});
        Class<?>clazz=Class.forName("Harmless",true,classLoader);
        clazz.newInstance();
    }

    publicvoiddoEvil(StringsourcePath)throwsException{
        Stringsource=readCode(sourcePath);
        PathjavaFile=saveSource(source);
        PathclassFile=compileSource(javaFile);
        runClass(classFile);
    }

    publicstaticvoidmain(String...args)throwsException{
        newEvilExecutor().doEvil(args[0]);
    }
}

以下是一些解释:

readCode()

从文件系统上的任意文件中读取源代码,并将其作为字符串返回。 另一种实现方式是从整个网络获取源。

saveSource()

从源代码在已启用读取的目录中创建一个新文件。 文件名是硬编码的,更精细的版本将解析code参数来创建一个根据其包含的类名命名的文件。

compileSource()

从Java文件中编译类文件。

runClass

加载编译的类并实例化一个新对象。 为了独立于任何强制类型转换,应在外部源代码类的构造函数中设置要执行的代码。

问题

从功能的角度来看,与不提供此功能的其他语言相比,即时编译代码可提高Java语言的价值。 从安全角度来看,这是一场噩梦。 能够在生产中执行任意代码的想法应该使任何IT组织(包括开发人员)(即使不是大多数)中的任何人都感到不寒而栗。

经验丰富的开发人员/操作人员或普通读者可能记得Java安全管理器以及如何激活它:

java-Djava .security.manager -cp target/classes ch.frankel.blog.runtimecompile.EvilExecutor harmless.txt

执行上面的命令行将产生以下结果:

 [email protected]
Exception in thread "main" java.security.AccessControlException:
    access denied ("java.io.FilePermission" "harmless.txt" "read")
  at java.security.AccessControlContext.checkPermission(AccessControlContext.java:472)
  at java.security.AccessController.checkPermission(AccessController.java:884)
  at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
  at java.lang.SecurityManager.checkRead(SecurityManager.java:888)
  at java.io.FileInputStream.<init>(FileInputStream.java:127)
  at java.io.FileInputStream.<init>(FileInputStream.java:93)
  at ch.frankel.blog.runtimecompile.EvilExecutor.readCode(EvilExecutor.java:19)
  at ch.frankel.blog.runtimecompile.EvilExecutor.doEvil(EvilExecutor.java:47)
  at ch.frankel.blog.runtimecompile.EvilExecutor.main(EvilExecutor.java:56)

结论

JVM提供了许多功能。 与任何工具一起使用时,无论好坏,它们都可以使用。 每个人都有责任对正确保护自己的JVM负责,在银行,军事等敏感领域则要加倍。

翻译自: https://blog.frankel.ch/compilation-java-code-on-the-fly/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值