1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > java源文件编译成jar_从源文件和JAR文件构建Java代码模型

java源文件编译成jar_从源文件和JAR文件构建Java代码模型

时间:2020-03-31 12:53:57

相关推荐

java源文件编译成jar_从源文件和JAR文件构建Java代码模型

java源文件编译成jar

最近,我花了一些时间来研究有效java ,该方法正在GitHub上达到300星(可以免费帮助实现目标:D)。

Effectivejava是在您的Java代码上运行查询的工具。 它基于我参与的另一个项目javaparser 。 Javaparser将Java源代码作为输入,并生成一个抽象语法树(AST)。 我们可以直接在AST上执行简单的分析。 例如,我们可以找出哪些方法采用超过5个参数(您可能希望对其进行重构……)。 但是,更复杂的分析要求解析符号

在这篇文章中,我将介绍在考虑源代码和JAR文件的情况下如何实现符号解析。 在第一篇文章中,我们将在源代码和JAR文件上建立同质视图,在下一篇文章中,我们将探索这些模型来解决这些符号。

代码可在GitHub上的有效Java的分支symbolsolver中获得。

解析符号

出于什么原因,我们需要解析符号?

给出以下代码:

foo.method(a, b, c);

我们需要弄清楚foo,method,a,b,c是什么。 它们是否引用局部变量? 给当前方法的参数? 到在类中声明的字段? 要从超类继承的字段? 他们有什么类型? 为了回答这个问题,我们需要能够解析符号。

为了解决符号,我们可以浏览AST并应用作用域规则。 例如,我们可以查看某个符号是否对应于局部变量。 如果没有,我们可以在该方法的参数中查找。 如果仍然找不到对应关系,则需要在类声明的字段中查找,如果仍然不走运,则可能必须在此类继承的字段中走运。

现在,作用域规则比我刚刚描述的一小步要复杂得多。 由于重载,解决方法特别复杂。 但是,一个关键点是,要解决符号,我们通常需要在导入的类,扩展的类和外部类中进行查找,这些类可能是项目的一部分,也可以作为依赖项导入。

因此,要解决符号,我们需要寻找相应的声明:

根据我们正在检查的项目类别的AST在用作依赖项的JAR文件中包含的类中

Javaparser为我们提供了第一点所需的AST,对于第二点,我们将使用Javassist在JAR文件中构建类的模型。

建立JAR文件中包含的类的模型

我们的符号求解器应按顺序在条目列表(我们的类路径条目)中查找,并查看是否可以在其中找到某个类。 为此,我们需要打开JAR文件并在其内容中查找。 出于性能原因,我们可能希望构建给定JAR中包含的元素的缓存。

