一种获取代码依赖关系的方法、装置及电子设备与流程

文档序号:18163015发布日期:2019-07-13 09:25阅读:187来源:国知局
一种获取代码依赖关系的方法、装置及电子设备与流程

本申请涉及软件开发领域,具体涉及一种获取代码依赖关系的方法。本方案还涉及一种获取代码依赖关系的装置以及一种电子设备。



背景技术:

目前,随着应用软件需求的提高,客户端代码的体量越来越大,代码规模已经增长到几百个模块,几十万个类的规模,业务开发人员和系统架构师很难比较直观,方便地获取模块/类/方法之间的相互耦合关系,对于依赖分析,系统设计,模块打标等都存在一定的门槛。

在此背景下,各个模块的开发人员都希望比较方便的获取整个客户端每个模块拥有的类,每个类的基本信息,并且这些模块/类/方法的相互调用关系,从而梳理出产品在模块/类/方法/属性等不同维度上的依赖关系。

在实际应用过程中,获取依赖关系是通过集成开发环境(ide),如eclipse,intelij等来实现。实现方式为导入项目源码,经过编译后,可以通过ide的调用方查找功能查看某个类/方法/属性的调用方。

然而,由于代码的数量一般都比较庞大,开发人员一般只拥有自己模块的源代码,其他模块对其来说是黑盒,而当前ide的使用方式,导致在查找调用方时局限于当前模块,而无法获取到整个产品的调用关系。同时也因此不能在模块层面上去获取模块之间的相互依赖关系。



技术实现要素:

本申请提供一种获取代码依赖关系的方法,以解决现有技术中的上述问题。本申请另外提供一种获取代码依赖关系的装置以及一种电子设备。

本申请提供一种获取代码依赖关系的方法,所述获取代码依赖关系的方法,包括:

解析字节码文件,获取所述字节码文件中各个类的信息以及各个成员信息;

基于所述各个类的信息,确定各个类中的方法对于其他方法的调用关系以及各个类中的继承关系,并在各个类之间添加依赖关系;

根据各个所述成员信息,获取所述成员信息对于已获取的其他成员信息的访问,在各个成员信息中添加依赖关系;

持久化所述类之间的依赖关系以及所述成员的依赖关系。

优选的,所述类的信息,至少包括:

类名以及父类的信息。

优选的,所述基于所述各个类的信息,确定各个类中的方法对于其他方法的调用关系以及各个类中的继承关系,并在各个类之间添加依赖关系,包括:

根据各个所述类的信息,构造对应所述类的信息的类节点;

在构造所述类节点之后,根据所述类的信息中的继承关系,获取所述类节点的父类的信息;

基于所述父类的信息确定对应的父类节点,在所述类节点与对应的所述父类节点之间添加依赖关系;

根据所述类中的方法对于其他方法调用关系,确定所述类调用的方法所属的类;

基于已确定的所述类获取对应的类节点,在所述类节点与调用的方法对应的所述类节点之间添加依赖关系。

优选的,在所述构造对应所述类的信息的类节点的步骤之后,包括:

将构造的所述类节点注册至节点仓库。

优选的,所述基于所述父类的信息确定对应的父类节点,包括:

在节点仓库内,基于所述父类的信息查找对应的父类节点;

若查找到对应的父类节点,则执行所述在所述类节点与对应的所述父类节点之间添加依赖关系的步骤;

若未查找到对应的父类节点,则在所述类节点内创建父类监听器。

优选的,在所述构造对应所述类的信息的类节点的步骤之后,包括:

回调所述类节点创建的父类监听器;

若构造的所述类节点,是所述父类监听器所监听的类节点,则将构造的所述类节点作为父类节点,与创建所述父类监听器的类节点之间添加依赖关系。

优选的,所述父类节点,包括:类节点以及接口节点。

优选的,所述成员信息,至少包括:

指令、方法列表、方法名、属性列表以及属性名。

优选的,在所述根据各个所述成员信息,获取所述成员信息对于已获取的其他成员信息的访问,在各个成员信息中添加依赖关系的步骤之前,包括:

根据各个所述成员信息,构造对应所述成员信息的方法节点以及属性节点。

优选的,所述根据各个所述成员信息,获取所述成员信息对于已获取的其他成员信息的访问,在各个成员信息中添加依赖关系,包括:

逐条解析所述成员信息中的指令,确定所述指令的类型;

根据所述指令的类型,访问对应的成员信息;

根据所述指令访问的成员信息,在所述指令所属的节点与对应所述成员信息的节点中添加依赖关系。

优选的,所述解析指令,确定所述指令的类型,包括:

解析所述指令获取操作码,根据所述指令的操作码,确定所述指令的类型。

优选的,所述成员类型,包括:属性以及方法;

相应的,所述确定所述指令的类型,包括:

属性访问指令以及方法调用指令。

优选的,所述根据所述指令的类型,访问对应的成员信息,包括:

若所述指令的类型为属性访问指令,则获取所述指令访问的属性对应的属性节点;

若所述指令的类型为方法调用指令,则获取所述指令调用的方法对应的方法节点。

优选的,所述根据所述指令访问的成员信息,在所述指令所属的节点与对应所述成员信息的节点中添加依赖关系,包括:

若所述指令访问了属性节点,则在所述指令所属的方法节点与所述属性节点之间,添加属性依赖关系;

若所述指令访问了方法节点,则在所述指令所属的方法节点与所述方法节点之间,添加方法依赖关系。

优选的,在所述指令所属的节点与对应所述成员信息的节点中添加依赖关系的步骤之后,包括:

获取所述指令访问的所述成员信息对应的类节点,将获取的所述类节点,与所述指令所属的类对应类节点之间添加类依赖关系。

优选的,在所述获取所述指令访问的所述成员信息对应的类节点,将获取的所述类节点,在所述指令所属的类对应类节点之间添加类依赖关系的步骤之后,包括:

确定所述指令访问的所述成员信息所属的模块,将所述模块,与所述指令所属的模块之间添加类依赖关系。

优选的,所述解析字节码文件,包括:

基于预设的字节码框架,解析所述字节码文件。

优选的,所述持久化所述类之间的依赖关系以及所述成员的依赖关系,包括:

以结构树的方式输出所述类之间的依赖关系;

以依赖图的方式输出所述成员之间的依赖关系。

