自定義 Gradle 插件進行統一的靜態代碼分析

靜態代碼分析是一項了不起的技術, 它能讓代碼庫更易于維護. 但是, 如果你在不同的版本庫中擁有多個服務(可能由不同的團隊開發), 如何才能讓每個人都遵循既定的代碼風格呢? 一個好辦法是將所有規則封裝在一個插件中, 該插件會在每個項目構建時自動執行所需的驗證.

因此, 在本文中我將向你展示:

  1. 如何創建帶有自定義 PMD 和 Checkstyle 規則的 Gradle 插件.
  2. 如何發布到 [plugins.gradle.org]
  3. 如何使用 GitHub Actions 自動執行發布流程.

你可以查看[本倉庫]中的代碼示例.

PMD, Checkstyle 和多倉庫的難點

[PMD] 是靜態分析工具, 可在每次項目構建時檢查代碼. 通過 [Gradle]“https://medium.com/javarevisited/why-java-developer-should-learn-maven-or-gradle-aefe7ea20a83”), 可以輕松應用它們.

plugins {id 'java'id 'pmd'id 'checkstyle'
}

現在, 你可以按照自己的方式調整每個插件.

checkstyle {toolVersion = '10.5.0'ignoreFailures = falsemaxWarnings = 0configFile = file(pathToCheckstyleConfig)
}pmd {consoleOutput = truetoolVersion = '6.52.0'ignoreFailures = falseruleSetFiles = file(pathToPmdConfig)
}

如果你的整個項目(甚至是公司)都是[單倉庫, 那么這樣的設置絕對沒問題. 你只需將這些配置放入根build.gradle文件中, 就能將這些插件應用到現有的每個模塊中. 但如果你選擇的是[多倉庫]呢?

如果你想在公司內開發人員正在開發的所有項目(以及程序員將來創建的所有項目)中共享相同的代碼風格, 該怎么辦?

那么, 你可以告訴他們只需[復制并粘貼]插件的配置即可. 無論如何, 這種方法容易出錯. 總有人可能會配置錯誤.

事實上, 我們需要在每個可行的項目中以某種方式重復使用已定義的代碼樣式配置. 答案很簡單. 我們需要一個定制的 Gradle 插件來封裝 PMD 和 Checkstyle 規則.

自定義 Gradle 插件

構建配置

請看下面的 build.gradle 聲明. 這是 Gradle 插件項目的基本設置.

plugins {id 'java-gradle-plugin'id 'com.gradle.plugin-publish' version '1.1.0'
}group = 'io.github.simonharmonicminor.code.style'
sourceCompatibility = '8'repositories {mavenCentral()
}ext {set('lombokVersion', '1.18.24')
}dependencies {compileOnly "org.projectlombok:lombok:${lombokVersion}"annotationProcessor "org.projectlombok:lombok:${lombokVersion}"testImplementation 'org.junit.jupiter:junit-jupiter:5.7.2'
}gradlePlugin {website = 'https://github.com/SimonHarmonicMinor/gradle-code-style-plugin-example'vcsUrl = 'https://github.com/SimonHarmonicMinor/gradle-code-style-plugin-example'plugins {gradleCodeStylePluginExample {id = 'io.github.simonharmonicminor.code.style'displayName = 'Gradle Plugin Code Style Example'description = 'Predefined Checkstyle and PMD rules'implementationClass = 'io.github.simonharmonicminor.code.style.CodingRulesGradlePluginPlugin'tags.set(['codestyle', 'checkstyle', 'pmd'])}}
}tasks.named('test') {useJUnitPlatform()
}

現在讓我們從 plugins 塊開始, 一步步解構配置. 請看下面的代碼片段.

plugins {id 'java-gradle-plugin'id 'com.gradle.plugin-publish' version '1.1.0'
}

java-gradle-plugin命令會啟用常規 Gradle 插件項目的任務. com.gradle.plugin-publish命令允許打包插件并發布到plugins.gradle.org.

我最近正在向你展示整個發布過程.

然后是基本的項目配置.

group = 'io.github.simonharmonicminor.code.style'
sourceCompatibility = '8'repositories {mavenCentral()
}

group定義了groupId, 以符合[Apache Maven 命名規范] sourceCompatibility是目標 Java 二進制文件的版本. 雖然 Java 8 現在已經過時, 但我還是建議你使用公司開發人員使用的最早 JDK 版本構建 Gradle 插件. 否則, 你會阻礙他們遵循你的代碼風格指南.

然后是 dependencies 范圍.

ext {set('lombokVersion', '1.18.24')
}dependencies {compileOnly "org.projectlombok:lombok:${lombokVersion}"annotationProcessor "org.projectlombok:lombok:${lombokVersion}"testImplementation 'org.junit.jupiter:junit-jupiter:5.7.2'
}

這里沒什么特別的. 接下來是發布配置.

website = 'https://github.com/SimonHarmonicMinor/gradle-code-style-plugin-example'vcsUrl = 'https://github.com/SimonHarmonicMinor/gradle-code-style-plugin-example'plugins {gradleCodeStylePluginExample {id = 'io.github.simonharmonicminor.code.style'displayName = 'Gradle Plugin Code Style Example'description = 'Predefined Checkstyle and PMD rules'implementationClass = 'io.github.simonharmonicminor.code.style.CodingRulesGradlePluginPlugin'tags.set(['codestyle', 'checkstyle', 'pmd'])}}
}

