Gradle to Maven Converter: แปลง dependency จาก build.gradle เป็น pom.xml snippet ได้ในไม่กี่วินาที
แปลง dependency และ plugin block จาก Gradle Groovy/Kotlin DSL เป็น Maven pom.xml snippet ในเบราว์เซอร์ล้วน ๆ แมป configuration เป็น scope เก็บ exclusion ครบ และตรวจจับ version catalog alias — 100% client-side
Table of Contents
Gradle to Maven Converter: แปลง dependency จาก build.gradle เป็น pom.xml snippet ได้ในไม่กี่วินาที
ซอฟต์แวร์ระดับองค์กรส่วนใหญ่ยังอาศัยอยู่บน Maven: monorepo ของบริษัท ระบบธนาคาร และ parent POM ภายในองค์กรนับไม่ถ้วน ล้วนมาตรฐานเดียวกันบน pom.xml โปรเจกต์ใหม่ ๆ ชอบ Gradle แต่การ migrate เดินสองทาง ทั้งการรับโปรเจกต์ที่ซื้อมาเข้าสู่ release train บน Maven และเครื่องมือ governance ที่สแกน dependency มักอ่านได้แค่ Maven เท่านั้น Gradle to Maven Converter เกิดมาเพื่อช่วงเวลาแบบนี้โดยเฉพาะ: paste dependency block และ plugin block ของ Gradle เข้าไป แล้วรับ pom.xml snippet ที่พร้อมใช้กลับออกมาภายในไม่กี่วินาที ทำงานในเบราว์เซอร์ล้วน ๆ
ส่วนที่น่าเบื่อที่สุดของการ migrate แบบนี้ไม่ใช่โครง build script แต่เป็น dependency block ทั้งหมด การแปลงมือเปล่าคือจุดที่ bug ซ่อนตัว: implementation ใน Groovy ไม่ได้กลายเป็น scope compile ด้วยดวงล้วน ๆ, accessor อย่าง libs.jackson ใน Kotlin DSL แปลว่าอะไรไม่ได้จนกว่าจะ resolve เป็นพิกัดจริง และทุก exclude block ที่หล่นหายคือ transitive leak ที่คุณจะไปเจออีกทีในการ audit รอบหน้า เครื่องมือนี้จัดการความเสี่ยงทั้งสามจุด: แมป configuration เป็น Maven scope, เก็บ exclusion เป็น XML block เต็มรูป และตรวจจับ version catalog alias เพื่อไม่ให้อะไรหลุดผ่านไปแบบไม่ resolve
ในคู่มือนี้ เราจะลงลึกการแมป scope ดูตัวอย่างจริงทั้ง Groovy และ Kotlin DSL และแนวปฏิบัติที่ทำให้การ migrate จาก Gradle ไป Maven ราบรื่นจนน่าเบื่อ — ในความหมายที่ดีที่สุด
ทำไมต้องใช้ Gradle to Maven Converter?
- ทำงานในเบราว์เซอร์ 100% ไม่มีการอัปโหลด การ parse และสร้างผลลัพธ์ทั้งหมดเกิดขึ้นบนเครื่องคุณ พิกัด artifact ภายในองค์กรและ groupId ส่วนตัวจึงไม่เคยออกจากเครื่องเลย ซึ่งช่วยให้ผ่าน security review และนโยบาย compliance ได้สบาย
- เข้าใจทั้ง Groovy และ Kotlin DSL ไม่ว่า block ของคุณจะเป็น implementation 'group:artifact:version' หรือ implementation(libs.acme.core) กล่อง input เดียวรับได้ทั้งสองภาษา ปนกันใน paste เดียวก็ยังได้
- แมป scope จริง ไม่ใช่แค่เปลี่ยนคำ เครื่องมือแปลง configuration แต่ละแบบไปยัง Maven scope ที่ถูกต้อง: implementation ไป compile (หรือ runtime เมื่อเข้าท่ากว่า), testImplementation ไป test, compileOnly ไป provided และอื่น ๆ
- exclusion รอดมาถึงปลายทาง clause แบบ exclude group:, module: กลายเป็น <exclusions> XML ที่ groupId กับ artifactId ตรงกัน สุขอนามัยของ dependency ที่คุณสร้างไว้ใน Gradle จึงเดินทางมาด้วย ไม่หายไปเงียบ ๆ
- รู้เรื่อง version catalog alias แบบ libs.* ถูกตรวจจับแล้วจัดการอย่างฉลาด: resolve เป็นพิกัดจริงเมื่อชัดเจน และ flag ไว้ให้เห็นชัดเมื่อไม่ชัด ไม่มีทางปล่อย string แบบ libs.something หลุดเข้า pom ไปเฉย ๆ
- ฟรี ไว และไม่มี friction ไม่ต้องสมัคร ไม่มี quota ไม่ต้อง install เปิด browser tab วางคู่กับ editor แล้ว paste-copy ต่อได้เลย
คุณสมบัติเด่น
| Feature | สิ่งที่ทำ |
|---|---|
| Groovy DSL parsing | อ่าน dependency ทั้งแบบ string notation และ map notation พร้อมเวอร์ชัน |
| Kotlin DSL parsing | เข้าใจ accessor call รวมถึง reference แบบ libs.* จาก version catalog |
| Configuration-to-scope map | แปลง implementation, api, testImplementation, compileOnly และอื่น ๆ |
| Exclusion preservation | สร้าง <exclusions> แบบซ้อน ที่สะท้อน clause exclude ฝั่ง Gradle ตรงตัว |
| Version catalog detection | จับ alias libs.* แล้ว resolve หรือ flag ทีละตัว แทนที่จะปล่อยผ่านไปตรง ๆ |
| Plugin block handling | แปลง plugin declaration ของ Gradle เป็น Maven build plugin snippet |
| Snippet emission | ได้ pom.xml fragment สะอาด พร้อม copy ไปวางใน POM จริงได้ทันที |
ผลลัพธ์ถูกออกแบบให้ระมัดระวัง: อะไรที่กำกวมจะถูก flag แทนการเดา เพราะ scope ที่ผิดแย่กว่า TODO ที่มองเห็นอย่างแน่นอน และเพราะประมวลผลทุกอย่างบนเครื่อง การ paste block ที่อ้างถึง artifact ภายในหรือเวอร์ชันที่ยังไม่เผยแพร่จึงปลอดภัย
วิธีใช้งาน
- หา dependency block ก่อน เปิด build.gradle หรือ build.gradle.kts แล้ว copy block dependencies { } — รวม plugins { } ถ้าเกี่ยว โดยไม่ต้องเอา build logic รอบ ๆ มาด้วย
- Paste เข้าเครื่องมือ วาง block ลงใน input panel บนหน้า Gradle to Maven Converter — บรรทัด Groovy กับ Kotlin DSL ปนกันใน paste เดียวได้
- ตรวจการแมป scope dependency แต่ละตัวจะออกมาเป็น pom.xml snippet พร้อม scope ให้เช็กว่า scope ตรงกับเจตนาจริง — นี่คือขั้นที่ต้องใช้ความคิดจริง ๆ ตามที่ลงลึกด้านล่าง
- เคลียร์ alias ที่ถูก flag alias libs.* ที่เครื่องมือ resolve ไม่ได้อย่างมั่นใจจะถูก mark ไว้ ให้เติม groupId, artifactId และ version จริงจาก gradle/libs.versions.toml ก่อนวางลง POM
- วางลง pom.xml แล้ว build เอา snippet ไปวางใน <dependencies> ของ POM เป้าหมาย แล้วรัน mvn clean verify เพื่อยืนยันว่า module ที่แปลงแล้ว resolve และ compile ผ่าน
เมื่อ Configuration กลายเป็น Scope
หัวใจของการแปลงคือการแมป configuration ไปเป็น scope สองระบบมอง visibility ต่างกัน: configuration ของ Gradle บอกว่า dependency ถูกใช้แบบไหน ส่วน scope ของ Maven บอกว่า dependency ไปตกอยู่บน classpath ไหนและแพร่แบบ transitive หรือไม่ เครื่องมือใช้ค่า default แบบนี้:
| Configuration ฝั่ง Gradle | Maven scope | หมายเหตุ |
|---|---|---|
| implementation | compile | จำเป็นทั้งตอน compile และ runtime ของ module ถ้าต้องใช้ตอน runtime เท่านั้น ใช้ runtime ดีกว่า |
| api | compile | ผู้บริโภคได้รับต่อแบบ transitive ตรงกับ compile ของ Maven — แต่มี nuance เรื่อง provided ด้านล่าง |
| testImplementation | test | อยู่บน classpath compile และ run ของ test เท่านั้น |
| compileOnly | provided | มีตอน compile แต่คาดว่า runtime จะได้จากที่อื่น |
| runtimeOnly | runtime | จำเป็นตอนรันแอปเท่านั้น — JDBC driver คือตัวอย่างคลาสสิก |
| kapt / annotationProcessor | provided (flag) | processor เป็น build-time tool ไม่ใช่ runtime dependency ดูข้อควรระวังด้านล่าง |
แถว api มี nuance มากที่สุด ใน Gradle มันหมายถึง "compile กับตัวนี้ได้และผู้ใช้ต่อได้รับต่อ" ซึ่งเข้ากับ scope compile ของ Maven โดยธรรมชาติ — แต่ servlet API, เครื่องมือแบบ Lombok ที่ใช้ตอน compile เท่านั้น หรือ container ที่ฉีด implementation ของตัวเองเข้ามา ควรใช้ provided เครื่องมือเลือก scope ที่ถูกโครงสร้างไว้ให้ ส่วนเจตนาจริงเป็นหน้าที่ของคุณตอน review
ส่วนแถว kapt และ annotationProcessor ถูก flag ไว้ตั้งใจ: processor ควรอยู่ใน annotationProcessorPaths ของ Maven compiler plugin ไม่ใช่ในรายการ dependency จึงควรมอง provided ที่ออกมาเป็นเพียงตัวแทนชั่วคราว
exclusion ถูกเก็บไว้ ไม่ใช่ทิ้ง ความสะอาดของ dependency ไม่มีความหมายถ้าระเหยหายระหว่างการแปลง ทุก clause exclude จึงกลายเป็น XML block ที่ตรงกัน:
dependencies {
implementation 'com.google.code.gson:gson:2.10.1'
implementation('org.apache.commons:commons-lang3:3.14.0') {
exclude group: 'commons-logging', module: 'commons-logging'
}
testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2'
}
ผลลัพธ์ที่เครื่องมือส่งออก:
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.14.0</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
version catalog alias ถูกตรวจจับ การวาง accessor ดิบ ๆ ของ Kotlin DSL ลง Maven ย่อมได้ขยะ ดังนั้นเครื่องมือจะจำ reference แบบ libs.* ได้ resolve ให้เท่าที่ทำได้ และ flag ที่เหลือไว้ให้เห็น:
dependencies {
implementation(libs.jackson.databind)
testImplementation(libs.mockito.core)
}
ผลลัพธ์สำหรับ alias ที่จำได้:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<!-- version อยู่ใน gradle/libs.versions.toml — ตรงนี้ควร pin เอง -->
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
ควรกลับไปกวาดตาพิกัดทุกตัวที่ออกมาเทียบกับ libs.versions.toml ของคุณ — แต่อย่างน้อยคุณจะไม่มีวันปล่อย string libs.jackson.databind ตรง ๆ ลง POM แล้วไปเจอตอน build พัง
กรณีการใช้งานจริง
การมาตรฐานบน Maven ในองค์กร
องค์กรใหญ่มักบังคับใช้ build tool เดียว: กลยุทธ์จัดการ dependency เดียว โครงสร้าง repository เดียว CI template เดียว เมื่อทีมใหม่เข้ามาพร้อม module บน Gradle เส้นทางที่เร็วที่สุดคือแปลง dependency block ก่อนแล้วค่อยปรับส่วนที่เหลือ เครื่องมือนี้เปลี่ยนงานที่น่าเบื่อที่สุดของ onboarding ให้เสร็จในไม่กี่นาที และพิกัดภายในองค์กรก็ไม่เคยออกจากเครื่องของ developer
การ audit dependency tree
งาน review ด้าน security และ architecture มักต้องการมุมมองแบบ Maven — scanner จำนวนมาก ทั้ง SBOM generator และเครื่องมือเช็ก license อ่าน pom.xml ได้เชื่อถือกว่า Gradle script มาก การแปลง dependency block ให้ได้ชุด snippet ที่ normalize แล้วไว้ป้อนเครื่องมือวิเคราะห์ โดย exclusion ยังอยู่ครบ ผล audit จึงสะท้อนสิ่งที่ build ส่งออกจริง ไม่ใช่ tree ที่บวมเกินจริง
การย้าย legacy module ทีละตัว
การ migrate จริงไม่ค่อย big-bang แอป legacy หก module ย้ายทีละ module โดย block ของแต่ละตัวถูกแปลง review และ build ผ่านก่อนเริ่มตัวถัดไป เครื่องมือนี้เข้ากับจังหวะแบบนี้พอดี: stateless ไว และทำซ้ำได้ สี่สิบ block ในหนึ่งไตรมาสก็ต้นทุนต่อ block เท่ากับแปลง block เดียว
การสร้าง pom ที่เป็นมิตรกับ BOM
ถ้า POM ปลายทาง import BOM และไม่ใส่ version ตรง ๆ ผลลัพธ์จากเครื่องมือคือจุดเริ่มที่สะอาด: dependency ทุกตัวมาพร้อม version ที่ประกาศไว้ใน Gradle ในตำแหน่งเดียวที่คาดเดาได้ พร้อมให้ลบทิ้งแล้วไปอาศัย dependencyManagement แทน ทีมที่มาตรฐานบน platform BOM ใช้ snippet เหล่านี้เป็นวัตถุดิบ แทนที่จะพิมพ์พิกัดเองทั้งหมด
แนวปฏิบัติที่ควรทำ
- ทบทวนการแมป scope ทุกตัวกับเจตนาจริง implementation ถูกแมปไป compile เป็นค่า default ซึ่งเป็นเซตที่ปลอดภัยที่สุด ถ้า dependency ไหนใช้จริงแค่ตอน runtime ให้ลดลงเป็น runtime — scope compile ที่กว้างเกินไปทำให้คนอ่าน pom ต่อไปเข้าใจผิด
- เก็บ exclusion ไว้แบบชัดเจน ถ้า Gradle เคย exclude commons-logging ให้คง exclusion นั้นใน pom ที่แปลงแล้ว แม้ Maven อาจกลบปัญหาด้วย conflict mediation ก็ตาม ความสะอาดที่ประกาศชัดรอดผ่านการเปลี่ยนเครื่องมือ ส่วนดวงไม่รอด
- resolve catalog alias ให้จบก่อนแปลงเป็นวงกว้าง ใช้เวลาห้านาทีกับ gradle/libs.versions.toml แมป alias ที่ใช้บ่อย แล้วการแปลงรอบถัด ๆ ไปจะสะอาดขึ้นทันที alias ที่ถูก flag คือฟีเจอร์ แต่ alias ที่ resolve แล้วเร็วกว่า
- build ทันทีหลังแปลงเสร็จแต่ละรอบ วาง snippet รัน mvn clean verify ให้ compiler ช่วยจับ scope ที่พลาด ขณะที่ต้นฉบับ Gradle ยังเปิดค้างอยู่ใน editor เทียบได้
- แปลงเป็นชุดเล็ก ๆ ครั้งละหนึ่ง module หรือหนึ่ง block dependencies ทำให้ review โฟกัส และถ้ามีปัญหาก็สาวต้นตอได้ทันที
- จัดการ annotation processor อย่างตั้งใจ ย้าย kapt และ annotationProcessor ที่ถูก flag ไปไว้ใน annotationProcessorPaths ของ maven-compiler-plugin แทนที่จะปล่อย provided ค้างไว้ในรายการ dependency
พร้อม migrate build file แบบไม่ปวดหัวแล้วหรือยัง? เปิด Gradle to Maven Converter วาง dependency block แรกของคุณ แล้วเปลี่ยนจากการพิมพ์เองสิบนาทีเสี่ยงพลาด ให้เหลือแค่ review สิบวินาที — ไม่ต้องสมัคร ไม่ต้องอัปโหลด ไม่ต้อง install
เครื่องมือที่เกี่ยวข้องที่คุณอาจสนใจ:
- YAML Formatter — จัดรูปแบบและตรวจ YAML config รวมถึงไฟล์พี่น้องอย่าง gradle/libs.versions.toml
- JSON Formatter — pretty-print และตรวจ JSON payload, lockfile และไฟล์ config เครื่องมือต่าง ๆ
- XML Formatter — จัดระเบียบและตรวจ pom.xml ที่คุณเพิ่งสร้างได้
ขอให้ทุก configuration ของ Gradle ลงหลักลงใน Maven scope ที่มันสมควรได้ — Online Tools Forge Team
คำถามที่พบบ่อย
ถ: Gradle to Maven Converter ใช้ฟรีจริงไหม และมีการอัปโหลด build file ขึ้น server หรือเปล่า?
ตอบ: ฟรีทั้งหมด ไม่ต้องสมัคร และไม่มีการอัปโหลดใด ๆ: การ parse ทั้งหมดเกิดขึ้นในเบราว์เซอร์ของคุณ พิกัดภายในองค์กรและ groupId ส่วนตัวจึงไม่เคยออกจากเครื่องเลย
ถ: paste block แบบ Groovy DSL กับ Kotlin DSL ลง input เดียวกันได้ไหม?
ตอบ: ได้ parser รับทั้งสองภาษาและแบบปนกัน — หลายโปรเจกต์เปลี่ยนไป Kotlin DSL แบบค่อยเป็นค่อยไป จึงมีสองสไตล์อยู่ร่วมกันช่วงหนึ่ง
ถ: ทำไม implementation ถึงแมปไป scope compile แทนที่จะเป็นอะไรแคบกว่านั้น?
ตอบ: dependency แบบ implementation อยู่บนทั้ง compile และ runtime classpath ของ module เอง ดังนั้น compile ของ Maven คือคำแปลที่ถูกโครงสร้างที่สุด ถ้าตัวไหนใช้จริงแค่ตอน runtime ให้สลับ snippet เป็น runtime ตอน review
ถ: alias แบบ libs.* ที่เครื่องมือ resolve ไม่ได้จะถูกจัดการยังไง?
ตอบ: จะถูก flag ไว้ชัดเจน ไม่เดาและไม่ปล่อยผ่านแบบดิบ ๆ ให้เปิด gradle/libs.versions.toml หา groupId, artifactId และ version จริง แล้วเติมก่อน snippet ไปถึง POM จริง
ถ: ควรจัดการ kapt และ annotationProcessor ใน pom ที่แปลงแล้วอย่างไร?
ตอบ: มอง provided ที่ถูก flag เป็นเครื่องเตือน: processor ควรอยู่ใน annotationProcessorPaths ของ Maven compiler plugin ไม่ใช่ในรายการ dependencies