优选的,所述解析字节码文件,还包括:

获取所述字节码文件的包名以及配置列表;

相应的,在所述持久化所述类之间的依赖关系以及所述成员的依赖关系的步骤之前,包括:

根据所述包名以及所述配置列表,过滤冗余信息。

相应的,本申请实施例还提供了一种获取代码依赖关系的装置,所述获取代码依赖关系的装置,包括:

文件解析单元,用于解析字节码文件,获取所述字节码文件中各个类的信息以及各个成员信息;

类依赖添加单元,用于基于所述各个类的信息,确定各个类中的方法对于其他方法的调用关系以及各个类中的继承关系,并在各个类之间添加依赖关系;

方法依赖添加单元,用于根据各个所述成员信息,获取所述成员信息对于已获取的其他成员信息的访问,在各个成员信息中添加依赖关系;

持久化单元,用于持久化所述类之间的依赖关系以及所述成员的依赖关系。

此外,本申请实施例还提供了一种电子设备,包括:

处理器;

存储器,用于存储代码依赖获取程序,所述程序在被所述处理器读取执行时,执行如下操作:解析字节码文件,获取所述字节码文件中各个类的信息以及各个成员信息;基于所述各个类的信息,确定各个类中的方法对于其他方法的调用关系以及各个类中的继承关系,并在各个类之间添加依赖关系;根据各个所述成员信息,获取所述成员信息对于已获取的其他成员信息的访问,在各个成员信息中添加依赖关系;持久化所述类之间的依赖关系以及所述成员的依赖关系。

与现有技术相比,本申请具有以下优点:

本申请提供的一种获取代码依赖关系的方法、装置以及电子设备,通过解析字节码文件,获取所述字节码文件中各个类的信息以及各个成员信息;基于所述各个类的信息,确定各个类中的方法对于其他方法的调用关系以及各个类中的继承关系,并在各个类之间添加依赖关系;根据各个所述成员信息,获取所述成员信息对于已获取的其他成员信息的访问,在各个成员信息中添加依赖关系;持久化所述类之间的依赖关系以及所述成员的依赖关系。所述技术方案通过对字节码文件进行全依赖扫描,构造客户端代码层面模块/类/方法/属性多个维度的层次树和依赖图,从而简单便捷的获取多个层面的依赖关系,从而为架构分析,模块重构,质量保障提供数据支撑。

附图说明

为了更清楚地说明本申请实施例或现有技术中的技术方案,下面将对实施例或现有技术描述中所需要使用的附图作简单地介绍,显而易见地,下面描述中的附图仅仅是本申请中记载的一些实施例,对于本领域普通技术人员来讲,还可以根据这些附图获得其他的附图。

图1示出了根据本申请的实施例提供的获取代码依赖关系的方法的流程图;

图2示出了根据本申请的实施例提供的基于所述各个类的信息,确定各个类中的方法对于其他方法的调用关系以及各个类中的继承关系,并在各个类之间添加依赖关系的流程图;

图3示出了根据本申请的实施例提供的基于所述各个类的信息,确定各个类中的方法对于其他方法的调用关系以及各个类中的继承关系,并在各个类之间添加依赖关系的实现过程;

图4示出了根据本申请的实施例提供的根据各个所述成员信息,获取所述成员信息对于已获取的其他成员信息的访问,在各个成员信息中添加依赖关系的流程图;

图5示出了根据本申请的实施例提供的以结构树的方式输出所述类之间的依赖关系的示意图;

图6示出了根据本申请的实施例提供的以依赖图的方式输出所述成员之间的依赖关系的示意图;

图7示出了根据本申请的实施例提供的获取代码依赖关系的装置的示意图;

图8示出了根据本申请的实施例提供的电子设备的示意图。

具体实施方式

为了能够更清楚地理解本申请的上述目的、特征和优点,下面结合附图和具体实施方式对本申请进行进一步的详细描述。需要说明的是,在不冲突的情况下,本申请的实施例及实施例中的特征可以相互组合。

在下面的描述中阐述了很多具体细节以便于充分理解本申请。但是,本申请能够以很多不同于在此描述的其它方式来实施,本领域技术人员可以在不违背本申请内涵的情况下做类似推广,因此,本申请不受下面公开的具体实施的限制。

本申请的实施例提供了一种获取代码依赖关系的方法;本申请实施例同时涉及一种获取代码依赖关系的装置以及一种电子设备。在下面的实施例中逐一进行详细说明。

目前,在实际应用过程中,获取依赖关系是通过集成开发环境(ide),如eclipse,intelij等来实现。实现方式为导入项目源码,经过编译后,可以通过ide的调用方查找功能查看某个类/方法/属性的调用方。然而,由于代码的数量一般都比较庞大,开发人员一般只拥有自己模块的源代码,其他模块对其来说是黑盒,而当前ide的使用方式,导致在查找调用方时局限于当前模块,而无法获取到整个产品的调用关系。同时也因此不能在模块层面上去获取模块之间的相互依赖关系。针对这一问题,在本申请实施例中,通过解析字节码文件,根据各个所述类的信息中的继承关系,以及根据各个所述成员信息对于其他成员信息的访问,从而获取所述类之间的依赖关系以及所述方法的依赖关系,实现了构造客户端代码层面模块/类/方法/属性多个维度的层次树和依赖图,从而简单便捷的获取多个层面的依赖关系,从而为架构分析,模块重构,质量保障提供数据支撑。

在详细描述本实施例的具体步骤之前,先对本技术方案涉及的字节码文件作简要说明。

所述字节码文件,是由字节码构成的文件,其中所述字节码是java虚拟机执行的一种指令格式。所述字节码文件可以为java源代码经过编译后生成的文件,例如.class文件。.class字节码文件有一个统一标准的规范,里面是jvm运行的时需要的相关指令。

代码依赖是指:代码层面上存在的调用与被调用关系。程序的执行过程就是方法的调用过程,有方法调用会促使对象跟对象之间产生依赖。"方法调用"是最常见产生依赖的原因,一个对象与其它对象会通信,通信通常情况下就意味着有方法的调用,有方法的调用就意味着这两个对象之间存在依赖关系,另外常见的一种产生依赖的原因是:继承。