websitevcsUrl應指向包含插件源代碼的公共 Git 倉庫. plugins塊定義了項目中Plugin接口的每個實現. 最后,tags只是在注冊表中搜索插件的hash標簽.

當你將 Gradle 插件發布到 [plugins.gradle.org] 時, 包的名稱至關重要. 你的插件代碼應該可以在 GitHub 上找到. 如果不是開源的, 發布時可能會遇到問題. 那么, 你可以將軟件包名稱聲明為io.github.your_github_login.any.package.you.like.

但是, 如果你想使用其他名稱, 如com.mycompany.my.plugin, 請確保域名mycompany.com. 否則, Gradle 工程師可能會拒絕發布.

注意 Gradle 禁止plugingradle作為標簽值. 在gradle publishPlugins任務執行過程中, 這樣的構建會失敗.

tasks.named('test') {useJUnitPlatform()
}

插件代碼

我想向大家展示整個插件的代碼. 然后我將向你解釋每個細節. 請看下面的代碼片段.

public class CodingRulesGradlePluginPlugin implements Plugin<Project> {@Overridepublic void apply(Project project) {project.getPluginManager().apply("checkstyle");project.getExtensions().configure(CheckstyleExtension.class, checkstyleExtension -> {checkstyleExtension.setToolVersion("10.5.0");checkstyleExtension.setIgnoreFailures(false);checkstyleExtension.setMaxWarnings(0);checkstyleExtension.setConfigFile(FileUtil.copyContentToTempFile("style/checkstyle.xml", ".checkstyle.xml"));});project.getPluginManager().apply("pmd");project.getExtensions().configure(PmdExtension.class, pmdExtension -> {pmdExtension.setConsoleOutput(true);pmdExtension.setToolVersion("6.52.0");pmdExtension.setIgnoreFailures(false);pmdExtension.setRuleSets(emptyList());pmdExtension.setRuleSetFiles(project.files(FileUtil.copyContentToTempFile("style/pmd.xml", ".pmd.xml")));});final SortedSet<String> checkstyleTaskNames = project.getTasks().withType(Checkstyle.class).getNames();final SortedSet<String> pmdTaskNames = project.getTasks().withType(Pmd.class).getNames();project.task("runStaticAnalysis",task -> task.setDependsOn(Stream.concat(checkstyleTaskNames.stream(),pmdTaskNames.stream()).collect(Collectors.toList())));}
}

最明顯也是最重要的細節是, 每個插件任務都必須實現 Gradle Plugin 接口.

import org.gradle.api.Plugin;
import org.gradle.api.Project;public class CodingRulesGradlePluginPlugin implements Plugin<Project> {@Overridepublic void apply(Project project) { ... }
}

然后我在配置 Checkstyle 任務. 我只需應用 checkstyle 插件, 獲取 CheckstyleConfiguration 并覆蓋我想要的屬性. 請看下面的代碼塊.

project.getPluginManager().apply("checkstyle");
project.getExtensions().configure(CheckstyleExtension.class, checkstyleExtension -> {checkstyleExtension.setToolVersion("10.5.0");checkstyleExtension.setIgnoreFailures(false);checkstyleExtension.setMaxWarnings(0);checkstyleExtension.setConfigFile(FileUtil.copyContentToTempFile("style/checkstyle.xml", ".checkstyle.xml"));
});

