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

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

在这篇文章中,我描述了同时考虑源代码和JAR文件来实现符号解析的工作方式。 在第一篇文章中,我们将在源代码和JAR文件上建立同质视图,在下一篇文章中,我们将探索这些模型来解决这些符号。

可以在GitHub上的有效Java的分支symbolsolver上找到代码。

解析符号

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

给出以下代码:

foo.method(a, b, c);

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

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

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

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

  1. 根据我们正在研究的项目类别的AST
  2. 在用作依赖项的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 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)))

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

为什么不仅仅使用反射?

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

  1. 当加载一个类时,将执行静态初始化程序,这可能不是我们想要的
  2. 它可能与有效Java的实际依赖项冲突。
  3. 最后,并非所有字节码中可用的信息都可以通过反射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代替点到单个字段例如abc
  • 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 )并将现有的类( VariableDeclaratorCtField )包装在Clojure中,我们只能说这些类扩展了协议,我们已经完成了。

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

结论

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

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

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

随时分享评论和建议!

翻译自: https://www.javacodegeeks.com/2015/08/building-models-of-java-code-from-source-and-jar-files.html

从源文件和JAR文件构建Java代码模型相关推荐

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

    java源文件编译成jar 最近,我花了一些时间来研究有效java ,该方法正在GitHub上达到300星(可以免费帮助实现目标:D). Effectivejava是在您的Java代码上运行查询的工具 ...

  2. java写创建对象时找不到符号_在JDK 1.8上构建java代码时找不到符号错误

    我在maven上用JDK 1.8构建Java代码.在构建过程中,我得到了cannot find symbol error. maven上的错误堆栈跟踪: [INFO] ---------------- ...

  3. ant java 外部jar包_java – 在使用Ant的新jar文件构建中包括外部jar文件

    我只是"继承"一个Java项目,而不是来自Java背景,我有时失去一点. Eclipse用于在开发期间调试和运行应用程序.我已经通过Eclipse成功创建了一个.jar文件,'包括 ...

  4. jar包不用java命令_使不能运行的JAR文件可以使用java -jar运行

    "); System.exit(0); } 请注重参数列表是如何被解释的,因为这对于后面的代码是非常重要的.参数的顺序和内容并不是硬性设置的,但是假如你改变它们也要记得适当的修改其他的代码. ...

  5. .jar文件还原java文件_从jar包还原出java源码(项目文件)

    原文转载至:https://blog.csdn.net/mxmxz/article/details/73043156 上周接到个新任务,一个遗留的接口工程需要改造,然而根据前任开发留下的文档看,这个工 ...

  6. wsdl文件转换为java代码

    转换前准备 wsdl文件,axis.wsdl axis所需要的jar包 一个bat文件,复制下面代码保存为axis.bat即可 set Axis_Lib=D:\jar\axis2-1.7.4\lib ...

  7. java telephony jar,android – 修改framework.jar中的java代码

    我手机上的手机存在与MVNO(移动虚拟网络操作符)的问题.基本上这意味着我的数据连接仅在漫游时有效.这是一个已经在几个roms上修复的已知问题(但不是我的). 为了解决这个问题,我想修改framewo ...

  8. JSP文件中Java代码的几种形式(JSP脚本)

    文章目录 第一种形式(Java 代码片段) 第二种形式(JSP 表达式) 第三种形式(JSP 声明) 第四种形式(JSP 指令) 第一种形式(Java 代码片段) 原样复制到 service() 方法 ...

  9. jsp文件里java代码的作用_如何使用JSP 2避免JSP文件中的Java代码?

    小编典典 自从2001年标签库(例如JSTL)和EL(表达语言,那些东西)的诞生以来,在JSP中确实不建议使用scriptlet(那些东西).${} scriptlet的主要缺点是: 可重用性:您无法 ...

最新文章

  1. 编写可调模板并使用自动调谐器
  2. 【转】GeoServer地图开发解决方案(四):发布Web地图服务(WMS)篇
  3. Python语言学习:解决python版本升级问题集合(python2系列→Python3系列)导致错误的总结集合
  4. 12864 OLED屏显示日历
  5. 零基础爬虫requests初阶教程,手把手教你爬数据
  6. 腾讯Techo Park开发者大会召开在即,全球200多位专家共话云计算
  7. (四) 数据库表基本操作
  8. 安卓自动化测试(2)Robotium环境搭建与新手入门教程
  9. mysql+proxysql+keepalived实现高可用的数据库读写分离
  10. linux终端输入五笔命令,Ubuntu 16.04如何安装Fcitx五笔拼音输入法
  11. opencv自适应二值化的应用
  12. 啊哈C语言课后练习题
  13. 内外兼修 高阶游戏鼠标达尔优A970黑武士评测
  14. 谁也没想到,苹果为了利润竟然如此敷衍用户!
  15. 程序员 谨防猝死
  16. 字节跳动测开实习面试题
  17. 两次腾讯面试挂二面的苦逼经历
  18. Flutter中PlatformView组件无法刷新的问题
  19. ARCGIS制作图中图——小图/一幅多图
  20. Le Chapitre IV

热门文章

  1. java实现遍历树形菜单方法——Dao层
  2. 新闻发布项目——实体类(newsTb)
  3. javaI/O流小结
  4. repo-话说软件详细设计工具
  5. HDU2067(卡特兰数)
  6. 版本交付_连续交付友好的Maven版本
  7. dynamodb容器使用_使用DynamoDB映射器将DynamoDB项目映射到对象
  8. eclipse中ast_JavaParser中AST节点的观察者
  9. k8s中graphite_在Graphite中存储Hystrix的几个月历史指标
  10. ejb运行程序_在哪里可以运行EJB?