继承是指:面向对象软件技术当中的一个概念,继承可以使得子类具有父类的属性和方法或者重新定义、追加属性和方法等。如果一个类别a“继承自”另一个类别b,就把这个a称为“b的子类”,而把b称为“a的父类”。继承可以使得子类具有父类的各种属性和方法,而不需要再次编写相同的代码。

本申请的实施例提供了一种获取代码依赖关系的方法,所述获取代码依赖关系的方法通过解析字节码文件从而获取所述类之间的依赖关系以及所述方法的依赖关系,实现了构造客户端代码层面模块/类/方法/属性多个维度的层次树和依赖图。所述获取代码依赖关系的方法实施例如下:

请参考图1,其示出了根据本申请的实施例提供的获取代码依赖关系的方法的流程图。

所述获取代码依赖关系的方法,包括:

步骤s101,解析字节码文件,获取所述字节码文件中各个类的信息以及各个成员信息。

在本实施例中,通过解析字节码文件,可以获取其中的各个类的信息,还可以获取所述类所包含的方法。

需要说明的是,java字节码文件是8位字节的二进制流。数据项按顺序存储在.class文件中,相邻的项之间没有间隔。在java字节码文件中包含了许多大小不同的项,由于每一项的结构都有严格规定,这使得.class文件能够从头到尾被顺利地解析。在.class文件内的constantpool项用于存放类中各种文字字符串、类名、方法名和接口名称、final变量以及对外部类的引用信息等常量;在.class文件内的access_flag项指明了该文件中定义的是类还是接口(一个.class文件中只能有一个类或接口),同时还指名了类或接口的访问标志,例如:public,private,abstract等信息;methods项对类或接口中声明的方法进行了细致的描述,例如:方法的名称、参数和返回值类型等。需要注意的是,methods列表里仅存放了本类或本接口中的方法,并不包括从超类和父接口继承而来的方法。

在具体实施时,解析所述字节码文件可以利用scanner扫描器结合asm(java字节码操纵框架)对所述字节码文件的constantpool项和methods项进行解析,得到所述字节码文件中各个类的信息以及各个成员信息。

需要说明的是,asm框架是一个java字节码操纵框架,它能被用来动态生成类或者增强既有类的功能。asm可以直接产生二进制class文件,也可以在类被加载入java虚拟机之前动态改变类行为。javaclass被存储在严格格式定义的.class文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及java字节码(指令)。asm框架从类文件中读入信息后,能够改变类行为,分析类的信息,甚至能够根据用户要求生成新类。

在本实施例中,在所述解析字节码文件之后,还可以定义一个全局的节点仓库,用来存储字节码文件解析过程中获取的各个类的信息以及各个成员信息。

在执行步骤s101解析字节码文件时,所述字节码文件的数量可以为一个,也可以是一个以上,对于一个以上的情况,本步骤可以依次解析每个字节码文件,并在节点仓库中记录解析得到的上述各种信息。

在本实施例中,所述字节码文件,可以通过打包文件jar或war提供。除此之外,还包括一些特有的含有字节码文件的代码模块,例如:aar,awb。

步骤s103,基于所述各个类的信息,确定各个类中的方法对于其他方法的调用关系以及各个类中的继承关系,并在各个类之间添加依赖关系。

在本实施例中,所述基于所述各个类的信息,确定各个类中的方法对于其他方法的调用关系以及各个类中的继承关系,并在各个类之间添加依赖关系,可以采用如下方式实现:针对于每一个类的信息,判断所述类的信息是否继承了其他的类,若是,则将所述类的信息作为子类,将所述类的信息继承的类作为父类,记录所述子类与所述父类之间的依赖关系;并且针对于每一个类的信息,判断所述类是否调用了其他类中的方法,若是,则记录所述类与调用的方法所在的类之间的依赖关系。

需要说明的是,本步骤是通过解析字节码文件后,为获取到的每一个类的信息进行判断该类是否继承了其他的类,从而在各个类之间添加依赖关系。

在本实施例中,通过解析所述字节码文件后获取的所述类的信息,至少包括:类名以及父类的信息。例如:具有关键字extends的类(classaextendsb),通过解析后获取到的类的信息为:类名a,父类的信息b;不具有关键字extends的类,过解析后获取到的类的信息为:类名c。

在本步骤中,所述根据各个所述类的信息中的继承关系,在各个类之间添加依赖关系,具体可以通过如下步骤进行实现:

请参考图2,其示出了根据本申请的实施例提供的基于所述各个类的信息,确定各个类中的方法对于其他方法的调用关系以及各个类中的继承关系,并在各个类之间添加依赖关系的流程图。

步骤s103-1,根据各个所述类的信息,构造对应所述类的信息的类节点。

在本实施例中,所述根据各个所述类的信息,构造对应所述类的信息的类节点,可以采用如下方式实现:根据在步骤s101中对所述字节码文件解析出的各个类的信息,构造相应的类节点。

需要说明的是,本步骤是通过asm框架解析源代码经过编译产生的字节码文件后,为获取到的每一个类的信息构造相应的类节点,且构造出的所述类节点已经包含父类和父接口的信息。

在本实施例中,针对于每一个类的信息构造的类节点可以注册至已定义的节点仓库内。其中,类节点是根据对应的类的信息所构建的,所以所述类节点可以记录构造该节点的类的相关信息,例如:类名称和父类的信息。

步骤s103-2,在构造所述类节点之后,根据所述类的信息中的继承关系,获取所述类节点的父类的信息。

在本实施例中,所述根据所述类的信息中的继承关系,获取所述类节点的父类的信息,可以采用如下方式实现:根据所述类的信息中的继承关系,确定所述类的信息所继承的父类,并基于所述父类获取所述类节点的父类的信息。

步骤s103-3,基于所述父类的信息确定对应的父类节点,在所述类节点与对应的所述父类节点之间添加依赖关系;

在本实施例中,所述基于所述父类的信息确定对应的父类节点,在所述类节点与对应的所述父类节点之间添加依赖关系,可以采用如下方式实现:基于所述父类的信息确定并查找对应的父类节点,在获取到所述父类节点后,在所述类节点与对应的所述父类节点之间添加依赖关系。

步骤s103-4,根据所述类中的方法对于其他方法调用关系,确定所述类调用的方法所属的类。

需要说明的是,在java代码中,类之间的依赖关系不仅仅是继承关系还有因为方法间的相互调用导致的所在类之间存在的依赖关系,例如:类a中的方法a调用了类b中的方法b,由于方法间的相互调用导致的类之间的依赖。

