具体实施方式
为使本申请的目的、技术方案和优点更加清楚,下面将结合本申请具体实施例及相应的附图对本申请技术方案进行清楚、完整地描述。显然,所描述的实施例仅是本申请一部分实施例,而不是全部的实施例。基于本申请中的实施例,本领域普通技术人员在没有做出创造性劳动前提下所获得的所有其他实施例,都属于本申请保护的范围。
本文将以sofa框架的系统为例来叙述本申请的技术方案,本申请各实施例中,所述方法的执行主体是计算机。
图1为本申请实施例提供的代码检测方法的过程,包括如下步骤:
S1:获取待检测的源代码文件中的模块Bundle的配置文件。
本申请实施例中,源代码文件是分支流的代码,并通过开放源代码的版本控制系统(Subversion,SVN)进行管理。则获取待检测的源代码文件的方式包括:
从版本控制系统的服务器中下载源代码文件到本地终端上,其中,通过SVN的checkout命令实现源代码文件的下载。在本申请其他实施例中,若所需检测的源代码文件存在本地终端上,则可以通过扫描本地代码路径或者系统名的方式获取到源代码文件。
sofa框架的系统是以实现特定功能的模块Bundle进行划分的,每个模块有自己独立的spring上下文,各模块spring上下文相互隔离,各模块Bundle内的方法能相互引用,不同模块之间需通过发布服务或者添加依赖(引用服务)的方式来实现互相访问。本申请实施例所需检测的是同一个模块Bundle中所存在的错误问题,故首先需要获取到待检测的源代码文件中所包含的各个模块Bundle所在的目录。本申请实施例中,sofa框架的配置文件包括xml文件(文件名后缀为.xml),本申请基于各个模块Bundle的目录中的xml文件来实现错误检测。
图2为本申请实施例提供的代码检测方法中获取源代码文件中的模块Bundle的配置文件的过程,包括如下步骤:
S11:确定源代码文件所在的目录。
本申请实施例中,若源代码文件所在本地终端上的目录是A,则判断目录A的子目录中是否存在名为“app”的目录,若存在目录app,则以目录A/app为起始目录进行扫描;若不存在目录app,则以目录A/系统名为起始路径进行扫描。
S12:判断源代码文件所在的目录的子目录中是否存在特定文件。
在以上实例中,基于所确定的目录A/app或者目录A/系统名,逐一扫描子目录中是否存在后缀是.MF的特定文件,若存在,则表明该子目录是一个模块Bundle的目录;若不存在,则表明该子目录不是一个模块Bundle的目录。例如:在子目录A/app/B/C下扫描到特定文件,则将其确定为一个模块Bundle的目录。
S13:若是,则获取该子目录中包含的配置文件。
在以上实例中,通过以上扫描手段,可确定待检测的源代码文件中所有模块Bundle的目录,并采用正则表达式获取到各个模块Bundle的目录下的各个xml文件。
在实际的应用中,因为xml文件中通常包含干扰测试结果的准确性的内容,如,#if与#end之间的内容:
#if(${core_test})
<bean id="ctubizDataSource"
class="com.alipay.zdatasource.ZDataFacSpring">
<property name="localTxDataSourceDO"ref="ctubiz"/>
</bean>
#else
<bean id="ctubizDataSource"
class="com.alipay.zdatasource.ZDataFacBean">
<property name="jndiName"value="ctubizDataSource"/>
</bean>
#end
在以上内容中,bean id的声明值是"ctubizDataSource",该声明值所对应的class的声明值包括两个,分别是"com.alipay.zdatasource.ZDataFacSpring"以及"com.alipay.zdatasource.ZDataFacBean",通过比对发现class的两个声明值不相等,即不唯一。然而,在源代码文件中,以上#if与#end之间所存在的声明值不唯一的情况并不属于代码错误,而是一种正常情况,所以如果直接扫描各个模块Bundle的xml文件,则有可能将以上#if与#end之间的声明值不唯一的情况视作代码错误,这显然是不合理的。为解决以上问题,在获取待检测的源代码文件中的模块Bundle的配置文件的步骤之后,读取配置文件中的第一声明值的步骤之前,本方法还包括删除配置文件中干扰内容的步骤。
图3为本申请实施例提供的代码检测方法中删除配置文件中干扰内容的具体过程,包括如下步骤:
S61:读取配置文件,并将配置文件的格式转换成字符串(String)格式。
本实施例中,将模块Bundle的xml文件进行拷贝,并将拷贝得到的xml文件进行格式转换。
S62:删除字符串(String)格式的文件中的干扰内容。
本实施例中,所述干扰内容是#if与#end之间的字符串。
S63:将删除干扰内容后的字符串格式转换成配置文件的格式,以进行后续的代码测试。
基于已经删除干扰内容的xml文件,进行如下的代码检测流程,包括步骤S2、步骤S3及步骤S4,具体地:
S2:读取配置文件中的第一声明值,其中,第一声明值是第一属性的声明。
S3:逐一读取配置文件中的与第一声明值相对应的第二声明值,其中,第二声明值是第二属性的声明。
S4:判断第二声明值是否唯一,若否,则判定源代码文件存在错误。
本申请实施例中,第一属性是组件Bean的标识,则第二属性是组件Bean所定义的类Class、或者组件Bean所定义的父类Parent。则第一声明值是组件Bean的标识的声明,第二声明值是组件Bean所定义的类Class、或者组件Bean所定义的父类Parent的声明。本实施例实现Bean类声明错误的检测。
以下将结合xml文件的示例叙述Bean类声明错误的检测过程。
xml文件的示例一如下:
<?xml version="1.0"encoding="GBK"?>
<bean id="baseDataPointExecutor"
class="com.alipay.securitydata.biz.dataset.datapoint.impl.BaseData1"/>
<bean id="baseDataPointExecutor"
class="com.alipay.securitydata.biz.dataset.datapoint.impl.BaseData2"/>
在以上示例一的xml文件中,首先获取到组件Bean的标识bean id所对应的第一声明值是"baseDataPointExecutor",随后通过扫描该xml文件,发现该xml文件中与"baseDataPointExecutor"相对应的类Class的第二声明值包括两个,分别是:"com.alipay.securitydata.biz.dataset.datapoint.impl.BaseData1"及"com.alipay.securitydata.biz.dataset.datapoint.impl.BaseData2",通过比对,发现以上两个第二声明值并不相同,即不唯一,此情况在程序启动时,会导致类Class的定义丢失,故判定以上情况是Bean类声明错误。
xml文件的示例二如下:
<?xml version="1.0"encoding="GBK"?>
<bean id="ctuEventCodeDAO"
class="com.alipay.ictu.common.dal.ictubiz.ibatis.IbatisCtuEventCodeDAO"parent="ctubizSqlMapClientDAO"/>
<bean id="ctuEventCodeDAO"
class="com.alipay.ictu.common.dal.ictubiz.ibatis.IbatisCtuEventCodeDAO"parent="ctubizSqlMapDAO"/>
在以上示例二的xml文件中,首先获取到组件Bean的标识bean id所对应的第一声明值是"ctuEventCodeDAO",随后通过扫描该xml文件,发现该xml文件中与"ctuEventCodeDAO"相对应的父类Parent的第二声明值包括两个,分别是:"ctubizSqlMapClientDAO"及"ctubizSqlMapDAO",通过比对,发现以上两个第二声明值并不相同,即不唯一,此情况在程序启动时,会导致父类Parent的定义丢失,故判定以上情况是Bean类声明错误。
本申请另一实施例中,第一属性是模块Bundle引用的服务,则第二属性是模块Bundle引用的服务的引用接口refInterface、或者模块Bundle引用的服务所定义的引用标识refUniqueid。则第一声明值是模块Bundle引用的服务的声明,第二声明值是模块Bundle引用的服务的引用接口refInterface、或者模块Bundle引用的服务所定义的引用标识refUniqueid的声明。本实施例实现引用服务错误的检测。
以下将结合xml文件的示例叙述引用服务错误的检测过程。
xml文件的示例三如下:
<?xml version="1.0"encoding="GBK"?>
<sofa:reference id="blackItemServiceClient"
interface="service.client.BlackItemServiceClient"
uniqueid="blackItemServiceClient"/>
<sofa:reference id="blackItemServiceClient"
interface="service.client.BlackItemServiceClient"
uniqueid="blackItemService"/>
在以上示例三的xml文件中,第二声明值包括:引用接口refInterface的声明值及引用标识refUniqueid的声明值。
首先获取到模块Bundle引用的服务sofa:reference id所对应的第一声明值是"blackItemServiceClient",随后通过扫描该xml文件,发现该xml文件中与"blackItemServiceClient"相对应的引用接口refInterface的声明值包括两个,通过比对发现两个refInterface声明值都是"service.client.BlackItemServiceClient",即唯一。继续扫描该xml文件,发现与"blackItemServiceClient"相对应的引用标识refUniqueid的声明值包括两个,分别是"blackItemServiceClient"及"blackItemService",通过比对发现以上两个refUniqueid的声明值不相同,即不唯一,则判定以上情况是引用服务错误。值得一提的是,在以上示例三的xml文件中,若refInterface声明值不唯一,refUniqueid的声明值唯一,则同样判定该情况是引用服务错误。
本申请又一实施例中,第二属性是模块Bundle发布的服务所定义的服务类serviceClass,则第一属性包括模块Bundle发布的服务的发布接口serviceInterface及模块Bundle发布的服务所定义的发布标识serviceUniqueid。第一声明值包括模块Bundle发布的服务的发布接口serviceInterface的声明值,以及模块Bundle发布的服务所定义的发布标识serviceUniqueid的声明值,第二声明值是模块Bundle发布的服务所定义的服务类serviceClass的声明。本实施例实现发布服务错误的检测。
以下将结合xml文件的示例叙述发布服务错误的检测过程。
xml文件的示例四如下:
<?xml version="1.0"encoding="GBK"?>
<bean id="uniformDataPointService"
class="com.alipay.UniServiceImpl"/>
<sofa:service ref="uniformDataPointService"
interface="com.alipay.datapoint.UniformDataPointService"uniqueId="12"/>
<bean id="uniformDataPoint"
class="com.alipay.UniPointImpl"/>
<sofa:service ref="uniformDataPoint"
interface="com.alipay.datapoint.UniformDataPointService"uniqueId="12"/>
在以上示例四的xml文件中,第一声明值包括发布接口serviceInterface的声明值:"com.alipay.datapoint.UniformDataPointService",以及发布标识serviceUniqueid的声明值:"12",通过扫描该xml文件发现,与第一声明值"com.alipay.datapoint.UniformDataPointService"及"12"相对应的serviceClass的第二声明值包括两个,分别是"com.alipay.UniServiceImpl"及"com.alipay.UniPointImpl",通过比对发现以上两个第二声明值不相同,即不唯一,以上情况会导致外围引用模块的服务时,无法确定所引用的是哪一个服务,则判定该情况是发布服务错误。
图4为本申请实施例提供的代码检测方法的具体过程。本实施例中,预先配置三个测试容器,其中,第一测试容器ArrayList<BeanConfig>beanChecker用以实现Bean类声明错误的检测,第二测试容器ArrayList<BeanConfig>referenceChecker用以实现引用服务错误的检测,第三测试容器ArrayList<BeanConfig>serviceChecker用以实现发布服务错误的检测。具体地,三个测试容器的实现过程是:
首先,定义一个类BeanConfig如下:
public class BeanConfig{
private String beanId;
private String beanClassName;
private String beanParent;
private String serviceRef;
private String serviceInterfaces;
private String serviceUniqueid;
private String serviceClassName;
private String refId;
private String refInterfaces;
private String refUniqueid;
//fileName
private String filePath;
}
其中,beanId对应于组件bean定义的id,beanClassName对应于组件bean定义的类Class,beanParent对应于组件bean定义的父类Parent,serviceRef对应于sofa:service定义的ref(id),serviceInterfaces对应于sofa:service定义的接口interface,serviceUniqueid对应于sofa:service定义的uniqueId,serviceClassName对应于服务类名,refId对应于sofa:reference定义的id,refInterfaces对应于sofa:reference定义的接口interface,refUniqueid对应于sofa:reference定义的uniqueId。
则xml文件的检测过程是:扫描xml文件中所有的bean类的定义(包括组件bean定义的id、组件bean定义的类Class及组件bean定义的父类Parent),放入第一测试容器ArrayList<BeanConfig>beanChecker中;扫描xml文件中所有的sofa:reference类的定义(包括sofa:reference定义的id、sofa:reference定义的接口interface及sofa:reference定义的uniqueId),放入第二测试容器ArrayList<BeanConfig>referenceChecker中;扫描xml文件中所有的sofa:service类的定义(包括sofa:service定义的id、sofa:service定义的接口interface及sofa:service定义的uniqueId),放入第三测试容器ArrayList<BeanConfig>serviceChecker中。
参图4所示,逐一读取配置文件中的与第一声明值相对应的第二声明值的步骤,具体包括:
S31:从配置文件中读取一个与第一声明值相对应的第二声明值。
本申请实施例中,将配置文件(xml文件)从头到尾进行扫描,依次读取到一个与第一声明值相对应的第二声明值。以示例一的xml文件为例,若按照从头到尾的顺序进行逐个扫描,则第一次所读取到的第二声明值是:"com.alipay.securitydata.biz.dataset.datapoint.impl.BaseData1",第二次所读取到的第二声明值是:"com.alipay.securitydata.biz.dataset.datapoint.impl.BaseData1"。
S32:判断测试容器中是否存在第一声明值。
S33:若否,则将从配置文件中读取到的第一声明值及第二声明值插入到测试容器中。
在示例一的xml文件中,若此时计算机所读取到的第二声明值是"com.alipay.securitydata.biz.dataset.datapoint.impl.BaseData1",则表明是第一次读取,也就是并未向所述第一测试容器中插入任何声明值,则此时将第一声明值"baseDataPointExecutor"及与"baseDataPointExecutor"相对应的第二声明值"com.alipay.securitydata.biz.dataset.datapoint.impl.BaseData1"存放到第一测试容器ArrayList<BeanConfig>beanChecker中。
S34:若是,则从测试容器中读取与第一声明值相对应的第二声明值。
在示例一的xml文件中,按照次序进行扫描,第二次所读取到的第二声明值是"com.alipay.securitydata.biz.dataset.datapoint.impl.BaseData2",因为此时第一测试容器中已经存在第一声明值"baseDataPointExecutor"及第二声明值"com.alipay.securitydata.biz.dataset.datapoint.impl.BaseData1",则直接从第一测试容器ArrayList<BeanConfig>beanChecker中读取出第二声明值"com.alipay.securitydata.biz.dataset.datapoint.impl.BaseData1"。
则,判断第二声明值是否唯一的步骤,具体包括:
S41:若测试容器中存在第一声明值,判断从测试容器中读取到的第二声明值是否等于从配置文件中读取到的第二声明值,若否,则判定源代码文件存在错误。
在示例一的xml文件中,从第一测试容器读取的第二声明值是"com.alipay.securitydata.biz.dataset.datapoint.impl.BaseData1",从xml文件读取的第二声明值是"com.alipay.securitydata.biz.dataset.datapoint.impl.BaseData2",通过比对发现两个第二声明值不相等,则表明该xml文件中存在Bean类声明错误。关于第二测试容器及第三测试容器的测试过程与第一测试容器类似,本文不再予以一一赘述。
值得一提的是,通过第三测试容器实现发布服务错误的检测的过程中,首先利用第一测试容器更新第三测试容器,以找到第一测试容器与第三测试容器中beanId与serviceRef相同的项,并将第一测试容器中beanClassName的值赋值给第三测试容器中的serviceClassName,随后再利用更新后的第三测试容器实现发布服务错误的检测。当然,在本申请其他实施例中,若不采取配置测试容器的方式进行测试,则可以依次获取xml文件中所包含的第一声明值,并逐一扫描该xml文件中所包含与第二声明值所对应的所有第二声明值,在读取到所有的第二声明值后,直接比对所有的第二声明值是否一致,若不一致则表明存在代码错误,后续按照以上方式,将所需检测的源代码文件中的各个模块的xml文件逐一进行扫描,以发现源代码文件所存在的所有错误。
继续参图4所示,本申请实施例中,所述方法还包括:
S5:将存在错误的第二声明值及与存在错误的第二声明值相对应的第一声明值进行展示,以便于代码开发人员快递找到源代码文件中所存在的各类错误,并及时进行修正,从而提升代码开发效率。其中,展示错误声明值的方式包括:将存在错误的第二声明值及其相应的第二声明值打印出来,或者将所在错误的第二声明值及其相应的第二声明值进行特定颜色的标注。
图5为本申请实施例提供的代码检测装置的结构示意图。基于以上方法的原理,该代码检测装置包括:
获取模块10,用于获取待检测的源代码文件中的模块Bundle的配置文件;
第一读取模块20,用于读取配置文件中的第一声明值,其中,第一声明值是第一属性的声明;
第二读取模块30,用于逐一读取配置文件中的与第一声明值相对应的第二声明值,其中,第二声明值是第二属性的声明;
判断模块40,用于判断第二声明值是否唯一,若否,则判定源代码文件存在错误。
本申请实施例中,所述装置还包括容器读写模块,用于:
从所述配置文件中读取一个与所述第一声明值相对应的第二声明值;
判断测试容器中是否存在所述第一声明值;
若是,则从所述测试容器中读取与所述第一声明值相对应的第二声明值;
若否,则将从所述配置文件中读取到的第一声明值及第二声明值插入到所述测试容器中;
则,所述判断模块具体用于:
若所述测试容器中存在所述第一声明值,且从所述测试容器中读取到的第二声明值不等于所述配置文件中的第二声明值,则判定所述源代码文件存在错误。
本申请实施例中,所述装置还包括:
展示模块,用于将存在错误的第二声明值及与存在错误的第二声明值相对应的第一声明值进行展示。
本申请实施例,所述获取模块具体包括:
源代码文件获取单元,用于确定所述源代码文件所在的目录;
判断单元,用于判断所述目录的子目录中是否存在特定文件;
配置文件获取单元,用于在所述目录的子目录中存在特定文件时,获取所述子目录中包含的配置文件。
本申请实施例中,所述装置还包括配置文件处理模块,用于:
读取所述配置文件,并将所述配置文件的格式转换成字符串格式;
删除所述字符串格式的文件中的干扰内容;
将删除所述干扰内容后的字符串格式转换成所述配置文件的格式。
本申请实施例中,所述第一属性是组件Bean的标识,所述第二属性是组件Bean所定义的类Class、或者组件Bean所定义的父类Parent。
本申请实施例中,所述第一属性是模块Bundle引用的服务,所述第二属性是模块Bundle引用的服务的引用接口refInterface、或者模块Bundle引用的服务所定义的引用标识refUniqueid。
本申请实施例中,所述第二属性是模块Bundle发布的服务所定义的服务类serviceClass,所述第一属性包括模块Bundle发布的服务的发布接口serviceInterface及模块Bundle发布的服务所定义的发布标识serviceUniqueid。
本申请实施例所提供的代码检测方法通过获取待检测的源代码文件中的模块Bundle的配置文件,并基于所述配置文件,分别读取第一声明值及第二声明值,并判断在第一声明值所对应的第二声明值是否是唯一的,在第二声明不唯一时判定代码出现错误,从而实现所述源代码文件中存在的错误的线下检测功能。本申请实施例能够在线下准确检测到源代码文件中存在的所有错误,从而确保代码上线后的代码质量,提升代码开发效率。
本领域内的技术人员应明白,本发明的实施例可提供为方法、系统、或计算机程序产品。因此,本发明可采用完全硬件实施例、完全软件实施例、或结合软件和硬件方面的实施例的形式。而且,本发明可采用在一个或多个其中包含有计算机可用程序代码的计算机可用存储介质(包括但不限于磁盘存储器、CD-ROM、光学存储器等)上实施的计算机程序产品的形式。
本发明是参照根据本发明实施例的方法、设备(系统)、和计算机程序产品的流程图和/或方框图来描述的。应理解可由计算机程序指令实现流程图和/或方框图中的每一流程和/或方框、以及流程图和/或方框图中的流程和/或方框的结合。可提供这些计算机程序指令到通用计算机、专用计算机、嵌入式处理机或其他可编程数据处理设备的处理器以产生一个机器,使得通过计算机或其他可编程数据处理设备的处理器执行的指令产生用以实现在流程图一个流程或多个流程和/或方框图一个方框或多个方框中指定的功能的装置。
这些计算机程序指令也可存储在能引导计算机或其他可编程数据处理设备以特定方式工作的计算机可读存储器中,使得存储在该计算机可读存储器中的指令产生包括指令装置的制造品,该指令装置实现在流程图一个流程或多个流程和/或方框图一个方框或多个方框中指定的功能。
这些计算机程序指令也可装载到计算机或其他可编程数据处理设备上,使得在计算机或其他可编程设备上执行一系列操作步骤以产生计算机实现的处理,从而在计算机或其他可编程设备上执行的指令提供用以实现在流程图一个流程或多个流程和/或方框图一个方框或多个方框中指定的功能的步骤。
在一个典型的配置中,计算设备包括一个或多个处理器(CPU)、输入/输出接口、网络接口和内存。
内存可能包括计算机可读介质中的非永久性存储器,随机存取存储器(RAM)和/或非易失性内存等形式,如只读存储器(ROM)或闪存(flash RAM)。内存是计算机可读介质的示例。
计算机可读介质包括永久性和非永久性、可移动和非可移动媒体可以由任何方法或技术来实现信息存储。信息可以是计算机可读指令、数据结构、程序的模块或其他数据。计算机的存储介质的例子包括,但不限于相变内存(PRAM)、静态随机存取存储器(SRAM)、动态随机存取存储器(DRAM)、其他类型的随机存取存储器(RAM)、只读存储器(ROM)、电可擦除可编程只读存储器(EEPROM)、快闪记忆体或其他内存技术、只读光盘只读存储器(CD-ROM)、数字多功能光盘(DVD)或其他光学存储、磁盒式磁带,磁带磁磁盘存储或其他磁性存储设备或任何其他非传输介质,可用以存储可以被计算设备访问的信息。按照本文中的界定,计算机可读介质不包括暂存电脑可读媒体(transitory media),如调制的数据信号和载波。
还需要说明的是,术语“包括”、“包含”或者其任何其他变体意在涵盖非排他性的包含,从而使得包括一系列要素的过程、方法、商品或者设备不仅包括那些要素,而且还包括没有明确列出的其他要素,或者是还包括为这种过程、方法、商品或者设备所固有的要素。在没有更多限制的情况下,由语句“包括一个……”限定的要素,并不排除在包括所述要素的过程、方法、商品或者设备中还存在另外的相同要素。
本领域技术人员应明白,本申请的实施例可提供为方法、系统或计算机程序产品。因此,本申请可采用完全硬件实施例、完全软件实施例或结合软件和硬件方面的实施例的形式。而且,本申请可采用在一个或多个其中包含有计算机可用程序代码的计算机可用存储介质(包括但不限于磁盘存储器、CD-ROM、光学存储器等)上实施的计算机程序产品的形式。
以上所述仅为本申请的实施例而已,并不用以限制本申请。对于本领域技术人员来说,本申请可以有各种更改和变化。凡在本申请的精神和原理之内所作的任何修改、等同替换、改进等,均应包含在本申请的权利要求范围之内。