FileUtil.copyContentToTempFile函數需要解釋一下. 我把 Checkstyle 配置放到了 src/main/resources/style/checkstyle.xml 文件中. 但是, 如果你直接指向它, 那么人們在他們的項目中應用你的 Gradle 時就會得到奇怪的錯誤信息. 有一些變通方法, 但最簡單的方法是將內容復制到臨時文件中.

看看下面的 PMD 配置. 與 Checkstyle 類似.

project.getPluginManager().apply("pmd");
project.getExtensions().configure(PmdExtension.class, pmdExtension -> {pmdExtension.setConsoleOutput(true);pmdExtension.setToolVersion("6.52.0");pmdExtension.setIgnoreFailures(false);pmdExtension.setRuleSets(emptyList());pmdExtension.setRuleSetFiles(project.files(FileUtil.copyContentToTempFile("style/pmd.xml", ".pmd.xml")));
});

現在我們準備就緒. 我們可以將其應用到實際項目中. 雖然也有一點改進. 請看下面的代碼片段.

final SortedSet<String> checkstyleTaskNames = project.getTasks().withType(Checkstyle.class).getNames();final SortedSet<String> pmdTaskNames = project.getTasks().withType(Pmd.class).getNames();project.task("runStaticAnalysis",task -> task.setDependsOn(Stream.concat(checkstyleTaskNames.stream(),pmdTaskNames.stream()).collect(Collectors.toList()))
);

runStaticAnalysis任務會觸發所有 Checkstyle 和 PMD 任務按順序運行. 當你想在創建拉取請求前驗證整個項目時, 它就派上用場了. 如果直接在build.gradle中添加runStaticAnalysis任務, 它將看起來像這樣:

task runStaticAnalysis {dependsOn checkstyleMain, checkstyleTest, pmdMain, pmdTest
}

同樣, 我將一次性展示整段代碼, 然后指出重要的細節.

class CodingRulesGradlePluginPluginTest {@Testvoid shouldApplyPluginSuccessfully() {final Project project = ProjectBuilder.builder().build();project.getPluginManager().apply("java");assertDoesNotThrow(() -> new CodingRulesGradlePluginPlugin().apply(project));final Task task = project.getTasks().getByName("runStaticAnalysis");assertNotNull(task, "runStaticAnalysis task should be registered");final Set<String> codeStyleTasks =Stream.of("checkstyleMain", "checkstyleTest", "pmdTest", "pmdMain").collect(toSet());assertTrue(task.getDependsOn().containsAll(codeStyleTasks),format("Task runStaticAnalysis should contain '%s' tasks, but actually: %s",codeStyleTasks,task.getDependsOn()));}
}

首先是 Gradle 項目實例化測試. 請看下面的代碼片段.

import org.gradle.testfixtures.ProjectBuilder;
import org.gradle.api.Project;final Project project = ProjectBuilder.builder().build();
project.getPluginManager().apply("java");

Gradle 為單元測試提供了一些固定裝置. ProjectBuilder創建了一個與 API 兼容的Project接口實現. 因此, 你可以放心地將它傳遞給 YourPluginClass.apply 方法.

在調用業務邏輯之前, 我們還要手動應用 java 插件. 我們的插件針對 Java 應用程序. 因此, 傳遞 Java 配置的 Project 實現是很自然的.

然后, 我們只需調用自定義插件方法并傳遞配置的 Project 實現.

assertDoesNotThrow(() -> new CodingRulesGradlePluginPlugin().apply(project)
);

之后是斷言. 我們需要確保 runStaticAnalysis 任務注冊成功.

final Task task = project.getTasks().getByName("runStaticAnalysis");
assertNotNull(task, "runStaticAnalysis task should be registered");

如果存在, 我們將根據現有的 Checkstyle 和 PMD 任務驗證該任務.

final Set<String> codeStyleTasks =Stream.of("checkstyleMain", "checkstyleTest", "pmdTest", "pmdMain").collect(toSet());
assertTrue(task.getDependsOn().containsAll(codeStyleTasks),format("Task runStaticAnalysis should contain '%s' tasks, but actually: %s",codeStyleTasks,task.getDependsOn())
);

這是我們在將插件推送到 [plugins.gradle.org/]之前應該測試的最基本情況.