步骤s103-5,基于已确定的所述类获取对应的类节点,在所述类节点与调用的方法对应的所述类节点之间添加依赖关系。

在本实施例中,所述基于已确定的所述类获取对应的类节点,在所述类节点与调用的方法对应的所述类节点之间添加依赖关系,可以采用如下方式实现:基于已确定的所述类的信息,在节点仓库内通过所述类的类名查找对应的类节点,在获取到调用的方法对应的所述类节点后,在在所述类节点与调用的方法对应的所述类节点之间添加依赖关系。

在基于所述父类的信息确定对应的父类节点时,具体可以通过如下步骤进行实现:

在节点仓库内,基于所述父类的信息查找对应的父类节点;

若查找到对应的父类节点,则执行所述在所述类节点与对应的所述父类节点之间添加依赖关系的步骤;

若未查找到对应的父类节点,则在所述类节点内创建父类监听器。

在本实施例中,所述在节点仓库内,基于所述父类的信息查找对应的父类节点,可以采用如下方式实现:根据确定了的所述父类的信息的类名称,在所述节点仓库内查找与所述父类名称相同的父类节点。

可以理解的,在执行到此步骤时,在节点仓库内注册的节点均为类节点,而在本步骤中查找的对应的父类节点,其本质上是类节点,但是该类节点中的属性或者方法被其他的类节点所继承使用,所以为了区分别其他类节点继承的类节点,将这些类节点称为父类节点。

需要说明的是,所述类的信息构造对应的类节点,是对从所述字节码文件中解析出的各个类的信息,逐个构造对应的类节点,所以根据所述类的信息构造对应的类节点的顺序,可分为以下两种情况:

未有父类节点时:

当根据所述类的信息构造对应的类节点,并将所述类节点注册至节点仓库后,若当前类节点为第一个构造出的类节点,且当前类节点中具有关键字extends,那么在所述节点仓库内必然未有父类节点;或者当前类节点中具有关键字extends,但由于所述类节点是按照一定顺序进行构建的,此时当前类节点所需求的父类节点还未进行构建,此时都需要在当前类节点中创建用于监听父类节点的父类监听器,在创建相应的父类监听器后,则继续对从所述字节码文件中解析出的其他类的信息构造相应的类节点。

已有父类节点时:

当根据所述类的信息构造对应的类节点,并将所述类节点注册至节点仓库后,若当前类节点在所述节点仓库中查找到相应的父类节点,则执行所述在所述类节点与对应的所述父类节点之间添加依赖关系的步骤,再添加依赖关系后,则继续对从所述字节码文件中解析出的其他类的信息构造相应的类节点。

具体的,在节点仓库内查找对应的父类节点时,可以通过以确定的父类的信息的名称,与节点仓库中的各类节点逐一进行匹配,若找到了相匹配的类节点,即:参与匹配的双方的上述信息一致,则判断出查找到的所述类节点为需求的父类节点,并执行所述在所述类节点与对应的所述父类节点之间添加依赖关系的步骤;相应的,若未找到能相匹配的类节点,即:在所述节点仓库内,未注册与所述父类的信息的名称相同的类节点,则判断出在所述节点仓库内,还未注册需求的父类节点,则在所述类节点内创建父类监听器。

例如:在所述节点仓库内已注册了的类节点包括:类节点b,类节点c,类节点e,若当前类节点a的父类的信息中父类的名称为b,则在节点仓库内查找名称为b的类节点,由于在所述节点仓库内已注册了类节点b,则以当前类节点a作为子类,类节点b作为父类,在类节点a与类节点b之间添加依赖关系;若当前类节点a的父类的信息中父类的名称为d,则在节点仓库内查找名称为d的类节点,由于在所述节点仓库内还未注册类节点d,则在类节点a内创建用于监听类节点d的父类监听器。

在本实施例中,由于在构建类节点时,是逐个进行构造的,所以在构造出新的类节点时,该类节点可能是已注册的类节点的父类节点,所以在执行步骤s103-1构造出每一个新的对应所述类的信息的类节点之后,需要基于已有的父类监听器执行如下步骤:

回调所述类节点创建的父类监听器;

若构造的所述类节点,是所述父类监听器所监听的类节点,则将构造的所述类节点作为父类节点,与创建所述父类监听器的类节点之间添加依赖关系。

需要说明的是,所述监听器实际上是一个类,这个类实现了特定的接口,在启动的时候就可以实例化这个类,启动监听器。当范围对象的状态发生变化的时候,则自动调用监听器对象中的方法。

在本实施例中,所述监听器可以涉及以下三个对象:

事件:对组件的一个操作,称之为一个事件(构造新的类节点)

事件源:发生事件的组件就是事件源

事件监听器(处理器):监听并负责处理事件的方法(将构造的所述类节点作为父类节点,与创建所述父类监听器的类节点之间添加依赖关系)

需要说明的是,所述回调所述类节点创建的父类监听器,是指:在构造了新的类节点时,调用已创建的父类监听器。

下面通过一个例子说明上述步骤的实现过程。

请参考图3,其示出了根据本申请的实施例提供根据各个所述类的信息中的继承关系,在各个类之间添加依赖关系的实现过程。

在图3中,实线代表实际处理的过程,虚线代表与节点仓库之间的交互过程。

下面对实例中涉及的类节点进行说明。在节点仓库内可以具有已经注册的类节点a,类节点b以及类节点c,且类节点a已经创建了相应的父类监听器并监听类节点d,暂未构建的类节点d继承了其他的类f,而暂未构建的类节点e继承了类c。

下面结合图3,对类之间添加依赖关系的实现过程进行说明。

在执行初始步骤,据各个所述类的信息,构造对应所述类的信息的类节点后,构造出类节点d;

将构造的类节点d注册至节点仓库内;

由于构造了新的类节点,回调在节点仓库中已注册类节点创建的父类监听器,由于只有类节点a创建了相应的父类监听器,则只回调类节点a的父类监听器;

由于类节点a创建的父类监听器是用于监听类节点d的,所以在构造出类节点d之后,由于触发了类节点a的父类监听器,所以以类节点a作为子类,以类节点d作为父类,在类节点a于类节点d之间添加依赖关系;