(ns app.jarloading(:use [app.javaparser])(:use [app.operations])(:use [app.utils])(:import [app.operations Operation]))(import .URLDecoder)(import java.util.jar.JarEntry)(import java.util.jar.JarFile)(import javassist.ClassPool)(import javassist.CtClass); An element on the classpath (a single class, interface, enum or resource file)(defrecord ClasspathElement [resource path contentAsStreamThunk])(defn- jarEntryToClasspathElement [jarFile jarEntry](let [name (.getName jarEntry)content (fn [] (.getInputStream jarFile jarEntry))](ClasspathElement. jarFile name content)))(defn getElementsEntriesInJar"Return a set of ClasspathElements"[pathToJarFile](let [url (URLDecoder/decode pathToJarFile "UTF-8")jarfile (new JarFile url)entries (enumeration-seq (.entries jarfile))entries' (filter (fn [e] (not (.isDirectory e))) entries )](map (partial jarEntryToClasspathElement jarfile) entries')))(defn getClassesEntriesInJar"Return a set of ClasspathElements"[pathToJarFile](filter (fn [e] (.endsWith (.path e) ".class")) (getElementsEntriesInJar pathToJarFile)))(defn pathToTypeName [path](if (.endsWith path ".class")(let [path' (.substring path 0 (- (.length path) 6))path'' (clojure.string/replace path' #"/" ".")path''' (clojure.string/replace path'' "$" ".")]path''')(throw (IllegalArgumentException. "Path not ending with .class"))))(defn findEntry"return the ClasspathElement corresponding to the given name, or nil"[typeName classEntries](first (filter (fn [e] (= typeName (pathToTypeName (.path e)))) classEntries)))(defn findType"return the CtClass corresponding to the given name, or nil"[typeName classEntries](let [entry (findEntry typeName classEntries)classPool (ClassPool/getDefault)](if entry(.makeClass classPool ((.contentAsStreamThunk entry)))nil)))

我们如何开始? 首先,我们阅读jar中列出的条目(getElementEntriesInJar)。 这样,我们得到了ClasspathElements的列表。 然后,我们仅关注.class文件(getClassesEntriesInJar)。 每个jar应调用一次此方法,并且应将结果缓存。 给定ClasspathElement列表,然后我们可以搜索与给定名称对应的元素(例如com.github.javaparser.ASTParser)。 为此,我们可以使用方法findEntry。 或者,我们也可以使用Javassist加载该类:findType方法执行的操作,返回CtClass的实例。

为什么不仅仅使用反射?

有人可能会认为,仅在有效java的类路径中添加依赖项,然后使用常规的类加载器和反射来获取所需的信息会更容易。 虽然这会更容易,但是存在一些缺点:

当加载一个类时,将执行静态初始化程序,这可能不是我们想要的 它可能与有效Java的实际依赖项冲突。 最后,并非所有字节码中可用的信息都可以通过反射API轻松检索到

解决符号:结合异构模型

现在,要解决符号问题,我们将必须实现作用域规则,并浏览从Javaparser获得的AST和从Javassist获得的CtClass。 我们将在以后的博客文章中看到详细信息,但是我们需要首先考虑另一个方面。 考虑以下代码:

package me.tomassetti;import com.github.someproject.ClassInJar;public class MyClass extends ClassInJar {private int myDeclaredField;public int foo(){return myDeclaredField + myInheritedField;}}

在这种情况下,我们假设有一个包含类com.github.someproject.ClassInJar的JAR,该类声明了字段myInheritedField。 当我们求解符号时,将具有以下映射:

myDeclaredField将被解析为com.github.javaparser.ast.body.VariableDeclarator的一个实例(在JavaParser类我们有映射到结构,如私人INT A,B,C型FieldDeclaration的节点;VariableDeclarators代替点到单个字段例如a,b或c) myInheritedField将解析为javassist.CtField的实例

问题在于我们希望能够以同质的方式对待它们:我们应该能够使用相同的函数来对待每个字段,而不管它们的起源(JAR文件还是Java源文件)。 为此,我们将使用clojure协议构建通用视图。 我倾向于将clojure的协议视为与Java接口等效。

(defprotocol FieldDecl(fieldName [this]))(extend-protocol FieldDeclcom.github.javaparser.ast.body.VariableDeclarator(fieldName [this](.getName (.getId this))))(extend-protocol FieldDecljavassist.CtField(fieldName [this](.getName this)))

在Java中,我们必须构建适配器,实现新的接口(FieldDecl)并将现有的类(VariableDeclarator,CtField)包装在Clojure中,我们只能说这些类扩展了协议,我们已经完成了。

现在我们可以将每个字段都视为fieldDecl,并且可以在每个字段上调用fieldName。 我们仍然需要弄清楚如何解决字段类型。 为此,我们需要研究符号解析,尤其是类型解析,这是我们的下一步。

结论

Java代码的构建模型使我着迷了一段时间。 作为我的硕士论文的一部分,我写了一个与现有Java代码交互的DSL(我也有编辑器,写为Eclipse插件和代码生成器:这很酷)。 在DSL中,可以使用源代码和JAR文件指定对Java类的引用。 我使用的是EMF,并且可能在该项目中采用了JaMoPP和Javassist。

后来,我建立了CodeModels库,以分析几种语言(Java,JavaScript,Ruby,Html等)的AST。

我认为构建用于操作代码的工具是元编程的一种非常有趣的形式,并且应该在每个开发人员的工具箱中。 我计划花更多的时间来使用有效的java。 有趣的时刻来了。

随时分享评论和建议!

翻译自: //08/building-models-of-java-code-from-source-and-jar-files.html

java源文件编译成jar

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。