使用 GitHub Actions 發布插件

當你在 [plugins.gradle.org/]上注冊一個新賬戶時, 進入你的頁面并打開 API Keys 選項卡. 你應該生成新的密鑰. 會有兩個.

gradle.publish.key=...
gradle.publish.secret=...

然后, 打開版本庫的Settings, 轉到Secrets and Variables -> Actions項. 你必須把獲得的密鑰存儲為版本庫秘密.

最后是 GitHub Actions 的構建配置.

我把自己的文件放在了.github/workflow/build.yml.

請看下面的整個設置. 然后, 我將告訴你特定區塊的含義.

name: Java CI with Gradleon:push:branches: [ "master" ]pull_request:branches: [ "master" ]permissions:contents: readjobs:build:runs-on: ubuntu-lateststeps:- uses: actions/checkout@v3- name: Set up JDK 8uses: actions/setup-java@v3with:java-version: '8'distribution: 'temurin'- name: Build with Gradleuses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1with:arguments: buildpublish:needs:- buildif: github.ref == 'refs/heads/master'runs-on: ubuntu-lateststeps:- name: Auto Increment Semver Actionuses: MCKanpolat/auto-semver-action@1.0.5id: versioningwith:releaseType: minorincrementPerCommit: falsegithub_token: ${{ secrets.GITHUB_TOKEN }}- name: Next Release Numberrun: echo ${{ steps.versioning.outputs.version }}- uses: actions/checkout@v3- name: Set up JDK 8uses: actions/setup-java@v3with:java-version: '8'distribution: 'temurin'- name: Publish Gradle pluginuses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1with:arguments: build publishPlugins -Pgradle.publish.key=${{ secrets.GRADLE_PUBLISH_KEY }} -Pgradle.publish.secret=${{ secrets.GRADLE_PUBLISH_SECRET }} -Pversion=${{ steps.versioning.outputs.version }}

文件頂部的聲明說明了管道觸發的規則.

name: Java CI with Gradleon:push:branches: [ "master" ]pull_request:branches: [ "master" ]

管道會在每次向master分支提出拉取請求和每次構建master分支時運行.

構建由兩項工作組成. 第一個工作很簡單. 它只是運行 Gradle build 任務. 請看下面的配置.

jobs:build:runs-on: ubuntu-lateststeps:- uses: actions/checkout@v3- name: Set up JDK 8uses: actions/setup-java@v3with:java-version: '8'distribution: 'temurin'- name: Build with Gradleuses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1with:arguments: build

然后是發布的任務本身. 它也包含幾個步驟. 第一個步驟是自動增加版本并保存到環境變量中. 這很方便, 因為 Gradle 插件不能以快照的形式發布.

publish:needs:- buildif: github.ref == 'refs/heads/master'runs-on: ubuntu-lateststeps:- name: Auto Increment Semver Actionuses: MCKanpolat/auto-semver-action@1.0.5id: versioningwith:releaseType: minorincrementPerCommit: falsegithub_token: ${{ secrets.GITHUB_TOKEN }}- name: Next Release Numberrun: echo ${{ steps.versioning.outputs.version }}

if: github.ref == 'refs/heads/master'告知 GitHub Actions 只有在master分支在構建的時候才能運行管道線中的任務. 因此, 在拉取請求構建過程中, GitHub Actions 不會觸發publish進程.

現在, 我們需要發布打包的插件本身. 請看下面的代碼片段.

- uses: actions/checkout@v3
- name: Set up JDK 8uses: actions/setup-java@v3with:java-version: '8'distribution: 'temurin'
- name: Publish Gradle pluginuses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1with:arguments: build publishPlugins -Pgradle.publish.key=${{ secrets.GRADLE_PUBLISH_KEY }} -Pgradle.publish.secret=${{ secrets.GRADLE_PUBLISH_SECRET }} -Pversion=${{ steps.versioning.outputs.version }}

如你所見, GitHub Actions 通過secrets傳遞了gradle.publish.keygradle.publish.secret屬性, 并將新項目版本作為環境變量.

總結一下

正如你所看到的, 在 Gradle 中自動檢查代碼樣式規則并不復雜. 順便說一句, 你可以通過包含 id 'io.github.simonharmonicminor.code.style' version '0.1.0' 來應用項目中描述的插件.

如果你看到了這里,覺得文章寫得不錯就給個贊唄?
更多Android進階指南 可以掃碼 解鎖更多Android進階資料