根据构造的类节点d中的继承关系,判断该类节点d中是否继承了其他的类;

由于类节点d继承了其他的类f,所以在访问节点仓库,并基于所述父类的信息查找对应的父类节点时,由于查询不到类节点f,则会在类节点d中创建用于监听类节点f的父类监听器;

在创建父类监听器之后,由于从字节码文件中解析出的类的信息中,还有未构造类节点的类的信息,所以返回初始步骤并继续构造相应的类节点e;

将构造的类节点e注册至节点仓库内;

由于构造了新的类节点,回调在节点仓库中已注册类节点创建的父类监听器,由于只有类节点a创建了相应的父类监听器,则只回调类节点a的父类监听器;

由于类节点a创建的父类监听器是用于监听类节点d的,所以在构造出类节点e之后,由于未触发父类监听器,所以直接根据构造的类节点e中的继承关系,判断该类节点e中是否继承了其他的类;

由于类节点e继承了其他的类c,所以在访问节点仓库,并基于所述父类的信息查找对应的父类节点时,由于查询到了类节点c,所以以类节点e作为子类,以类节点c作为父类,在类节点e于类节点c之间添加依赖关系;

由于从字节码文件中解析出的类的信息中,已全部构造了类节点,则结束上述对类之间添加依赖关系的步骤。

下面通过一个实际例子说明,在所述类节点与对应的所述父类节点之间添加依赖关系的过程。

定义一个person类:

定义一个student类继承person类:

由于student类具有关键字extends,则说明student类继承了person类,即:student类为子类,person类为父类,则在节点仓库中,在student类节点与person类节点之间建立student类节点依赖于person类节点的依赖关系。

需要说明的是,除了可以以类节点作为父类节点外,还可以以接口节点作为父类节点。由于接口里面可以提供方法,但不提供这个方法的具体操作,所以类是接口实现的载体,即类实现接口中定义的方法,则类可以去实现这个接口,在类中去写方法的具体实现,即:可以将接口作为父类的信息,并通过其他类去实现这个接口。

具体的,在执行步骤s103时,还可以将接口信息构造为接口节点,并根据类节点于接口节点之间的实现关系,在类与接口之间添加依赖关系。由于与上述实施例中的实现过程较为相似,具体内容可以参考上述实施例,对于类与接口之间添加依赖关系的过程,在此不再赘述。

下面通过一个实际例子说明,类节点与接口节点之间添加依赖关系的过程。

定义一个action接口:

定义一个student类实现了action接口:

由于student类具有关键字implements,则说明student类实现了action接口,即:student类为子类,action接口为父类,则在节点仓库中,在student类节点与action接口节点之间建立student类节点依赖于action接口节点的依赖关系。

步骤s105,根据各个所述成员信息,获取所述成员信息对于已获取的其他成员信息的访问,在各个成员信息中添加依赖关系。

在本实施例中,所述根据各个所述成员信息,获取所述成员信息对于已获取的其他成员信息的访问,在各个成员信息中添加依赖关系,可以采用如下方式实现:针对于每一个成员信息,判断所述成员信息是否访问了已获取的其他成员信息,若是,则在所述成员信息与所述成员信息所访问的成员信息之间添加依赖关系。

需要说明的是,本步骤是通过解析字节码文件后,为获取到的每一个成员信息进行判断该类是否访问了已获取的其他成员信息,从而在成员信息中添加依赖关系。

在本实施例中,所述成员信息,至少包括:

指令、方法列表、方法名、属性列表以及属性名。

需要说明的是,从类的结构上而言,其内部可以有如下几种常见形态:属性(包括类属性和实例属性)、方法(包括类方法和实例方法),即:属性和方法是包含在类中的。

由于所述属性和方法是包含在类中的,所以在执行步骤s105根据各个所述成员信息,获取所述成员信息对于已获取的其他成员信息的访问,在各个成员信息中添加依赖关系之前,还需要根据各个所述成员信息,构造所述成员信息(属性和方法)对应的节点,具体可以在步骤s103构造类节点时,构造所述类中属性和方法的节点,即:构造相应的方法节点以及属性节点。

在本实施例中,在构造出相应的方法节点以及属性节点之后,可以注册至已定义的节点仓库内。其中,所述方法节点可以记录构造该节点的方法的相关信息,例如:方法名;同样的,所述属性节点可以记录构造该节点的属性的相关信息,例如:属性名。

具体的,所述根据各个所述成员信息,获取所述成员信息对于已获取的其他成员信息的访问,在各个成员信息中添加依赖关系,可以采用如下方式实现:通过解析字节码文件,对于解析出的各个指令进行遍历,通过指令的操作码(opcode)来判断当前指令的类型,并根据当前指令的类型,与当前方法执行序列所访问的成员信息,添加相应的依赖关系。

在本步骤中,所述根据各个所述成员信息,获取所述成员信息对于已获取的其他成员信息的访问,在各个成员信息中添加依赖关系,具体可以通过如下步骤进行实现:

请参考图4,其示出了根据本申请的实施例提供的根据各个所述成员信息,获取所述成员信息对于已获取的其他成员信息的访问,在各个成员信息中添加依赖关系的流程图。

步骤s105-1,逐条解析所述成员信息中的指令,确定所述指令的类型。

在本实施例中,所述逐条解析所述成员信息中的指令,确定所述指令的类型,可以采用如下方式实现:根据步骤s101中对所述字节码文件解析出的指令,逐条确定所述指令的操作码,从而确定指令的类型。

可以理解的,所述指令可以访问的成员类型,包括:属性以及方法,所以针对于所述指令所访问的不同成员,所述指令的类型,包括:属性访问指令以及方法调用指令。

步骤s105-2,根据所述指令的类型,访问对应的成员信息。

在本实施例中,所述根据所述指令的类型,访问对应的成员信息,可以采用如下方式实现:根据所述所述指令的类型,确定所述指令所要访问的成员类型,并基于所述成员类型在节点仓库内查找对应的节点进行访问。

需要说明的是,所述指令根据类型的不同,会在所述节点仓库内查找不同类型的节点进行访问。例如:

若所述指令的类型为属性访问指令,则获取所述指令访问的属性节点;

若所述指令的类型为方法调用指令,则获取所述指令调用的方法节点。

