java源文件编译成jar_从源文件和JAR文件构建Java代码模型
java源文件編譯成jar
最近,我花了一些時(shí)間來研究有效java ,該方法正在GitHub上達(dá)到300星(可以免費(fèi)幫助實(shí)現(xiàn)目標(biāo):D)。
Effectivejava是在您的Java代碼上運(yùn)行查詢的工具。 它基于我參與的另一個(gè)項(xiàng)目javaparser 。 Javaparser將Java源代碼作為輸入,并生成一個(gè)抽象語法樹(AST)。 我們可以直接在AST上執(zhí)行簡單的分析。 例如,我們可以找出哪些方法采用超過5個(gè)參數(shù)(您可能希望對其進(jìn)行重構(gòu)……)。 但是,更復(fù)雜的分析要求解析符號 。
在這篇文章中,我將介紹在考慮源代碼和JAR文件的情況下如何實(shí)現(xiàn)符號解析。 在第一篇文章中,我們將在源代碼和JAR文件上建立同質(zhì)視圖,在下一篇文章中,我們將探索這些模型來解決這些符號。
代碼可在GitHub上的有效Java的分支symbolsolver中獲得。
解析符號
出于什么原因,我們需要解析符號?
給出以下代碼:
foo.method(a, b, c);我們需要弄清楚foo , method , a , b , c是什么。 它們是否引用局部變量? 給當(dāng)前方法的參數(shù)? 到在類中聲明的字段? 要從超類繼承的字段? 他們有什么類型? 為了回答這個(gè)問題,我們需要能夠解析符號。
為了解決符號,我們可以瀏覽AST并應(yīng)用作用域規(guī)則。 例如,我們可以查看某個(gè)符號是否對應(yīng)于局部變量。 如果沒有,我們可以在該方法的參數(shù)中查找。 如果仍然找不到對應(yīng)關(guān)系,則需要在類聲明的字段中查找,如果仍然不走運(yùn),則可能必須在此類繼承的字段中走運(yùn)。
現(xiàn)在,作用域規(guī)則比我剛剛描述的一小步要復(fù)雜得多。 由于重載,解決方法特別復(fù)雜。 但是,一個(gè)關(guān)鍵點(diǎn)是,要解決符號,我們通常需要在導(dǎo)入的類,擴(kuò)展的類和外部類中進(jìn)行查找,這些類可能是項(xiàng)目的一部分,也可以作為依賴項(xiàng)導(dǎo)入。
因此,要解決符號,我們需要尋找相應(yīng)的聲明:
Javaparser為我們提供了第一點(diǎn)所需的AST,對于第二點(diǎn),我們將使用Javassist在JAR文件中構(gòu)建類的模型。
建立JAR文件中包含的類的模型
我們的符號求解器應(yīng)按順序在條目列表(我們的類路徑條目)中查找,并查看是否可以在其中找到某個(gè)類。 為此,我們需要打開JAR文件并在其內(nèi)容中查找。 出于性能原因,我們可能希望構(gòu)建給定JAR中包含的元素的緩存。
(ns app.jarloading(:use [app.javaparser])(:use [app.operations])(:use [app.utils])(:import [app.operations Operation]))(import java.net.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)))我們?nèi)绾伍_始? 首先,我們閱讀jar中列出的條目( getElementEntriesInJar )。 這樣,我們得到了ClasspathElements的列表。 然后,我們僅關(guān)注.class文件( getClassesEntriesInJar )。 每個(gè)jar應(yīng)調(diào)用一次此方法,并且應(yīng)將結(jié)果緩存。 給定ClasspathElement列表,然后我們可以搜索與給定名稱對應(yīng)的元素(例如com.github.javaparser.ASTParser )。 為此,我們可以使用方法findEntry 。 或者,我們也可以使用Javassist加載該類: findType方法執(zhí)行的操作,返回CtClass的實(shí)例。
為什么不僅僅使用反射?
有人可能會認(rèn)為,僅在有效java的類路徑中添加依賴項(xiàng),然后使用常規(guī)的類加載器和反射來獲取所需的信息會更容易。 雖然這會更容易,但是存在一些缺點(diǎn):
解決符號:結(jié)合異構(gòu)模型
現(xiàn)在,要解決符號問題,我們將必須實(shí)現(xiàn)作用域規(guī)則,并瀏覽從Javaparser獲得的AST和從Javassist獲得的CtClass 。 我們將在以后的博客文章中看到詳細(xì)信息,但是我們需要首先考慮另一個(gè)方面。 考慮以下代碼:
package me.tomassetti;import com.github.someproject.ClassInJar;public class MyClass extends ClassInJar {private int myDeclaredField;public int foo(){return myDeclaredField + myInheritedField;} }在這種情況下,我們假設(shè)有一個(gè)包含類com.github.someproject.ClassInJar的JAR,該類聲明了字段myInheritedField 。 當(dāng)我們求解符號時(shí),將具有以下映射:
- myDeclaredField將被解析為com.github.javaparser.ast.body.VariableDeclarator的一個(gè)實(shí)例(在JavaParser類我們有映射到結(jié)構(gòu),如私人INT A,B,C型FieldDeclaration的節(jié)點(diǎn); VariableDeclarators代替點(diǎn)到單個(gè)字段例如a , b或c )
- myInheritedField將解析為javassist.CtField的實(shí)例
問題在于我們希望能夠以同質(zhì)的方式對待它們:我們應(yīng)該能夠使用相同的函數(shù)來對待每個(gè)字段,而不管它們的起源(JAR文件還是Java源文件)。 為此,我們將使用clojure 協(xié)議構(gòu)建通用視圖。 我傾向于將clojure的協(xié)議視為與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中,我們必須構(gòu)建適配器,實(shí)現(xiàn)新的接口( FieldDecl )并將現(xiàn)有的類( VariableDeclarator , CtField )包裝在Clojure中,我們只能說這些類擴(kuò)展了協(xié)議,我們已經(jīng)完成了。
現(xiàn)在我們可以將每個(gè)字段都視為fieldDecl ,并且可以在每個(gè)字段上調(diào)用fieldName 。 我們?nèi)匀恍枰宄绾谓鉀Q字段類型 。 為此,我們需要研究符號解析,尤其是類型解析,這是我們的下一步。
結(jié)論
Java代碼的構(gòu)建模型使我著迷了一段時(shí)間。 作為我的碩士論文的一部分,我寫了一個(gè)與現(xiàn)有Java代碼交互的DSL(我也有編輯器,寫為Eclipse插件和代碼生成器:這很酷)。 在DSL中,可以使用源代碼和JAR文件指定對Java類的引用。 我使用的是EMF,并且可能在該項(xiàng)目中采用了JaMoPP和Javassist。
后來,我建立了CodeModels庫,以分析幾種語言(Java,JavaScript,Ruby,Html等)的AST。
我認(rèn)為構(gòu)建用于操作代碼的工具是元編程的一種非常有趣的形式,并且應(yīng)該在每個(gè)開發(fā)人員的工具箱中。 我計(jì)劃花更多的時(shí)間來使用有效的java。 有趣的時(shí)刻來了。
隨時(shí)分享評論和建議!
翻譯自: https://www.javacodegeeks.com/2015/08/building-models-of-java-code-from-source-and-jar-files.html
java源文件編譯成jar
總結(jié)
以上是生活随笔為你收集整理的java源文件编译成jar_从源文件和JAR文件构建Java代码模型的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: pl/postgresql_将Postg
- 下一篇: 电脑放硬盘的方法(硬盘应该怎样放)