在這里插入圖片描述
敲代碼不易,關注一下吧。?( ′・?・` )

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/12890.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/12890.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/12890.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

最新版rancher環境配置安裝和集群搭建詳細教程記錄

&#x1f680; 作者 &#xff1a;“二當家-小D” &#x1f680; 博主簡介&#xff1a;?前荔枝FM架構師、阿里資深工程師||曾任職于阿里巴巴擔任多個項目負責人&#xff0c;8年開發架構經驗&#xff0c;精通java,擅長分布式高并發架構,自動化壓力測試&#xff0c;微服務容器化k…

2024-05-15 問AI: 介紹一下 , Tacotron系列

文心一言 Tacotron系列是由谷歌開發的&#xff0c;主要用于文本到語音&#xff08;TTS&#xff09;轉換的模型。該系列模型基于端到端的序列到序列&#xff08;Seq2Seq&#xff09;架構&#xff0c;能夠直接從文本中生成聽起來自然的語音。 Tacotron模型是首個端到端的TTS深度…

BGP學習三:BGP路由優選12條規則,閃亮登場啦啦啦啦啦

目錄 一.BGP策略工具 &#xff08;1&#xff09;Router-policy作用 &#xff08;2&#xff09;組成部分 &#xff08;3&#xff09;router-policy注意事項 二.優選規則 ①丟棄下一跳不可達 (1)優選prefered-value值大的路由 1.首選優先級 (2)優選local-preference(本地…

Fast-Poisson-Image-Editing代碼介紹(二)

目錄 2.fpei文件下 2.6 number_solver.py 2.7 process.py 2.8 taichi_solver.py 3. 算法總結 4. 代碼運行 4.1 測試 4.2 基于GUI后端自定義框輸出編輯圖像結果 4.2.1 下載open-cv 4.2.2 輸入命令 4.2.3 自定義框 4.2.4 按ESC退出 接續Fast-Poisson-Image-Editing代碼…

企業研發必備網絡:這些關鍵特性,你get了嗎?

對于以研發為核心的企業&#xff0c;如軟件開發、生物制藥、智能汽車等&#xff0c;安全、穩定的研發網絡可是他們業務發展不可或缺的。那么&#xff0c;這些研發網絡究竟有哪些獨特之處&#xff0c;又能為企業帶來哪些價值呢&#xff1f; 首先&#xff0c;我們知道企業研發常常…

開放式耳機哪款具有高性價比?5款高分開放式耳機傾力推薦

作為多年的耳機發燒友&#xff0c;強烈給你們安利開放式耳機&#xff0c;真的是舒適耐用&#xff0c;性價比高。開放式耳機以其獨特的不入耳設計&#xff0c;給用戶帶來了最舒適的佩戴感受。如果小白還不知道怎么選擇高性價比的開放式耳機那就看看我的總結吧&#xff01;下面就…

前端面試題(二十三)(答案版)

面試形式&#xff1a;線上電話面試&#xff1a;一面&#xff1a;時長30分鐘 面試評價&#xff1a;精準考察項目所需技術理論工作實踐 面試官的提問大綱&#xff1a;本公司項目要求本人簡歷 工作經驗&#xff1a;2-4年 公司名稱&#xff1a;深圳XX&#xff08;想知道的就滴喔…

馮喜運:5.15黃金原油晚盤分析:鮑威爾再放鷹,降息懸念重重

【黃金消息面分析】&#xff1a;在全球經濟動蕩和通脹預期不斷上升的背景下&#xff0c;黃金作為傳統的避險資產&#xff0c;再次成為投資者關注的焦點。當前&#xff0c;黃金價格交投于2370美元/盎司左右&#xff0c;連續兩日日線呈現上漲趨勢&#xff0c;而白銀價格也在連續三…

超級數據查看器 教程合集 整理版本 pdf格式 1-31集

點擊下載 超級數據查看器 教程合集整理版本 pdf格式https://download.csdn.net/download/qq63889657/89311725?spm1001.2014.3001.5501

16個可幫助我們工作的職場神器

在職場中&#xff0c;有效的工具可以顯著提高工作效率和組織能力。以下是一些可以幫助我們更好地組織工作的“職場神器”&#xff1a; 項目管理軟件 - zz-plan https://zz-plan.com/ 利用在線甘特圖和看板功能&#xff0c;幫助團隊成員清晰地規劃和跟蹤項目進度。支持資源視圖&…

微信小程序更新日志

還不會用github&#xff0c;git等&#xff0c;先用熟悉的記了 20240514 1.添加了簡易的錄音功能 2.添加了簡易的鬧鐘到時振動功能。 3.準備使用setInterval實現持續振動&#xff0c;直到用戶停止。 4.實現3的功能 5.獲取了訂閱消息模版

如何解決Java 中的精度問題

在 Java 編程中&#xff0c;處理浮點數和超大整數時常常會遇到精度丟失和數值溢出的困擾。為了確保計算結果的精確性&#xff0c;尤其是在金融計算等對精度要求極高的場景中&#xff0c;我們需要使用 BigDecimal 和 BigInteger 類。本文將詳細介紹浮點數精度丟失的原因、如何解…

更新Windows 11 后遇到的一些問題(更新中...)

目錄 插入U盤后讀取不到 在磁盤中新建文件夾需要管理員權限 導致不能安裝一些軟件 插入U盤后讀取不到 解決方法&#xff1a;點擊我的電腦或者是此電腦、選擇管理、找到設備管理器、選擇通用串行總線控制器、右鍵、選擇啟動。 第一步&#xff1a;點擊我的電腦或者是此電腦、選…

數據質量檢測標準

背景 為支持數據倉庫全局的數據質量管控&#xff0c;需做好風險點監控&#xff0c;確保數據的完整性、準確性、及時性、一致性。為此&#xff0c;擬定DQC配置方案&規則&#xff0c;評審通過后落地實施。 目標 核心任務dqc覆蓋率100%&#xff0c;質量問題及時知曉非核心任…

Java學習48-Java 流(Stream)、文件(File)和IO - 復習章節

1.File類的使用 File類的一個實例對應著磁盤上的文件或文件目錄。(必須熟悉)File的實例化(新建一個對象)&#xff0c;常用的方法File類中只有新建&#xff0c;刪除&#xff0c;獲取路徑等方法&#xff0c;不包含讀寫文件的方法&#xff0c;此時需要使用使用下面說的IO流 IO流…

論文閱讀:基于改進 YOLOv5算法的密集動態目標檢測方法

目錄 概要 Motivation 整體架構流程 技術細節 小結 論文地址&#xff1a;基于改進YOLOv5算法的密集動態目標檢測方法 - 中國知網 (cnki.net) 概要 目的&#xff1a;提出一種基于 YOLOv5改進的檢測算法&#xff0c;解決密集動態目標檢測精度低及易漏檢的問題。 方法&…

Linux虛擬主機cPanel重置密碼

我使用的Hostease的Linux虛擬主機產品默認帶普通用戶權限的cPanel面板&#xff0c;這邊自購買后一直未重新設置過cPanel面板的密碼&#xff0c;但是了解到要定期重置一下cPanel面板的密碼&#xff0c;以確保主機數據安全&#xff0c;因此想要進行重置cPanel面板的密碼&#xff…

二刷算法訓練營Day08 | 字符串(1/2)

今日任務&#xff1a; 344.反轉字符串 541. 反轉字符串II卡碼網&#xff1a;54.替換數字 151.翻轉字符串里的單詞卡碼網&#xff1a;55.右旋轉字符串 詳細布置&#xff1a; 1. 344. 反轉字符串 編寫一個函數&#xff0c;其作用是將輸入的字符串反轉過來。輸入字符串以字符數組 …

Django 從零到一:pip 基本使用

文章目錄 pip 常用命令pip 命令演示pip 自動補全 pip 是 Python 的包管理工具&#xff0c;就如同 Gnu/Linux 系統的 yum、apt&#xff0c;MacOS 系統的 brew。因此&#xff0c;我們必須要熟練使用該工具。 接下來我們就演示一下 pip 的常用命令。 pip 常用命令 pip 提供的命令…

SpringBoot上傳文件到服務器(跨服務器上傳)

目錄 &#xff08;一&#xff09;上傳文件到本地&#xff08;windows&#xff09; &#xff08;二&#xff09;上傳文件到linux服務器 &#xff08;三&#xff09;跨服務器上傳文件 &#xff08;一&#xff09;上傳文件到本地&#xff08;windows&#xff09; 1.新建一個文件…