例如:指令是一条方法调用指令(即指令opcode为invokestatic,invokevirtual,invokeinterface,invokespecial的一个)则可以解析出该指令所调用方法的类名,方法名和参数列表,从而找到找到该方法的方法节点。

需要说明的是,若当前指令是方法调用指令,则通过解析出被调用方法的方法名称、参数列表和返回值类型,以及方法所在类的类名,然后从节点仓库中查找与所述被调用方法对应的方法节点;同样的道理,若当前指令是属性访问指令,则通过解析出被访问的属性名称以及属性所在类的类名,然后从节点仓库中查找与所述被访问的属性对应的属性节点。

具体的,在从所述节点仓库中查找方法节点时,

是通过包名和所述方法所在类的类名,与节点仓库中的各节点逐一进行匹配,直至找到相匹配的类节点,在所述类节点的方法列表中查找对应的方法节点,如果找不到方法节点,则在该类节点的父节点中进行查找,不断向父节点回溯,直到找到对应的方法节点为止;同样的道理,在从所述节点仓库中查找属性节点时,先通过包名和所述属性所在类的类名在所述节点仓库中查找类节点,在查找到所述方法节点后,在所述类节点的属性列表中查找对应的属性节点,如果找不到属性节点,则在该类节点的父类节点中进行查找,不断向父节点回溯,直到找到对应的属性节点为止。例如:被访问的属性name在类a中,类a在包com.alibaba.test中,则先通过com.alibaba.test.a在节点仓库中进行查找类节点,然后类节点的属性节点列表中查找名称为name的属性节点。

下面通过一个实际例子说明,若所述指令的类型为方法调用指令,则获取所述指令调用的方法节点的过程。

定义一个test类中的add()方法:

定义一个student类调用add()方法:

由于要对student类中的数学和英语成绩进行求和,且student类中没有add()方法,所以定义外部类test,由student类调用外部类test的add()方法对数学和英语成绩进行求和。在调用add()方法时,student通过类的实例化进行调用,通过关键字new对类进行实例化,通过sum=test.add(maths,english)对实例化的类进行add方法调用,并引入参数进行计算。

可以理解的,在所述指令的类型为方法调用指令时,所述指令调用的方法为add()方法,所以在节点仓库内需要获取的方法节点为add节点。

需要说明的是,当被调用方法声明为static时,可以在其他方法中直接调用本类中的方法,具体如下:

定义一个student类调用add()方法:

下面通过一个实际例子说明,若所述指令的类型为属性访问指令,则获取所述指令访问的属性节点的过程。

定义一个student类访问属性maths:

需要说明的是,上述例子中是以打印方法println()调用属性maths,并输出maths的值。

可以理解的,在所述指令的类型为属性访问指令时,所述指令访问属性maths,所以在节点仓库内需要获取的属性节点为maths节点。

步骤s105-3,根据所述指令访问的成员信息,在所述指令所属的节点与对应所述成员信息的节点中添加依赖关系。

在本实施例中,所述根据所述指令访问的成员信息,在所述指令所属的节点与对应所述成员信息的节点中添加依赖关系,可以采用如下方式实现:基于所述指令,从节点仓库中查找到的需要进行访问的成员信息对应的节点,并在获取到所述节点后,在所述指令所属的节点与获取的所述节点之间添加依赖关系。

需要说明的是,由于所述指令访问的成员信息的类型不同,所以在执行步骤s105-3根据所述指令访问的成员信息,在所述指令与对应的所述成员信息中添加依赖关系时,需要根据成员信息的类型,分别添加依赖关系,具体包括如下步骤:

若所述指令访问了属性节点,则在所述指令所属的方法节点与所述属性节点之间,添加属性依赖关系;

若所述指令访问了方法节点,则在所述指令所属的方法节点与所述方法节点之间,添加方法依赖关系。

需要说明的是,由于属性由方法所调用,且方法需要被包含在类中,而当一个类中的方法调用其他类中的方法时,因为方法间的相互调用导致的所在类之间存在依赖关系,所以在步骤s105-3在所述指令所属的节点与对应所述成员信息的节点中添加依赖关系之后,还需要确定被访问的类节点与所述指令所属的类对应类节点之间的依赖关系,具体包括如下步骤:

获取所述指令访问的所述成员信息对应的类节点,将获取的所述类节点,与所述指令所属的类对应类节点之间添加类依赖关系。

在java开发中,类是被封装在包内的,一个包中可以包含多各类,而包是按照需要实现功能被分配在相应的功能模块,一个功能模块可以包含该模块可以实现的多个功能的包,所以在所述添加所述指令所属的类与被访问的类节点之间的类依赖关系之后,还需要根据各个所述类所属的模块信息,添加所述模块信息之间的模块依赖关系,具体包括如下步骤:

确定所述指令访问的所述成员信息所属的模块,将所述模块,与所述指令所属的模块之间添加类依赖关系

需要说明的是,模块/类/方法/属性之间的关系是模块包含类,类包含方法/属性,如果某一个方法a的指令访问了某个属性b或者方法c,那么这个指令所在的方法a就依赖了属性b/方法c,方法a所在的类a就依赖了属性b/方法c所在的类b,类a所在模块就依赖类b所在的模块。

步骤s107,持久化所述类之间的依赖关系以及所述成员的依赖关系。

在本实施例中,所述持久化所述类之间的依赖关系以及所述成员的依赖关系,可以采用如下方式实现:将已获取的所述类之间的依赖关系以及所述成员的依赖关系,以预设的方式进行持久化,进行输出。

需要说明的是,所述持久化是将程序数据在持久状态和瞬时状态间转换的机制。就是瞬时数据(比如内存中的数据,是不能永久保存的)持久化为持久数据(比如持久化至数据库中,能够长久保存)。例如:是将内存中的对象存储在数据库中,或者存储在磁盘文件中、xml数据文件中等。

在本实施例中,所述预设的方式是指:以结构树的方式输出所述类之间的依赖关系,并以依赖图的方式输出所述成员之间的依赖关系。

下面仍然沿用上述实际的例子,对持久化所述类之间的依赖关系以及所述成员的依赖关系进行说明。

请参考图5,其示出了根据本申请的实施例提供的以结构树的方式输出所述类之间的依赖关系的示意图。

有图5可知,图中包括:类节点student,类节点person,接口节点action,类节点a以及类节点b。

