掃二維碼與項目經(jīng)理溝通
我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流
本文轉(zhuǎn)載自微信公眾號「bugstack蟲洞?!?,作者小傅哥。轉(zhuǎn)載本文請聯(lián)系bugstack蟲洞棧公眾號。

你會對你用到都技術(shù),好奇嗎?
雖然我們都被稱為碼農(nóng),也都是寫著代碼,但因為所處場景需求的不同,所以各類碼農(nóng)也都做著不一樣都事情。
有些人統(tǒng)一規(guī)范、有些人開發(fā)組件、有些人編寫業(yè)務(wù)、有些人倒騰驗證,但越是工作內(nèi)容簡單如CRUD一樣的碼農(nóng),用到別人提供好的東西卻是越多。一會安裝個插件、一會引入個Jar包、一會調(diào)別人個接口,而自己的工作就像是裝配工,東拼拼西湊湊,就把產(chǎn)品需求寫完了。
壞了,這么干可能幾年下來,也不會有什么技術(shù)上都突破。因為你對那些使用都技術(shù)不好奇,不想知道它們是怎么實現(xiàn)的。就像阿里的P3C插件,是怎么檢查代碼分析出來我寫的拉胯的呢?
P3C 是阿里開源代碼庫的插件工程名稱,它以阿里巴巴Java開發(fā)手冊為標準,用于監(jiān)測代碼質(zhì)量的 IDEA/Eclipse 插件。
源碼:https://github.com/alibaba/p3c
插件安裝完成后,就可以按照編程規(guī)約,靜態(tài)分析代碼中出現(xiàn)的代碼:命名風格、常量定義、集合處理、并發(fā)處理、OOP、控制語句、注釋、異常等各項潛在風險,同時會給出一些優(yōu)化操作和實例。
在最開始使用這類代碼檢查都插件的時候,就非常好奇它是怎么發(fā)現(xiàn)我的屎山代碼的,用了什么樣都技術(shù)原理呢,如果我能分析下是不是也可以把這樣都技術(shù)手段用到其他地方。
在分析這樣一個代碼檢查插件前,先思考要從 IDEA 插件都源碼查起,看看它是什么個邏輯,之后分析具體是如何使用都。其實這與一些其他的框架性源碼學習都是類似的,拿到官網(wǎng)都文檔、GitHub 對應(yīng)的源碼,按照步驟進行構(gòu)建、部署、測試、調(diào)試、分析,進而找到核心原理。
P3C 以 IDEA 插件開發(fā)為例,主要涉及到插件部分和規(guī)約部分,因為是把規(guī)約檢查的能力與插件技術(shù)結(jié)合,所以會涉及到一些 IDEA 開發(fā)的技術(shù)。另外 P3C 插件涉及到都技術(shù)語言不只是 Java 還有一部分 kotlin 它是一種在 Java 虛擬機上運行的靜態(tài)類型編程語言。
1. 插件配置 p3c.xml
- popup="true" text="編碼規(guī)約掃描" icon="P3cIcons.ANALYSIS_ACTION">
- first-keystroke="shift ctrl alt J"/>
2. 編碼規(guī)約掃描( AliInspectionAction)
- class AliInspectionAction : AnAction() {
- override fun actionPerformed(e: AnActionEvent) {
- val project = e.project ?: return
- val analysisUIOptions = ServiceManager.getService(project, AnalysisUIOptions::class.java)!!
- analysisUIOptions.GROUP_BY_SEVERITY = true
- val managerEx = InspectionManager.getInstance(project) as InspectionManagerEx
- val toolWrappers = Inspections.aliInspections(project) {
- it.tool is AliBaseInspection
- }
- val psiElement = e.getData
(CommonDataKeys.PSI_ELEMENT) - val psiFile = e.getData
(CommonDataKeys.PSI_FILE) - val virtualFile = e.getData
(CommonDataKeys.VIRTUAL_FILE) - ...
- createContext(
- toolWrappers, managerEx, element,
- projectDir, analysisScope
- ).doInspections(analysisScope)
- }
這是一個基于 kotlin 語言開發(fā)的插件代碼邏輯,它通過 actionPerformed 方法獲取到工程信息、類信息等,接下來就可以執(zhí)行代碼檢查了 doInspections
3. 規(guī)約 p3c-pmd
當我們再往下翻看閱讀的時候,就看到了一個關(guān)于 pmd 的東西。PMD 是一款采用 BSD 協(xié)議發(fā)布的Java 程序靜態(tài)代碼檢查工具,當使用PMD規(guī)則分析Java源碼時,PMD首先利用JavaCC和EBNF文法產(chǎn)生了一個語法分析器,用來分析普通文本形式的Java代碼,產(chǎn)生符合特定語法結(jié)構(gòu)的語法,同時又在JavaCC的基礎(chǔ)上添加了語義的概念即JJTree,通過JJTree的一次轉(zhuǎn)換,這樣就將Java代碼轉(zhuǎn)換成了一個AST,AST是Java符號流之上的語義層,PMD把AST處理成一個符號表。然后編寫PMD規(guī)則,一個PMD規(guī)則可以看成是一個Visitor,通過遍歷AST找出多個對象之間的一種特定模式,即代碼所存在的問題。該軟件功能強大,掃描效率高,是 Java 程序員 debug 的好幫手。
那么 p3c-pmd 是什么呢?
- ViolationUtils.addViolationWithPrecisePosition(this, node, data,
- I18nResources.getMessage("java.naming.ClassNamingShouldBeCamelRule.violation.msg",
- node.getImage()));
p3c-pmd 插件是基于 PMD 實現(xiàn)的,更具體的來說是基于 pmd-java 的,因為 PMD 不僅支持 Java 代碼分析,還支持其他多種語言。
具體自定義規(guī)則的方式,通過自定義Java類和XPATH規(guī)則實現(xiàn)。
講道理,說一千道一萬,還得是拿出代碼跑一下,才知道 PMD 具體是什么個樣子。
1. 測試工程
- guide-pmd
- └── src
- ├── main
- │ ├── java
- │ │ └── cn.itedus.guide.pmd.rule
- │ │ ├── naming
- │ │ │ ├── ClassNamingShouldBeCamelRule.java
- │ │ │ ├── ConstantFieldShouldBeUpperCaseRule.java
- │ │ │ └── LowerCamelCaseVariableNamingRule.java
- │ │ ├── utils
- │ │ │ ├── StringAndCharConstants.java
- │ │ │ └── ViolationUtils.java
- │ │ └── I18nResources
- │ └── resources
- │ ├── rule
- │ │ └── ali-naming.xml
- │ ├── messages.xml
- │ └── namelist.properties
- └── test
- └── java
- └── cn.itedus.demo.test
- ├── ApiTest.java
- └── TErrDto.java
這是一個類似 p3c-pmd 的測試工程,通過自行擴展重寫代碼監(jiān)測規(guī)約的方式,來處理自己關(guān)于代碼的審核標準處理。
2. 駝峰命名規(guī)約
- public class ClassNamingShouldBeCamelRule extends AbstractJavaRule {
- private static final Pattern PATTERN
- = Pattern.compile("^I?([A-Z][a-z0-9]+)+(([A-Z])|(DO|DTO|VO|DAO|BO|DAOImpl|YunOS|AO|PO))?$");
- @Override
- public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
- if (PATTERN.matcher(node.getImage()).matches()) {
- return super.visit(node, data);
- }
- ViolationUtils.addViolationWithPrecisePosition(this, node, data,
- I18nResources.getMessage("java.naming.ClassNamingShouldBeCamelRule.violation.msg",
- node.getImage()));
- return super.visit(node, data);
- }
- }
3. ali-naming.xml 配置
- language="java"
- since="1.6"
- message="java.naming.ClassNamingShouldBeCamelRule.rule.msg"
- class="cn.itedus.guide.pmd.rule.naming.ClassNamingShouldBeCamelRule">
3
4. 測試驗證規(guī)約
問題類示例
- public class TErrDto {
- public static final Long max = 50000L;
- public void QueryUserInfo(){
- boolean baz = true;
- while (baz)
- baz = false;
- }
- }
單元測試
- @Test
- public void test_naming(){
- String[] str = {
- "-d",
- "E:\\itstack\\git\\github.com\\guide-pmd\\src\\test\\java\\cn\\itedus\\demo\\test\\TErrDto.java",
- "-f",
- "text",
- "-R",
- "E:\\itstack\\git\\github.com\\guide-pmd\\src\\main\\resources\\rule\\ali-naming.xml"
- // "category/java/codestyle.xml"
- };
- PMD.main(str);
- }
測試結(jié)果
- TErrDto.java:3: 【TErrDto】不符合UpperCamelCase命名風格
- TErrDto.java:5: 常量【max】命名應(yīng)全部大寫并以下劃線分隔
- TErrDto.java:7: 方法名【QueryUserInfo】不符合lowerCamelCase命名風格
- Process finished with exit code 4
從測試結(jié)果可以看到,我們寫的三個代碼規(guī)約分別監(jiān)測出了代碼的命名風格、常量大寫、方法名不符合駝峰標識。
同時你還可以測試 category/java/codestyle.xml 這個是 PMD 自身提供好的規(guī)約監(jiān)測。
其實有了 PMD 靜態(tài)代碼檢查規(guī)約,能做都事情就很多,不是只對正在寫的代碼進行檢查,還可以對不同階段的代碼進行分析和風險提醒,比如:準備提測階段、已經(jīng)上線完成,都可以做相應(yīng)的監(jiān)測處理。
而 Sonar 就是一個這樣都工具,它是一個Web系統(tǒng),可以展現(xiàn)靜態(tài)代碼掃描的結(jié)果,結(jié)果是可以自定義的,支持多種語言的原理是它的擴展性。https://www.sonarqube.org/
PMD 是一款采用 BSD 協(xié)議的代碼檢查工具,你可以擴展實現(xiàn)為自己的標準和規(guī)范以及完善個性的提醒和修復操作。
另外基于 IDEA 插件實現(xiàn)的代碼檢查或者有審計要求的處理,也可以基于 IDEA 插件做更多的擴展,比如提醒修復、提供修復操作、自身業(yè)務(wù)邏輯的檢查。例如momo開源庫下的一款I(lǐng)DEA靜態(tài)代碼安全審計及漏洞一鍵修復插件 https://github.com/momosecurity/momo-code-sec-inspector-java
這里補充一點,kotlin 語言可以在 IDEA 中轉(zhuǎn)換為 Java 語言,這樣你在閱讀類似這樣的代碼時候,如果不好看懂也可以轉(zhuǎn)換一下在閱讀。此外 IDEA 插件開發(fā)需要基于 Gradle 或者本身提供都模版進行創(chuàng)建,如果感興趣也可以閱讀我寫的 IDEA 插件開發(fā)文章。

我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流