其中,类节点student具有方法节点以及属性节点,且所述方法节点包括:study()以及println(),所述属性节点,包括:studentno,maths,english以及sum;

类节点person具有属性节点,且所述属性节点,包括:name,age,sex;

接口节点action具有具有方法节点,且所述方法节点包括:eat(),move();

在图5中,类节点student与类节点person之间的黑色箭头代表继承关系,类节点student继承于类节点person;类节点person与类节点a之间的黑色箭头代表继承关系,类节点person继承于类节点a;类节点student与类节点b之间的黑色箭头代表继承关系,类节点b继承于类节点student;类节点student与接口节点action之间的黑色箭头代表实现关系,类节点student实现接口节点action中定义的方法。

需要说明的是,在实际情况下,由于类的数量会远大于图5中类的数量,所以无法再图5中将完整类之间的依赖关系进行展示,所以图5仅仅是示意性的。

请参考图6,其示出了根据本申请的实施例提供的以依赖图的方式输出所述成员之间的依赖关系的示意图。

在本实施例中,所述依赖图,是指将模块/类/方法/属性之间的依赖关系以一种逻辑上的图的形式来进行展示,具体持久化之后会有两种形式:

第一种(图6.1)是以纯文本的方式来描述模块/类/方法/属性之间的依赖关系;

第二种(图6.2)是通过html页面,以一种交互性查询的方式来展示模块/类/方法/属性之间的依赖关系。

作为一种优选的实施方式,在本申请实施例提供的获取代码依赖关系的方法中,由于java项目中往往会携带有来自库文件(jdk/adk)的冗余信息,所以在持久化所述类之间的依赖关系以及所述成员的依赖关系时,也会将冗余信息进行持久化,为了避免上述情况的产生,在执行步骤s101解析字节码文件时,还需要获取所述字节码文件的包名以及配置列表。

在获取所述字节码文件的包名以及配置列表之后,就可以确定在所述字节码文件中的冗余信息,并在执行步骤s107持久化所述类之间的依赖关系以及所述成员的依赖关系之前,根据所述包名以及所述配置列表,过滤冗余信息。

在上述的实施例中,提供了一种获取代码依赖关系的方法,与上述获取代码依赖关系的方法相对应的,本申请还提供了一种获取代码依赖关系的装置。由于装置的实施例基本相似于方法的实施例,所以描述得比较简单,相关之处参见方法实施例的部分说明即可。下述描述的装置实施例仅仅是示意性的。所述获取代码依赖关系的装置实施例如下:

请参考图7,其示出了根据本申请的实施例提供的获取代码依赖关系的装置的示意图。

所述获取代码依赖关系的装置,包括:文件解析单元701、类依赖添加单元703、方法依赖添加单元705以及持久化单元707;

所述文件解析单元701,用于解析字节码文件,获取所述字节码文件中各个类的信息以及各个成员信息;

所述类依赖添加单元703,用于基于所述各个类的信息,确定各个类中的方法对于其他方法的调用关系以及各个类中的继承关系,并在各个类之间添加依赖关系;

所述方法依赖添加单元705,用于根据各个所述成员信息,获取所述成员信息对于已获取的其他成员信息的访问,在各个成员信息中添加依赖关系;

所述持久化单元707,用于持久化所述类之间的依赖关系以及所述成员的依赖关系。

可选的,所述文件解析单元701,解析字节码文件获取的所述类的信息,至少包括:

类名以及父类的信息。

可选的,所述类依赖添加单元703,包括:类节点构造子单元、父类获取子单元、类依赖添加子单元、调用类获取子单元以及调用类依赖添加子单元;

所述类节点构造子单元,用于根据各个所述类的信息,构造对应所述类的信息的类节点;

所述父类获取子单元,用于在构造所述类节点之后,根据所述类的信息中的继承关系,获取所述类节点的父类的信息;

所述类依赖添加子单元,用于基于所述父类的信息确定对应的父类节点,在所述类节点与对应的所述父类节点之间添加依赖关系;

所述调用类获取子单元,用于根据所述类中的方法对于其他方法调用关系,确定所述类调用的方法所属的类;

所述调用类依赖添加子单元,用于基于已确定的所述类获取对应的类节点,在所述类节点与调用的方法对应的所述类节点之间添加依赖关系。

可选的,所述类依赖添加单元703,还包括:注册子单元;

所述注册子单元,用于在所述构造对应所述类的信息的类节点之后,将构造的所述类节点注册至节点仓库。

可选的,所述父类获取子单元,包括:父类节点查找子单元、类依赖添加触发子单元以及监听器创建子单元;

所述父类节点查找子单元,用于在节点仓库内,基于所述父类的信息查找对应的父类节点;

所述类依赖添加触发子单元,用于若查找到对应的父类节点,则触发所述类依赖添加子单元;

所述监听器创建子单元,用于若未查找到对应的父类节点,则在所述类节点内创建父类监听器。

可选的,所述类依赖添加单元703,还包括:回调子单元以及监听子单元;

所述回调子单元,用于在所述构造对应所述类的信息的类节点之后,回调所述类节点创建的父类监听器;

所述监听子单元,用于若构造的所述类节点,是所述父类监听器所监听的类节点,则将构造的所述类节点作为父类节点,与创建所述父类监听器的类节点之间添加依赖关系。

可选的,所述父类获取子单元,获取的所述类节点,包括:类节点以及接口节点。

可选的,所述文件解析单元701,解析字节码文件获取的所述成员信息,至少包括:

指令、方法列表、方法名、属性列表以及属性名。

可选的,所述获取代码依赖关系的装置,还包括:方法属性节点构造单元;

所述方法属性节点构造单元,用于在所述根据各个所述成员信息,获取所述成员信息对于已获取的其他成员信息的访问,在各个成员信息中添加依赖关系之前,根据各个所述成员信息,构造对应所述成员信息的方法节点以及属性节点。

可选的,所述方法依赖添加单元705,包括:类型确定子单元、访问子单元以及方法依赖添加子单元;

所述类型确定子单元,用于逐条解析所述成员信息中的指令,确定所述指令的类型;

所述访问子单元,用于根据所述指令的类型,访问对应的成员信息;

所述方法依赖添加子单元,用于根据所述指令访问的成员信息,在所述指令所属的节点与对应所述成员信息的节点中添加依赖关系。

可选的,所述类型确定子单元,具体用于解析所述指令获取操作码,根据所述指令的操作码,确定所述指令的类型。

可选的,所述类型确定子单元,用于根据所述指令所访问的成员信息中的成员类型,包括:属性以及方法;

相应的,所述类型确定子单元,具体用于具体用于根据所述指令所访问的成员信息中的成员类型,确定的所述指令的类型,包括:属性访问指令以及方法调用指令。

可选的,所述访问子单元,包括:属性节点访问子单元以及方法节点访问子单元;

所述属性节点访问子单元,用于若所述指令的类型为属性访问指令,则获取所述指令访问的属性对应的属性节点;

所述方法节点访问子单元,用于若所述指令的类型为方法调用指令,则获取所述指令调用的方法对应的方法节点。

可选的,所述方法依赖添加子单元,包括:添加属性依赖子单元以及添加方法依赖子单元;

所述添加属性依赖子单元,用于若所述指令访问了属性节点,则在所述指令所属的方法节点与所述属性节点之间,添加属性依赖关系;

所述添加方法依赖子单元,用于若所述指令访问了方法节点,则在所述指令所属的方法节点与所述方法节点之间,添加方法依赖关系。

可选的,所述方法依赖添加单元705,还包括:添加类依赖子单元;

所述添加类依赖子单元,用于在所述指令所属的节点与对应所述成员信息的节点中添加依赖关系之后,根据所述指令所属的类的信息,添加所述指令所属的方法节点与所述类对应的类节点之间的依赖关系。

可选的,所述方法依赖添加单元705,还包括:添加类间依赖子单元;

所述添加类间依赖子单元,用于在所述指令所属的节点与对应所述成员信息的节点中添加依赖关系之后,获取所述指令访问的所述成员信息对应的类节点,将获取的所述类节点,与所述指令所属的类对应类节点之间添加类依赖关系。

可选的,所述方法依赖添加单元705,还包括:添加模块依赖子单元;

所述添加模块依赖子单元,用于在所述获取所述指令访问的所述成员信息对应的类节点,将获取的所述类节点,在所述指令所属的类对应类节点之间添加类依赖关系之后,确定所述指令访问的所述成员信息所属的模块,将所述模块,与所述指令所属的模块之间添加类依赖关系。

可选的,所述文件解析单元701,具体用于基于预设的字节码框架,解析所述字节码文件。

可选的,所述持久化单元707,包括:结构树输出单元以及依赖图输出单元;

所述结构树输出单元,用于以结构树的方式输出所述类之间的依赖关系;

所述依赖图输出单元,用于以依赖图的方式输出所述成员之间的依赖关系。

可选的,所述文件解析单元701,包括:配置获取单元;

所述配置获取单元,用于获取所述字节码文件的包名以及配置列表;

相应的,所述获取代码依赖关系的装置,还包括:过滤单元;

所述过滤单元,用于在所述持久化所述类之间的依赖关系以及所述成员的依赖关系之前,根据所述包名以及所述配置列表,过滤冗余信息。

在上述的实施例中,提供了一种获取代码依赖关系的方法以及一种获取代码依赖关系的装置,此外,本申请还提供了一种电子设备;所述电子设备实施例如下:

请参考图8,其示出了根据本申请的实施例提供的电子设备的示意图。

所述电子设备,包括:处理器801;存储器803;

所述存储器803,用于存储代码依赖获取程序,所述程序在被所述处理器读取执行时,执行如下操作:解析字节码文件,获取所述字节码文件中各个类的信息以及各个成员信息;基于所述各个类的信息,确定各个类中的方法对于其他方法的调用关系以及各个类中的继承关系,并在各个类之间添加依赖关系;根据各个所述成员信息,获取所述成员信息对于已获取的其他成员信息的访问,在各个成员信息中添加依赖关系;持久化所述类之间的依赖关系以及所述成员的依赖关系。

例如,所述电子设备为一台计算机,所述计算机解析字节码文件,获取所述字节码文件中各个类的信息以及各个成员信息;并基于所述各个类的信息,确定各个类中的方法对于其他方法的调用关系以及各个类中的继承关系,并在各个类之间添加依赖关系;再根据各个所述成员信息,获取所述成员信息对于已获取的其他成员信息的访问,在各个成员信息中添加依赖关系;最后持久化所述类之间的依赖关系以及所述成员的依赖关系。由于本电子设备使用上述获取代码依赖关系的方法,相关之处请参见上述获取代码依赖关系的方法的实施例说明,此处不再赘述。

在一个典型的配置中,计算设备包括一个或多个处理器(cpu)、输入/输出接口、网络接口和内存。

内存可能包括计算机可读介质中的非永久性存储器,随机存取存储器(ram)和/或非易失性内存等形式,如只读存储器(rom)或闪存(flashram)。内存是计算机可读介质的示例。

1、计算机可读介质包括永久性和非永久性、可移动和非可移动媒体可以由任何方法或技术来实现信息存储。信息可以是计算机可读指令、数据结构、程序的模块或其他数据。计算机的存储介质的例子包括,但不限于相变内存(pram)、静态随机存取存储器(sram)、动态随机存取存储器(dram)、其他类型的随机存取存储器(ram)、只读存储器(rom)、电可擦除可编程只读存储器(eeprom)、快闪记忆体或其他内存技术、只读光盘只读存储器(cd-rom)、数字多功能光盘(dvd)或其他光学存储、磁盒式磁带,磁带磁磁盘存储或其他磁性存储设备或任何其他非传输介质,可用于存储可以被计算设备访问的信息。按照本文中的界定,计算机可读介质不包括非暂存电脑可读媒体(transitorymedia),如调制的数据信号和载波。

2、本领域技术人员应明白,本申请的实施例可提供为方法、系统或计算机程序产品。因此,本申请可采用完全硬件实施例、完全软件实施例或结合软件和硬件方面的实施例的形式。而且,本申请可采用在一个或多个其中包含有计算机可用程序代码的计算机可用存储介质(包括但不限于磁盘存储器、cd-rom、光学存储器等)上实施的计算机程序产品的形式。

本申请虽然以较佳实施例公开如上,但其并不是用来限定本申请,任何本领域技术人员在不脱离本申请的精神和范围内,都可以做出可能的变动和修改,因此本申请的保护范围应当以本申请权利要求所界定的范围为准。

当前第1页1 2 
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1