[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"\u002Fresource\u002Fdocument\u002Flist?undefined":3,"\u002Fresource\u002Fdocument\u002Fquery\u002F3at7ybv04dmjc0wp?undefined":462,"\u002Fresource\u002Fadvertise\u002Flist?type=all?undefined":467},{"data":4,"status":460,"success":461},[5,148,202,291,332,370,420],{"books":6,"desc":145,"id":8,"image":146,"title":147},[7,40,63,78,93,105,117],{"cateId":8,"chapters":9,"desc":36,"id":11,"time":37,"title":38,"video":39},1,[10,15,18,21,24,27,30,33],{"bookId":11,"id":12,"indexOrder":13,"name":14},24,"8egfulw98v3h680j",0,"JavaSE 笔记（一）走进Java语言",{"bookId":11,"id":16,"indexOrder":13,"name":17},"pew6po6wrou23pk3","JavaSE 笔记（二）面向过程编程",{"bookId":11,"id":19,"indexOrder":13,"name":20},"eldst1fgrbdkmfs7","JavaSE 笔记（三）面向对象基础",{"bookId":11,"id":22,"indexOrder":13,"name":23},"48zphgkpjto8cath","JavaSE 笔记（四）面向对象高级篇",{"bookId":11,"id":25,"indexOrder":13,"name":26},"6r4llai92yc15j98","JavaSE 笔记（五）泛型程序设计",{"bookId":11,"id":28,"indexOrder":13,"name":29},"k6fmxd6qabgkwm9i","JavaSE 笔记（六）集合类与IO",{"bookId":11,"id":31,"indexOrder":13,"name":32},"qrd0xfttsz32gpqg","JavaSE 笔记（七）多线程与反射",{"bookId":11,"id":34,"indexOrder":13,"name":35},"td5tgn04nqmkrryt","JavaSE 笔记（八）GUI程序开发","基于Java25全新录制的SE课程",2025,"JavaSE 核心内容","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV163GGz2E8c",{"cateId":8,"chapters":41,"desc":59,"id":8,"time":60,"title":61,"video":62},[42,44,46,49,51,53,55,57],{"bookId":8,"id":43,"indexOrder":13,"name":14},"ibeeuwsbbi00undq",{"bookId":8,"id":45,"indexOrder":13,"name":17},"dncxjecdv4wciqcp",{"bookId":8,"id":47,"indexOrder":13,"name":48},"jviyz2hsht9ete5k","JavaSE 笔记（三）面向对象基础篇",{"bookId":8,"id":50,"indexOrder":13,"name":23},"qb9i6q9fap7bg1cc",{"bookId":8,"id":52,"indexOrder":13,"name":26},"hnkrjrkm3hjzeq6s",{"bookId":8,"id":54,"indexOrder":13,"name":29},"erpm32wduoaaqmrx",{"bookId":8,"id":56,"indexOrder":13,"name":32},"lfqtvxr7azumcwja",{"bookId":8,"id":58,"indexOrder":13,"name":35},"qs7gqok56gzc6idr","2022年制作的JavaSE版本",2022,"JavaSE 22年旧版","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1YP4y1o75f\u002F",{"cateId":8,"chapters":64,"desc":75,"id":66,"time":60,"title":76,"video":77},[65,69,72],{"bookId":66,"id":67,"indexOrder":13,"name":68},2,"g96k66kczovvbm1i","JVM 笔记（一）走进JVM",{"bookId":66,"id":70,"indexOrder":13,"name":71},"ydd7n3jg8unc3clg","JVM 笔记（二）内存管理",{"bookId":66,"id":73,"indexOrder":13,"name":74},"r9dq37de0kaeauoi","JVM 笔记（三）类与类加载","了解Java的底层运作机制","Java JVM 虚拟机","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1Er4y1r7as\u002F",{"cateId":8,"chapters":79,"desc":90,"id":81,"time":60,"title":91,"video":92},[80,84,87],{"bookId":81,"id":82,"indexOrder":13,"name":83},3,"asncyye9ya18gfar","JUC 笔记（一）再谈多线程",{"bookId":81,"id":85,"indexOrder":13,"name":86},"5tr1sm4ho6ygpt9q","JUC 笔记（二）并发编程核心",{"bookId":81,"id":88,"indexOrder":13,"name":89},"1scf51z5300mzxkh","JUC 笔记（三）并发编程进阶","你也可以成为多线程的主宰者","Java JUC 并发编程","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1JT4y1S7K8\u002F",{"cateId":8,"chapters":94,"desc":102,"id":96,"time":60,"title":103,"video":104},[95,99],{"bookId":96,"id":97,"indexOrder":13,"name":98},4,"eedesc445ygiqhil","NIO 笔记（一）基础内容",{"bookId":96,"id":100,"indexOrder":13,"name":101},"ndz9t0uunrmfmv4n","NIO 笔记（二）Netty框架专题","编写畅快的高性能网络服务器","Java NIO 网络编程","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1ar4y1J7mC\u002F",{"cateId":8,"chapters":106,"desc":114,"id":108,"time":60,"title":115,"video":116},[107,111],{"bookId":108,"id":109,"indexOrder":13,"name":110},5,"9890i8ofuadpwy2b","[扩展篇] Java 9-17新特性介绍",{"bookId":108,"id":112,"indexOrder":13,"name":113},"tsrkqvb6zpmtwh0n","[扩展篇] JavaSE关键字总结 笔记","精彩仍在继续，不要停止脚步","其他内容","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1tU4y1y7Fg\u002F",{"cateId":8,"chapters":118,"desc":141,"id":120,"time":142,"title":143,"video":144},[119,123,126,129,132,135,138],{"bookId":120,"id":121,"indexOrder":13,"name":122},6,"4db9h32opv7imszh","JavaSE 笔记（一）面向过程编程",{"bookId":120,"id":124,"indexOrder":13,"name":125},"c93u3v37br7hgn1q","JavaSE 笔记（二）面向对象基础篇",{"bookId":120,"id":127,"indexOrder":13,"name":128},"yglsjde9gi1jxkcb","JavaSE 笔记（三）泛型与集合类",{"bookId":120,"id":130,"indexOrder":13,"name":131},"ilhi987n986rmvo3","JavaSE 笔记（四）异常机制",{"bookId":120,"id":133,"indexOrder":13,"name":134},"pqv38vexmenglk4k","JavaSE 笔记（五）IO",{"bookId":120,"id":136,"indexOrder":13,"name":137},"jiq41n87i9ia7ilw","JavaSE 笔记（六）多线程",{"bookId":120,"id":139,"indexOrder":13,"name":140},"wn7x2mge9ws79zps","JavaSE 笔记（七）反射","此版本为早期录制的旧版本",2021,"JavaSE 21年旧版","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1Gv411T7pi\u002F","包含JavaSE基础路线全部教程笔记，打下坚实的基础","https:\u002F\u002Fpic2.zhimg.com\u002F80\u002Fv2-bf1a927f037a79f4d57d9ae543430a0d_1440w.webp","JavaSE 系列笔记 ☕️",{"books":149,"desc":199,"id":66,"image":200,"title":201},[150,166,178],{"cateId":66,"chapters":151,"desc":162,"id":153,"time":163,"title":164,"video":165},[152,156,159],{"bookId":153,"id":154,"indexOrder":13,"name":155},21,"iqbc2haub31bwqtz","Lombok 极速上手",{"bookId":153,"id":157,"indexOrder":13,"name":158},"ijay2hay19kn1k031","Mybatis 快速上手",{"bookId":153,"id":160,"indexOrder":13,"name":161},"ru4ogh2waocpn4jo","Maven 快速上手","JavaWeb阶段必须扩展知识点",2024,"常用知识讲解","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1gb421J7ok\u002F",{"cateId":66,"chapters":167,"desc":175,"id":169,"time":163,"title":176,"video":177},[168,172],{"bookId":169,"id":170,"indexOrder":13,"name":171},22,"ek20yvb6huhxizx7","JavaWeb 笔记（一）计算机网络基础",{"bookId":169,"id":173,"indexOrder":13,"name":174},"pgevws6w2krkffa4","JavaWeb笔记（二）Java与数据库","全面升级的JavaWeb课程","JavaWeb 网站开发","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1kS421X7rq\u002F",{"cateId":66,"chapters":179,"desc":196,"id":181,"time":142,"title":197,"video":198},[180,184,187,190,193],{"bookId":181,"id":182,"indexOrder":13,"name":183},7,"ggwwj09j2vkfftvd","JavaWeb 笔记（一）Java网络编程",{"bookId":181,"id":185,"indexOrder":13,"name":186},"sauvq105istskjaz","JavaWeb 笔记（二）数据库基础",{"bookId":181,"id":188,"indexOrder":13,"name":189},"xgbeasmvrhxx9tn4","JavaWeb 笔记（三）Java与数据库",{"bookId":181,"id":191,"indexOrder":13,"name":192},"k7dfwua3bsezvw9q","JavaWeb 笔记（四）前端基础",{"bookId":181,"id":194,"indexOrder":13,"name":195},"ycpagby2v7j4p728","JavaWeb 笔记（五）后端开发","搭建属于自己的Web网站","JavaWeb 旧版","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1CL4y1i7qR\u002F","包含JavaWeb路线全套笔记，从零开始搭建自己的网站！","https:\u002F\u002Fpic3.zhimg.com\u002F80\u002Fv2-df3b38e3012258ed70c23b586309e3f6_1440w.webp","JavaWeb 系列笔记 🚛",{"books":203,"desc":288,"id":81,"image":289,"title":290},[204,220,235,255,273],{"cateId":81,"chapters":205,"desc":216,"id":207,"time":217,"title":218,"video":219},[206,210,213],{"bookId":207,"id":208,"indexOrder":13,"name":209},8,"h7sjo5oy0l03607e","SSM笔记（一）Spring基础",{"bookId":207,"id":211,"indexOrder":13,"name":212},"eve8gq72qmdb46sg","SSM笔记（二）SpringMvc基础",{"bookId":207,"id":214,"indexOrder":13,"name":215},"63v73g0zh1qlr6fk","SSM笔记（三）SpringSecurity基础","Spring的探索之路从这里开始",2023,"JavaSSM 基础部分","[\"https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1Kv4y1x7is\u002F\", \"https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1Lh4y1M7kx\u002F\", \"https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1fV411M7aS\u002F\"]",{"cateId":81,"chapters":221,"desc":232,"id":223,"time":217,"title":233,"video":234},[222,226,229],{"bookId":223,"id":224,"indexOrder":13,"name":225},16,"0k66v5r6slsfuog4","SpringBoot笔记（一）核心内容",{"bookId":223,"id":227,"indexOrder":13,"name":228},"bqlrnc2yvkaxo8s1","SpringBoot笔记（二）数据交互",{"bookId":223,"id":230,"indexOrder":13,"name":231},"wci9lb9tgea866jt","SpringBoot笔记（三）前后端分离","SpringBoot全新重制版","SpringBoot 新版","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1xu4y1m7UP\u002F",{"cateId":81,"chapters":236,"desc":252,"id":238,"time":60,"title":253,"video":254},[237,240,243,246,249],{"bookId":238,"id":239,"indexOrder":13,"name":225},9,"e43gl1ilygps032v",{"bookId":238,"id":241,"indexOrder":13,"name":242},"emnmd8nzfdb3hr50","SpringBoot笔记（二）Git版本控制",{"bookId":238,"id":244,"indexOrder":13,"name":245},"jjlolj5igvttvyhv","SpringBoot笔记（三）Redis数据库",{"bookId":238,"id":247,"indexOrder":13,"name":248},"skgr4ivb5curdoux","SpringBoot笔记（四）其他框架介绍",{"bookId":238,"id":250,"indexOrder":13,"name":251},"le91fqhu4dqui1k4","SpringBoot笔记（五）Linux系统","逐步走向企业级开发","SpringBoot 旧版","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1UL411V7f3\u002F",{"cateId":81,"chapters":256,"desc":270,"id":258,"time":60,"title":271,"video":272},[257,261,264,267],{"bookId":258,"id":259,"indexOrder":13,"name":260},10,"oejzo0l77zeb6a7e","SpringCloud笔记（一）微服务基础",{"bookId":258,"id":262,"indexOrder":13,"name":263},"f6eya9taaelsl35p","SpringCloud笔记（二）微服务进阶",{"bookId":258,"id":265,"indexOrder":13,"name":266},"35v1hbsfcdgagdnw","SpringCloud笔记（三）微服务应用",{"bookId":258,"id":268,"indexOrder":13,"name":269},"a782u84512tyuo1m","SpringCloud笔记（四）消息队列","体验微服务架构带来的魅力","SpringCloud 进阶","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1AL4y1j7RY\u002F",{"cateId":81,"chapters":274,"desc":285,"id":276,"time":142,"title":286,"video":287},[275,278,280,282],{"bookId":276,"id":277,"indexOrder":13,"name":209},11,"efjw75u8a251qxk5",{"bookId":276,"id":279,"indexOrder":13,"name":212},"guc134xb7sl78vju",{"bookId":276,"id":281,"indexOrder":13,"name":215},"u8ekxxucowr2b1tm",{"bookId":276,"id":283,"indexOrder":13,"name":284},"vkpmw9wbej21nei6","SSM笔记（四）MySQL进阶","此教程为2021年旧版教程","JavaSSM 旧版","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1xL4y1H7Tq\u002F","包含Spring全套框架笔记，从开始到Spring Boot，以及众多运维小知识。","https:\u002F\u002Fpic4.zhimg.com\u002F80\u002Fv2-28c3144421220d7c048703281bc34f63_1440w.webp","Spring 系列笔记 🍏",{"books":292,"desc":329,"id":96,"image":330,"title":331},[293,308],{"cateId":96,"chapters":294,"desc":305,"id":296,"time":60,"title":306,"video":307},[295,299,302],{"bookId":296,"id":297,"indexOrder":13,"name":298},12,"jd3e8u5cmvx5gco6","C语言（一）计算机思维导论",{"bookId":296,"id":300,"indexOrder":13,"name":301},"lqv77apvx82nkkio","C语言（二）基础语法",{"bookId":296,"id":303,"indexOrder":13,"name":304},"xb0b9t37gyv96xns","C语言（三）高级特性","包含高等院校需要教授的全部内容","C语言程序设计","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1Cr4y137os\u002F",{"cateId":96,"chapters":309,"desc":326,"id":311,"time":60,"title":327,"video":328},[310,314,317,320,323],{"bookId":311,"id":312,"indexOrder":13,"name":313},13,"8a046ps2e4w6k4py","数据结构与算法（一）线性结构篇",{"bookId":311,"id":315,"indexOrder":13,"name":316},"3ma8db91f9zrnkja","数据结构与算法（二）树形结构篇",{"bookId":311,"id":318,"indexOrder":13,"name":319},"0lsjm59k7cgu4tpr","数据结构与算法（三）散列表篇",{"bookId":311,"id":321,"indexOrder":13,"name":322},"0qzy7bogo0g2pusa","数据结构与算法（四）图结构篇",{"bookId":311,"id":324,"indexOrder":13,"name":325},"6gmcxcikcilyxblj","数据结构与算法（五）排序算法篇","虽然很难，但是它是考研必学科目","数据结构与算法","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV13W4y127Ey\u002F","你的内心一直有一个坚定的声音在告诉你，一定要考上一名研究生，向着未来前进吧！","https:\u002F\u002Fpic2.zhimg.com\u002F80\u002Fv2-ac128404efb29ce1c9d1ccc61024f1d1_1440w.webp","C语言 系列笔记 🥬",{"books":333,"desc":367,"id":108,"image":368,"title":369},[334,349,358],{"cateId":108,"chapters":335,"desc":346,"id":337,"time":163,"title":347,"video":348},[336,340,343],{"bookId":337,"id":338,"indexOrder":13,"name":339},17,"urw2e6gg1lprv65w","Kotlin（一）基础语法",{"bookId":337,"id":341,"indexOrder":13,"name":342},"t7lnl87f74f3v1ju","Kotlin（二）类与对象",{"bookId":337,"id":344,"indexOrder":13,"name":345},"v1zzvki0knb1xvml","Kotlin（三）高级特性","包含Kotlin语言完整基础部分","Kotlin程序设计基础","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1P94y1c7tV\u002F",{"cateId":108,"chapters":350,"desc":355,"id":352,"time":163,"title":356,"video":357},[351],{"bookId":352,"id":353,"indexOrder":13,"name":354},18,"ovbzpe7065bye1st","Kotlin扩展（一）","包含Kotlin额外扩展知识","Kotlin扩展篇","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1Hg4y1m7Ca\u002F",{"cateId":108,"chapters":359,"desc":364,"id":361,"time":163,"title":365,"video":366},[360],{"bookId":361,"id":362,"indexOrder":13,"name":363},19,"3at7ybv04dmjc0wp","Gradle基础教程","Gradle配置教程（Kotlin）","Gradle教程","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1Fc411x7xF\u002F","Kotlin让JVM平台焕发新的生机，让语言的表达更加优美","https:\u002F\u002Fpic2.zhimg.com\u002F80\u002Fv2-be815568f7c79c64cdaa171b0409786d_1440w.webp","Kotlin 系列笔记 ☘️",{"books":371,"desc":418,"id":120,"title":419},[372,391,403],{"cateId":120,"chapters":373,"desc":387,"id":375,"time":388,"title":389,"video":390},[374,378,381,384],{"bookId":375,"id":376,"indexOrder":13,"name":377},26,"zjf5qapwqtqiohcn","JavaScript笔记（一）基础语法",{"bookId":375,"id":379,"indexOrder":13,"name":380},"95jc6sjyjwcp9pvp","JavaScript笔记（二）核心知识",{"bookId":375,"id":382,"indexOrder":13,"name":383},"j35cdc1qz8dzq7pn","JavaScript笔记（三）进阶知识",{"bookId":375,"id":385,"indexOrder":13,"name":386},"sdhodlihphnpcg37","JavaScript笔记（四）前端基础","包含JavaScript最新语法规范讲解",2026,"JavaScript教程","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1xq6gBgESU",{"cateId":120,"chapters":392,"desc":400,"id":394,"time":37,"title":401,"video":402},[393,397],{"bookId":394,"id":395,"indexOrder":13,"name":396},23,"bsisgazdftiz3o9c","HTML5笔记（一）基础内容",{"bookId":394,"id":398,"indexOrder":13,"name":399},"njol93fs34gfwuzf","HTML5笔记（二）高级内容","包含HTML基础内容和相关知识点","HTML5核心教程","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1BrBiYNEWg",{"cateId":120,"chapters":404,"desc":415,"id":406,"time":37,"title":416,"video":417},[405,409,412],{"bookId":406,"id":407,"indexOrder":13,"name":408},25,"jo74ciirtg8wh90y","CSS笔记（一）基础入门",{"bookId":406,"id":410,"indexOrder":13,"name":411},"ap5ixyomoejuw4ue","CSS笔记（二）盒模型和布局",{"bookId":406,"id":413,"indexOrder":13,"name":414},"4djgk5xy1lzpiuf2","CSS笔记（三）变换和过渡","包含CSS3基础内容和相关知识点","CSS3核心教程","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1sQeEzFEKi","包含Web前端学习路径全部教程笔记，打下坚实的基础","Web前端 系列笔记",{"books":421,"desc":458,"id":423,"image":368,"title":459},[422,432,450],{"cateId":423,"chapters":424,"desc":429,"id":426,"time":163,"title":430,"video":431},100,[425],{"bookId":426,"id":427,"indexOrder":13,"name":428},20,"o0ab271mkdsas87","Markdown基础语法","编写简洁而又优美的文档","Markdown教程","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1eJ4m157kC",{"cateId":423,"chapters":433,"desc":447,"id":435,"time":60,"title":448,"video":449},[434,438,441,444],{"bookId":435,"id":436,"indexOrder":13,"name":437},14,"6386mh7anqt4tzyv","设计模式（一）面向对象设计原则",{"bookId":435,"id":439,"indexOrder":13,"name":440},"8ftkb38wfn6ox0ug","设计模式（二）创建型",{"bookId":435,"id":442,"indexOrder":13,"name":443},"i1msql1k8y70etey","设计模式（三）结构型",{"bookId":435,"id":445,"indexOrder":13,"name":446},"5434a3cyyjvwhs8s","设计模式（四）行为型","使你的编码水平得到质的飞跃","设计模式系列","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1u3411P7Na\u002F",{"cateId":423,"chapters":451,"desc":456,"id":453,"time":60,"title":457},[452],{"bookId":453,"id":454,"indexOrder":13,"name":455},15,"zj9uvg0sp3b0sok8","Docker 容器技术 笔记","这里包含其他中间件课程笔记","其他中间件笔记","我们对知识的探索从未停止，只有不断地学习，才能走向美好的未来！","其他笔记分类 🌽",200,true,{"data":463,"status":460,"success":461},{"bookId":361,"content":464,"id":362,"indexOrder":8,"introduction":465,"lastUpdate":466,"name":363},"![image-20231227235639447](https:\u002F\u002Fs2.loli.net\u002F2023\u002F12\u002F27\u002FgKzYuobaJSD4QR6.png)\n\n# Gradle依赖管理（基于Kotlin DSL）\n\n> **注意：** 如果不是工作原因或是编写安卓项目必须要用Gradle，建议学习Maven即可，Gradle的学习成本相比Maven高很多，而且学了有没有用还是另一回事，所以，作为过来人我不希望浪费大家这几个小时的时间。\n\n欢迎各位小伙伴来到Gradle的学习课程，在开始本课程之前，您需要完成以下前置课程：\n\n* **SpringBoot教程：**  本Gradle教程全篇以构建Java项目为例进行讲解，建议至少学习到SpringBoot及之后再观看。\n* **Kotlin程序设计教程：**  Gradle脚本基于Kotlin DSL编写，必须掌握Kotlin程序设计基础知识。\n* **Maven依赖管理教程：**  推荐先了解一下更简单的Maven依赖管理，会更加容易理解。\n* **（可选）SpringCloud视频教程：**  包含对于多模块项目的介绍。\n\nGradle构建工具是一个快速、可靠和适应性强的开源构建自动化工具，具有优雅和可扩展的声明性构建语言，Gradle包含许多优势：\n\n- Gradle是JVM平台最受欢迎的构建系统，也是Android和Kotlin多平台项目的默认选择，它拥有丰富的社区插件生态系统。\n- Gradle可以使用其内置函数、第三方插件或自定义构建逻辑自动执行各种软件构建场景。\n- Gradle提供了一种高级、声明式和富有表现力的构建语言，使阅读和编写构建逻辑变得很轻松。\n- Gradle快速、可扩展，可以构建任意规模和复杂度的项目。\n- Gradle产生可靠的结果，同时受益于增量构建、构建缓存和并行执行等优化。\n\nGradle支持Android、Java、Kotlin Multiplatform、Groovy、Scala、Javascript和C\u002FC++等热门语言的构建工作：\n\n![用户指南语言](https:\u002F\u002Fs2.loli.net\u002F2023\u002F12\u002F28\u002FD3wn2eRj8TdOaJy.png)\n\n所有常见的IDE都支持Gradle，包括Android Studio、IntelliJ IDEA、Visual Studio Code、Eclipse和NetBeans。\n\n![用户指南ides](https:\u002F\u002Fs2.loli.net\u002F2023\u002F12\u002F28\u002Fk1gcEao7ULFhS2Y.png)\n\n接下来就让我们开始学习Gradle吧。\n\n## 走进Gradle\n\n这一部分我们来学习Gradle的安装和初始化。\n\n### 安装Gradle环境\n\n在使用Gradle之前，我们先来看看如何安装，这里演示Windows环境下，如何进行安装，其他平台请参考官方文档：https:\u002F\u002Fdocs.gradle.org\u002Fcurrent\u002Fuserguide\u002Finstallation.html#installation\n\n在开始安装之前，首先确保您已经安装好Java环境（使用`java --version`命令进行查看）版本不能低于Java 8，本教程使用的是Java 17版本：\n\n![image-20231228201351164](https:\u002F\u002Fs2.loli.net\u002F2023\u002F12\u002F28\u002FItXV8bmFSpYWAKE.png)\n\n确认无误后，我们进入到Gradle官网下载最新版本：[下载地址](https:\u002F\u002Fgradle.org\u002Freleases\u002F?_gl=1*bycxul*_ga*MTkwMDMxNTMyMS4xNzAzNjkxNjA5*_ga_7W7NC6YNPT*MTcwMzc2MDE2OS4yLjEuMTcwMzc2MTE0OS4zNi4wLjA.)，这里我们选择直接下载已经编译好的二进制文件：\n\n![image-20231228202158100](https:\u002F\u002Fs2.loli.net\u002F2023\u002F12\u002F28\u002FwEH4a6MRqxWUKNQ.png)\n\n**注意：**  本教程教学采用的是8.5版本，如果各位小伙伴在学习时官方已经推出了新的版本，请务必选择跟教程相同的版本，因为Gradle的版本兼容性很差，不同版本出现特性变化的情况很常见，容易出现不兼容的情况。\n\n下载完成后解压，然后将解压得到的目录拖到你想要安装的位置，本教程就放到`C:\\Program Files`目录下：\n\n![image-20231228202341759](https:\u002F\u002Fs2.loli.net\u002F2023\u002F12\u002F28\u002FZIdJWKfi6o12v9E.png)\n\n接着我们需要配置相应的环境变量才能使用，打开环境变量配置面板：\n\n![image-20231228202604085](https:\u002F\u002Fs2.loli.net\u002F2023\u002F12\u002F28\u002FaUem1XCgJou8ZFR.png)\n\n添加环境变量`GRADLE_HOME`，变量值为刚刚存放的Gradle文件夹位置。接着我们在Path环境变量中添加对应的bin目录：\n\n![image-20231228202750236](https:\u002F\u002Fs2.loli.net\u002F2023\u002F12\u002F28\u002Fxi26RJwybNVumrK.png)\n\n添加完成后，我们打开一个新的命令窗口，输入`gradle -v`命令查看是否配置生效：\n\n![image-20231228202834050](https:\u002F\u002Fs2.loli.net\u002F2023\u002F12\u002F28\u002FdMrSJuasKWNZeEP.png)\n\n可以看到当前的Gradle相关版本信息已经打印到控制台了。\n\n**注意：**  如果配置没有生效，可以尝试重启电脑或者查看是否路径编写错误。\n\n### 初始化Gradle项目\n\n安装完成Gradle之后，我们就可以开始正式进行学习了，首先我们还是来尝试创建一个Gradle项目。\n\n这里我们创建一个新的空文件夹用于测试：\n\n![image-20231228203506625](https:\u002F\u002Fs2.loli.net\u002F2023\u002F12\u002F28\u002FOYz3KtRcX9FSvxq.png)\n\n接着，我们在当前目录打开CMD窗口，并执行以下命令：\n\n```shell\ngradle init\n```\n\n出现以下内容，让我们选择当前项目的类型，这里我们选择`2`应用类型：\n\n```sh\n> Task :wrapper\n\nSelect type of project to generate:\n  1: basic\n  2: application\n  3: library\n  4: Gradle plugin\nEnter selection (default: basic) [1..4] 2\n```\n\n接着是选择我们程序使用的语言类型，本教程统一使用Java语言讲解，选择`3`号：\n\n```sh\n> Task :init\n\nSelect implementation language:\n  1: C++\n  2: Groovy\n  3: Java\n  4: Kotlin\n  5: Scala\n  6: Swift\nEnter selection (default: Java) [1..6] 3\n```\n\n接着是是否生成多个子项目结构，我们先按照最简单的单个项目进行讲解，这里直接选择`no`即可：\n\n```sh\nGenerate multiple subprojects for application? (default: no) [yes, no] no\n```\n\n接着是编写Gradle脚本采用的语言，目前Gradle支持Kotlin和Groovy两种，且目前官方推荐的是Kotlin语言，所以这里就选择`1`号：\n\n```sh\nSelect build script DSL:\n  1: Kotlin\n  2: Groovy\nEnter selection (default: Kotlin) [1..2] 1\n```\n\nGradle还需要配置一个测试使用的框架，这里我们选择默认的就行：\n\n```sh\nSelect test framework:\n  1: JUnit 4\n  2: TestNG\n  3: Spock\n  4: JUnit Jupiter\nEnter selection (default: JUnit Jupiter) [1..4] \n```\n\n接着是当前项目的名称以及包名，默认就是当前目录名字，这个自己根据情况选择吧：\n\n```sh\nProject name (default: Test):\n\nSource package (default: test): com.test\n```\n\n最后是采用的Java版本，因为这里我们安装的是Java17，所以直接输入17即可，还有是否选择使用新特性直接`no`就好了：\n\n```sh\nEnter target version of Java (min. 7) (default: 17): 17\n\nGenerate build using new APIs and behavior (some features may change in the next minor release)? (default: no) [yes, no]\n```\n\n接着我们可以看到项目成功完成了初始化操作：\n```sh\n> Task :init\nTo learn more about Gradle by exploring our Samples at https:\u002F\u002Fdocs.gradle.org\u002F8.5\u002Fsamples\u002Fsample_building_java_applications.html\n\nBUILD SUCCESSFUL in 12m 38s\n2 actionable tasks: 2 executed\n```\n\n现在回到我们的文件夹中，可以看到已经生成了大大小小的文件了：\n\n![image-20231228204959573](https:\u002F\u002Fs2.loli.net\u002F2023\u002F12\u002F28\u002FhvfrjEpsmGn71wR.png)\n\n**注意：**  不同版本的Gradle对我们使用的IDEA版本同样存在兼容性问题，这里推荐使用`IntelliJ IDEA 2023.3`或更高版本，旧版本的IDEA可能会不支持当前的Gradle 8.5版本。\n\n我们可以直接在IDEA中打开这个项目，打开后正常情况下会自动开始初始化，初始化过程中会下载很多东西，包括Gradle当前版本本身，以及它所需要的所有依赖包以及Kotlin相关库：\n\n![image-20231228210139642](https:\u002F\u002Fs2.loli.net\u002F2023\u002F12\u002F28\u002FCebTGlcu3Zx8N1y.png)\n\n不对啊，我们本地不是已经安装了Gradle吗，怎么初始化项目的时候又要下载一次呢？这是因为Gradle Wrapper的作用，由于Gradle的跨版本兼容性很差，因此它指定了当前项目中使用的Gradle版本，使得不同的开发人员或CI\u002FCD系统都能使用相同的Gradle版本来构建项目，从而提高项目的一致性和可移植性。Gradle Wrapper的另一个好处是，它可以自动下载和使用正确版本的Gradle，无需手动安装或配置Gradle，使得项目的维护和协作更加方便，后续别人拿到我们这个项目的时候，不需要自己在系统中部署Gradle环境。\n\n这个过程中可能会很卡，因为服务器在国外（建议挂梯）直到出现`BUILD SUCCESSFUL`之后，表示初始化完成：\n\n![image-20231228215331330](https:\u002F\u002Fs2.loli.net\u002F2023\u002F12\u002F28\u002FowxVuWtFXYKS5vA.png)\n\n此时我们的项目目录里面已经存在这些内容了：\n\n![image-20231228215500566](https:\u002F\u002Fs2.loli.net\u002F2023\u002F12\u002F28\u002F2e4oAKW5vyitdkl.png)\n\n我们来依次介绍一下这些内容分别代表什么以及有什么作用：\n\n* `.gradle`：Gradle自动生成的项目缓存目录。\n* `.idea`：这个是IDEA的项目配置目录，跟Gradle生成的没关系，无视掉就行。\n* `app`：存放整个项目的源代码、测试等，这里面就是我们写代码的地方了。\n  * `build.gradle.kts`：项目的gradle构建脚本。\n  * `src`：存放源代码和测试代码。\n    * `main`：编写所有项目核心代码。\n    * `test`：编写项目测试代码。\n* `gradle`：包含JAR文件和Gradle Wrapper的配置。\n* `gradlew`：适用于macOS和Linux的使用Gradle Wrapper执行构建的脚本（这里的版本就是GradleWrapper指定的版本）\n* `gradlew.bat`：适用于Windows的使用Gradle Wrapper执行构建的脚本。\n* `settings.gradle.kts`：定义子项目列表的项目配置文件，也是最关键的设置文件。\n\n除了以上文件以外的其他文件，一般都是一些额外的Git文件，例如`.gitignore`，不属于Gradle的范畴，无视掉就可以了。\n\nGradle项目在生成时默认为我们创建了一个测试的主类：\n\n```java\n\u002F*\n * This Java source file was generated by the Gradle 'init' task.\n *\u002F\npackage com.test;\n\npublic class App {\n    public String getGreeting() {\n        return \"Hello World!\";\n    }\n\n    public static void main(String[] args) {\n        System.out.println(new App().getGreeting());\n    }\n}\n```\n\n我们可以在IDEA中尝试直接运行：\n\n![image-20231228222003591](https:\u002F\u002Fs2.loli.net\u002F2023\u002F12\u002F28\u002F6FAUa1McupVmjGh.png)\n\n这样，咱们的第一个Gradle项目就成功创建并运行了。\n\n### Gradle常用命令\n\n我们在一开始就说了，Gradle最主要的目的就是为了构建大型项目，其中构建项目的常用命令非常关键，我们这节课就来学习一下。\n\n首先我们可以查看一下所有Gradle支持的任务，这里我们使用GradleWapper提供的`gradlew`进行操作，使用方式其实和`gradle`命令是一样的，只是这里用的是生成的（注意Windows平台下需要使用gradlew.bat来运行）\n\n```\n.\u002Fgradlew.bat task\n```\n\n```sh\n.\u002Fgradlew task\n```\n\n其中包含了大量的命令操作：\n\n![image-20231228223724192](https:\u002F\u002Fs2.loli.net\u002F2023\u002F12\u002F28\u002FFrRusjEThGVLzKU.png)\n\n比如第一个`run`命令就可以将当前项目以JVM应用程序的形式运行：\n\n![image-20231228223822107](https:\u002F\u002Fs2.loli.net\u002F2023\u002F12\u002F28\u002FFIbNteufMWG5Q93.png)\n\n这个命令会自动编译并运行我们的项目，我们也可以手动执行其中的每一步，现在就来尝试一下吧，首先先执行一次清理命令，将整个项目目录进行一次清理，主要清理掉的就是构建出来的文件：\n\n```sh\n.\u002Fgradlew clean\n```\n\n我们在编写好一个Java项目之后，第一步就是编译成class文件，然后才能开始运行，所以，我们可以使用以下命令来编译代码：\n\n```sh\n.\u002Fgradlew classes\n.\u002Fgradlew classes -q  #安静模式，只执行，不打印日志\n```\n\n![image-20231228222559491](https:\u002F\u002Fs2.loli.net\u002F2023\u002F12\u002F28\u002FCINiswEULbZeqSn.png)\n\n执行完成之后，如果出现`BUILD SUCCESSFUL`表示编译成功，此时在app目录下会生成一个新的build目录，此目录中存放的就是编译之后的相关文件了，其中classes目录存放的就是所有编译之后的class文件：\n\n![image-20231228222729882](https:\u002F\u002Fs2.loli.net\u002F2023\u002F12\u002F28\u002F8qR5mHGpwYac6lB.png)\n\n有些时候我们的项目可能需要在编译之后运行一些测试用例，来快速查看是否存在一些问题，这在开发过程中非常常见，我们可以看到Gradle默认情况下为我们生成了一个简易的测试用例：\n\n```kotlin\nclass AppTest {\n  \t\u002F\u002F默认使用的是JUnit作为单元测试工具\n    @Test void appHasAGreeting() {\n        App classUnderTest = new App();\n      \t\u002F\u002F这里是判断如果结果为Null的话就抛出异常\n        assertNotNull(classUnderTest.getGreeting(), \"app should have a greeting\");\n    }\n}\n```\n\n我们可以将App类中的getGreeting方法的返回值设置为null试试看：\n\n```java\npublic class App {\n    public String getGreeting() {\n        return null;\n    }\n    ...\n}\n```\n\n使用以下命令来执行测试：\n\n```sh\n.\u002Fgradlew test\n```\n\n![image-20231228225443787](https:\u002F\u002Fs2.loli.net\u002F2023\u002F12\u002F28\u002F5mJz4R23oGckANH.png)\n\n可以看到测试失败了，并且Gradle还自动为我们生成了一个错误报告，其中明确指出了我们出现错误的测试用例：\n\n![image-20231228225519918](https:\u002F\u002Fs2.loli.net\u002F2023\u002F12\u002F28\u002FZaxH84OWjzDUdYi.png)\n\n我们也可以快速执行一个完整的编译+测试流程来构建整个项目，并得到打包好的jar等文件，使用以下命令来完成：\n\n```sh\n.\u002Fgradlew build\n```\n\n如果某些时候我们不想执行测试，只想构建整个项目，也可以添加参数跳过：\n\n```sh\n.\u002Fgradlew build -x test\n```\n\n只不过这样去敲命令实在是太累了，在IDEA中已经自动帮助我们继承了Gradle插件，我们可以直接展开右侧工具栏，双击就能使用Gradle命令了，非常方便：\n\n![image-20231228230031414](https:\u002F\u002Fs2.loli.net\u002F2023\u002F12\u002F28\u002FeoIXKlRmBfhyF8D.png)\n\n## 项目配置\n\n前面我们介绍了Gradle项目的搭建与使用，我们先来看一下整个Gradle项目构建的流程：\n\n![image-20240114000012919](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F14\u002FDS7tZTxrLBsJa69.png)\n\n大概清楚构建流程后，我们接着就来了解一下Gradle项目是如何进行配置的。\n\n### 配置文件介绍\n\n设置文件`settings.gradle`是整个Gradle项目的入口点：\n\n![image-20240113150604473](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F13\u002F6yJS1i3reDfGL4R.png)\n\n*设置文件*用于定义所有的子项目，并让它们参与到构建中，Gradle支持单项目和多项目的构建：\n\n- 对于单个项目构建，设置文件是可选的。\n- 对于多项目构建，设置文件必须存在，并在其中定义所有子项目。\n\n设置文件可以使用Groovy语言（名称为`settings.gradle`）或是Kotlin语言（名称为`settings.gradle.kts`）编写，本教程一律采用Kotlin语言进行讲解，设置文件通常位于项目的根目录中：\n\n![image-20240113151121760](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F13\u002Fg6X89wValSPdhFA.png)\n\n一个标准的Gradle设置文件按照以下样式进行编写，这里使用Kotlin语言介绍：\n\n```kotlin\nrootProject.name = \"root-project\"   \u002F\u002FrootProject对象代表当前这个项目，其name属性就是当前项目的名称\n\ninclude(\"sub-project-a\")     \u002F\u002F所有的子项目使用include()函数进行添加，如果没有子项目可以不写\ninclude(\"sub-project-b\")\ninclude(\"sub-project-c\")\n```\n\n接着我们来看针对于单个项目的构建文件，其中`gradle.build`文件就是对应的构建配置，这里我们使用的是Kotlin语言，因此项目中会存在一个`gradle.build.kts`文件：\n\n```kotlin\nplugins {\n    id(\"java\")\n}\n\ngroup = \"cn.itbaima\"\nversion = \"1.0-SNAPSHOT\"\n\nrepositories {\n    mavenCentral()\n}\n\ndependencies {\n    testImplementation(platform(\"org.junit:junit-bom:5.9.1\"))\n    testImplementation(\"org.junit.jupiter:junit-jupiter\")\n}\n\ntasks.test {\n    useJUnitPlatform()\n}\n```\n\n可以看到，在这个配置文件中存在大量的Lambda语句，这也使得整个配置文件写起来更加简介美观，所以说虽然Gradle依赖于JVM平台，但是仅支持Kotlin和Groovy，它们相比Java在语法上存在更大的优势，更适合编写这种类似脚本一样的配置文件。\n\n我们首先来看最顶上的plugins函数，后面的Lambda中编写了当前项目需要使用到的插件：\n\n```kotlin\nplugins {\n    id(\"java\")\n}\n```\n\n使用`id()`函数指定需要使用的插件，这里使用的是`java`插件，`java`插件将Java编译以及测试和捆绑功能添加到项目中，为建任何类型的 Java 项目提供支持。\n\n当然，除了java插件之外，我们如果需要构建其他类型的项目，也可以使用多种多样的插件：\n\n```kotlin\nplugins {\n    id(\"cpp-application\")   \u002F\u002F用于构建C++应用程序  \n}\n```\n\n```kotlin\nplugins {\n    id(\"swift-application\")   \u002F\u002F用于在MacOS构建Swift应用程序\n}\n```\n\n```kotlin\nplugins {\n    id(\"org.jetbrains.kotlin.jvm\") version \"1.9.0\"  \u002F\u002F使用version中缀函数为id指定的插件添加版本\n}\n```\n\n有关插件相关的内容，我们会在后面的部分深入为大家介绍，不同的插件会为Gradle添加不同的任务。\n\n这里的group和version分别设置了当前项目所属的组名称和版本：\n\n```kotlin\ngroup = \"cn.itbaima\"\nversion = \"1.0-SNAPSHOT\"\n```\n\n接着是所有依赖的仓库配置，默认使用的是Maven中心仓库：\n\n```kotlin\nrepositories {\n    mavenCentral()\n}\n```\n\n接着是所有的依赖列表，这里默认为我们导入了JUnit相关依赖用于测试：\n\n```kotlin\ndependencies {\n    testImplementation(platform(\"org.junit:junit-bom:5.9.1\"))\n    testImplementation(\"org.junit.jupiter:junit-jupiter\")\n}\n```\n\n最后是任务相关配置，这里对`test`任务进行了相关配置：\n\n```kotlin\ntasks.test {\n    useJUnitPlatform()\n}\n```\n\n从下节课开始我们来详细介绍一下各个部分。\n\n### 编写设置文件\n\n我们前面介绍了Gradle构建的大致流程和配置文件各个部分，而设置文件`settings.gradle.kts`则是整个构建的入口点，在Gradle构建生命周期的早期，初始化阶段会在项目根目录中找到设置文件。\n\n当找到设置文件`settings.gradle(.kts)`时，Gradle会实例化一个[`Settings`](https:\u002F\u002Fdocs.gradle.org\u002Fcurrent\u002Fdsl\u002Forg.gradle.api.initialization.Settings.html)对象，我们可以通过此对象来声明要包含在构建中的所有项目，包括我们项目名称的声明也是通过它来完成：\n\n```kotlin\nsettings.rootProject.name = \"untitled\"\n```\n\n我们也可以省略掉settings直接使用其提供的属性：\n\n```kotlin\nrootProject.name = \"untitled\"\n```\n\n其中，Settings对象包含以下常用属性：\n\n| 姓名          | 描述                                           |\n| :------------ | :--------------------------------------------- |\n| `buildCache`  | 项目构建所用缓存配置。                         |\n| `plugins`     | 用于设置的插件。                               |\n| `rootDir`     | 项目构建的根目录，根目录是整个项目目录最外层。 |\n| `rootProject` | 构建的根项目。                                 |\n| `settings`    | 返回设置对象。                                 |\n\nSettings对象也包含以下方法可供调用：\n\n| 姓名             | 描述                                         |\n| :--------------- | :------------------------------------------- |\n| `include()`      | 将指定名称的项目添加到构建列表中。           |\n| `includeBuild()` | 添加指定路径上的其他Gradle项目到构建列表中。 |\n\n我们在编写配置文件时，本质上是对Gradle API的一系列方法调用，结合Groovy或Kotlin的`{...}`语法（在Groovy中称作闭包，Kotlin中称为Lambda）能够轻松编写非常简洁的配置，比如配置插件：\n\n```kotlin\nrootProject.name = \"untitled\"\n\nplugins {  \u002F\u002F使用plugins函数配置插件，结合Lambda表达式可以使用简洁语法完成配置\n    id(\"test\")   \u002F\u002F瞎写的一个\n}\n```\n\n在配置文件中定义的语句，Gradle会一行一行地向下执行，就像一个脚本那样。实际上，我们编写的配置文件在执行Gradle构建时会编译为对应的class文件加载执行，这些文件存放在Gradle的缓存目录中：\n\n![image-20240113153603116](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F13\u002FElVenziDkGgSq91.png)\n\n因此，我们直接在Gradle配置中编写的自定义语句也可以执行：\n\n![image-20240113154214625](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F13\u002FMitGbFHSE4ycqJr.png)\n\n这里我们执行了自定义的打印语句，包括可以通过`rootProject`对象拿到当前的项目文件File对象等。\n\n在Gradle中，和Maven一样也分为插件和依赖，我们可以在`settings.gradle.kt`中可以为所有的项目进行统一配置，比如要修改获取插件的仓库位置：\n\n```kotlin\npluginManagement {   \u002F\u002F使用pluginManagement函数配置插件仓库列表\n    repositories {   \u002F\u002F在repositories函数中配置需要用的仓库\n        gradlePluginPortal()   \u002F\u002FGradle插件仓库\n        google()    \u002F\u002FGoogle插件仓库\n    }\n}\n```\n\n我们也可以修改为国内的阿里云镜像：\n\n```kotlin\npluginManagement {\n    repositories {\n      \t\u002F\u002F手动指定maven仓库地址，修改URL地址\n        maven {\n            setUrl(\"https:\u002F\u002Fmaven.aliyun.com\u002Frepository\u002Fpublic\u002F\")\n        }\n    }\n}\n```\n\n同样的，对于所有的依赖，也可以直接配置为国内的阿里云镜像仓库：\n\n```kotlin\ndependencyResolutionManagement {   \u002F\u002F依赖解析管理中可以配置全局依赖仓库\n    repositories {  \u002F\u002F只不过这种方式目前还在孵化阶段，可能会在未来某个版本移除\n        maven {\n            setUrl(\"https:\u002F\u002Fmaven.aliyun.com\u002Frepository\u002Fpublic\u002F\")\n        }\n    }\n}\n```\n\n不过，除了在`settings.gradle.kts`中配置三方仓库之外，我们更推荐在之后学习的`build.gradle.kts`中对仓库进行配置。\n\n与Maven一样，Gradle插件可以帮助我们在构建过程中实现各种各样的高级功能，它是用于增加和扩展任务（tasks）或构建逻辑的一种特殊类型的模块，在`settings.gradle.kts`中可以配置插件，只不通常被用于需要对多个项目进行操作的插件，或者需要在构建开始之前进行一些配置的情况：\n\n```kotlin\nplugins {   \u002F\u002F使用id明确插件名称，使用version中缀函数明确插件版本\n    id(\"org.gradle.toolchains.fake\") version \"0.6.0\"\n}\n```\n\n一般很少见有项目在这里配置插件。\n\n对于多个子项目的大型项目而言，我们还需要在这里添加所有的子项目：\n\n```kotlin\ninclude(\"app\")\ninclude(\"business-logic\")\ninclude(\"data-model\")\n```\n\n有关多项目的详细介绍，我们会放在下一个章节，本章节主要以单项目为主。\n\n除了以上提到的内容，`Settings`对象上还有更多属性和方法，您可以使用它们来配置构建。不过，虽然许多Gradle脚本通常以简短的Groovy或Kotlin语法编写，但设置脚本中的每个操作都是在`Settings`对象上调用方法：\n\n```kotlin\ninclude(\"app\")\nsettings.include(\"app\")   \u002F\u002F实际上是这样的，只是说settings可以省掉\n```\n\n### 编写构建脚本\n\n前面我们介绍了设置文件的编写，它是我们整个项目的构建起点，包含了所有基本内容的配置。\n\n我们接着来看针对于我们每一个项目的构建脚本`build.gradle.kts`文件：\n\n![image-20240114170828340](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F14\u002FzfNasmdqBk2XVnl.png)\n\n对于设置文件中包含的每个项目，Gradle都会为其创建一个`Project`实例对象，我们可以直接在`build.gradle.kts`中使用，跟之前一样，可以省略：\n\n```kotlin\ngroup = \"cn.itbaima\"\nversion = \"1.0-SNAPSHOT\"\n```\n\n```kotlin\nproject.group = \"cn.itbaima\"   \u002F\u002F本质上就是project的属性\nproject.version = \"1.0-SNAPSHOT\"\n```\n\n在此对象中，包含以下常见属性：\n\n| 姓名           | 类型                | 描述                               |\n| :------------- | :------------------ | :--------------------------------- |\n| `name`         | `String`            | 项目目录的名称。                   |\n| `path`         | `String`            | 该项目的完全限定名称。             |\n| `description`  | `String`            | 该项目的描述。                     |\n| `dependencies` | `DependencyHandler` | 配置项目的依赖列表。               |\n| `repositories` | `RepositoryHandler` | 配置项目的依赖仓库。               |\n| `layout`       | `ProjectLayout`     | 通过此对象来访问项目中的关键位置。 |\n| `group`        | `Object`            | 项目的组。                         |\n| `version`      | `Object`            | 项目的版本。                       |\n\n我们依次来看每个部分是如何进行编写的。\n\n首先是整个项目所采用的插件，我们可以像下面这样编写：\n\n```kotlin\nplugins {\n    id(\"org.jetbrains.kotlin.jvm\") version \"1.9.0\"\n    id(\"application\")\n}\n```\n\n其中，插件`application`是由官方内置的插件，可以直接使用`id()`函数来选择，而上面的`org.jetbrains.kotlin.jvm`插件没有被官方内置，需要我们手动使用`version`中缀函数指定其版本，这样Gradle才能在仓库中正确找到它。\n\n一般情况下，我们普通的Java项目可以直接使用`java`插件，它能够直接完成编译和打包Java代码：\n\n```kotlin\nplugins {\n    id(\"java\")\n}\n```\n\n我们也可以对这个插件进行一些配置，比如我们要生成目标的Java版本等：\n\n```kotlin\nconfigure\u003CJavaPluginExtension> {\n    targetCompatibility = JavaVersion.VERSION_17    \u002F\u002F编译目标版本\n    sourceCompatibility = JavaVersion.VERSION_17    \u002F\u002F源代码版本（会导致只能使用对应版本具有的特性，如果使用更高版本的特性或语法将无法通过编译）\n}\n```\n\n如果我们需要将项目打包为一个可执行的文件，也可以使用`application`插件，它包含`java`插件的全部功能，同样支持编译和打包Java代码，并且支持生成可执行的应用程序：\n\n```kotlin\nplugins {\n    id(\"application\")\n}\n\njava {   \u002F\u002Fconfigure\u003CJavaApplication> 也可以直接写成 java 这个扩展函数，效果一样\n    targetCompatibility = JavaVersion.VERSION_17\n    sourceCompatibility = JavaVersion.VERSION_17\n}\n\napplication {   \u002F\u002F同configure\u003CJavaApplication>\n    mainClass = \"com.test.Main\"  \u002F\u002F配置主类\n}\n```\n\n配置完成后，我们直接执行`build`构建项目，生成以下文件：\n\n![image-20240114174530531](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F14\u002FWFqy8DrtMgYCBHG.png)\n\n可以看到这里生成的压缩包的形式，我们可以直接解压，得到一系列文件：\n\n![image-20240114174651561](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F14\u002FiE23BSKG6HjCR84.png)\n\n可以看到，在bin目录中已经生成了用于运行我们Java项目的脚本（Mac\u002FLinux是项目名称的脚本，Windows是bat脚本）然后在lib目录中是已经帮我们打包好的项目jar文件，如果我们项目中还存在其他的依赖，也会包含在这个lib中。我们可以直接运行：\n\n![image-20240114175030614](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F14\u002FgpjSQ65lakIoXzW.png)\n\n这跟我们前面学习的Maven其实非常相似，同样可以通过插件实现打包，只不过这里是采用编程的形式配置，而Maven统一采用XML形式进行配置。\n\n接着我们来看如何配置项目中的依赖项，首先是对于依赖仓库的选择，默认情况下生成的代码选择的是Maven中心仓库：\n\n```kotlin\nrepositories {\n    mavenCentral()  \u002F\u002FMaven中心仓库\n    google()   \u002F\u002F谷歌仓库\n}\n```\n\n我们可以自己定义，比如只使用本地仓库中的包：\n\n```kotlin\nrepositories {\n    mavenLocal()   \u002F\u002F只使用Maven本地仓库中的软件包\n}\n```\n\n我们可以通过`maven`函数来直接指定第三方仓库：\n\n```kotlin\nrepositories {\n    maven {\n      \t\u002F\u002F修改其url属性来指定一个第三方仓库地址\n        setUrl(\"https:\u002F\u002Fmaven.aliyun.com\u002Frepository\u002Fpublic\u002F\")\n    }\n}\n```\n\n配置好仓库地址后，我们就可以开始添加需要的依赖了，在`dependencies`中进行编写，默认情况下添加了一些测试相关的依赖：\n\n```kotlin\ndependencies {\n    testImplementation(platform(\"org.junit:junit-bom:5.9.1\"))\n    testImplementation(\"org.junit.jupiter:junit-jupiter\")\n}\n```\n\n这里我们可以使用两种不同的函数导入依赖：\n\n* `implementation`导入依赖，用于编译和运行*生产*代码。\n* `testImplementation`导入依赖，用于编译和运行*测试*代码。\n\n其中填写的字符串就是我们依赖的组、名称以及其对应的版本号：\n\n* `org.junit:junit-bom:5.9.1`对应的组为`org.junit`，依赖名称为：`junit-bom`，版本号为：`5.9.1`\n\n我们可以尝试一下添加自己需要的依赖，比如这里我们添加一个Spring框架的核心依赖，首先我们可以在这里找到需要的包：https:\u002F\u002Fcentral.sonatype.com，找到后选择：\n\n![image-20240121154528351](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F21\u002FCHmIGW5cA8h3d6J.png)\n\n然后直接粘贴到依赖列表里面即可：\n\n```kotlin\n\u002F\u002F使用implementation来添加依赖\nimplementation(\"org.springframework:spring-context:6.1.3\")\n```\n\n更新后，就可以直接使用了：\n\n```java\n@Configuration\npublic class Main {\n\n    @Bean\n    public String string() {\n        return \"Hello World!\";\n    }\n\n    public static void main(String[] args) {\n        ApplicationContext context = new AnnotationConfigApplicationContext(Main.class);\n        System.out.println(context.getBean(String.class));\n    }\n}\n```\n\n使用起来跟Maven其实差别不是很大。\n\n当然，除了像这样写在一起之外，我们也可以分开进行编写，把组、名称和版本填写为三个参数：\n\n```kotlin\nimplementation(\"org.springframework\", \"spring-context\", \"6.1.3\")\n```\n\n我们有些时候希望一直使用最新版本的依赖，也可以直接将版本设置为`+`来始终使用最新版本：\n\n```kotlin\nimplementation(\"org.springframework:spring-context:+\")  \n\u002F\u002F生产环境下不推荐这样写，万一新版改了啥东西导致项目出大问题就得不偿失了\n```\n\n下一节我们将继续深入依赖的相关配置，了解更多配置细节。\n\n### 深入依赖配置\n\n上一部分我们介绍了如何导入依赖，我们接着来看导入依赖之后的更多操作。\n\n除了通过直接编写Maven坐标的形式来引入仓库中的依赖之外，我们也可以直接将一个本地的Jar包引入到项目中，这里我们在项目根目录下新建一个`lib`目录，用于存放我们需要引用的Jar包：\n\n![image-20240121160637134](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F21\u002F96ZuXSbYnrdm5zj.png)\n\n接着我们在依赖中编写引入的语句：\n\n```kotlin\ndependencies {\n  \t\u002F\u002F使用files方法来指定一个文件进行导入\n    implementation(files(\"lib\u002Fspring-context-6.1.3.jar\", \n        \"lib\u002Fspring-core-6.1.3.jar\", \"lib\u002Fspring-beans-6.1.3.jar\", \"lib\u002Fspring-jcl-6.1.3.jar\"))\n    ...\n}\n```\n\n```java\npublic static void main(String[] args) {\n    ApplicationContext context = new FileSystemXmlApplicationContext();\n}\n```\n\n如果我们一个目录下的jar包太多，需要全部导入，像上面这样一个一个写太累了，我们可以直接使用fileTree方法来统一获取：\n\n```kotlin\nimplementation(fileTree(\"lib\"))  \u002F\u002F直接引入lib下全部jar包\n```\n\n效果和上面一个一个导入一样。\n\n我们接着来看依赖的排除，在之前Maven的学习中我们知道，一个依赖的内部可能又会存在多个依赖，在使用一些依赖时，我们可能希望排除其中某些不需要的依赖或是与其他依赖冲突的依赖，我们可以对依赖进行排除操作：\n\n```kotlin\nimplementation(\"org.springframework:spring-context:6.1.3\") {\n  \t\u002F\u002F在Lambda中使用exclude来排除不需要的依赖\n    exclude(\"org.springframework\", \"spring-aop\")\n}\n```\n\n这里我们在引入`spring-context`之后，不需要其中的AOP模块，因此我们可以直接使用exclude函数将其排除，此时AOP模块就不存在于外部库中了：\n\n![image-20240121163348285](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F21\u002F3I2w1GjY6AzRfZq.png)\n\n我们再来看下面这种情况：\n\n```kotlin\nimplementation(\"org.springframework:spring-context:6.1.3\")\nimplementation(\"org.springframework:spring-aop:6.1.1\")\n```\n\n此时我们的项目中不仅依靠`spring-context`引入了`spring-aop`的6.1.3版本，也手动引入了`spring-aop`的6.1.1版本，此时Gradle会优先使用更新的版本作为依赖，所以这里实际引入使用的也是6.1.3版本。\n\n如果我们执意要使用旧版本的依赖，可以通过上面的方式进行依赖的排除，或是给版本号添加感叹号表示强制使用：\n\n```kotlin\nimplementation(\"org.springframework:spring-context:6.1.3\") {\n    exclude(\"org.springframework\", \"spring-aop\")  \u002F\u002F直接排除掉新版本的\n}\nimplementation(\"org.springframework:spring-aop:6.1.1\")\n```\n\n```kotlin\nimplementation(\"org.springframework:spring-context:6.1.3\") \nimplementation(\"org.springframework:spring-aop:6.1.1!!\")   \u002F\u002F添加双感叹号\n```\n\n最后我们来列举一下DependencyHandlerScope里面包含的方法：\n\n1. `implementation`: 用于添加项目的依赖项，这是最常用的方法。\n2. `api`: 与 `implementation` 类似，但它会暴露依赖项给项目的所有模块（多项目配置中讲解）\n3. `compileOnly`: 用于指定编译时依赖，但不会在运行时包含在最终构建结果中。\n4. `testImplementation`: 用于添加测试时需要的依赖项。\n5. `androidTestImplementation`: 用于添加Android测试时需要的依赖项。\n6. `kapt`: 用于添加 Kotlin 注解处理器依赖项。\n7. `annotationProcessor`: 用于添加 Java 注解处理器依赖项。\n8. `runtimeOnly`：仅在运行时使用，不用于编译\n\n相信各位小伙伴对于`implementation`已经非常熟悉了，它其实是Java插件提供的方法，我们可以使用它来引入依赖，并且在打包之后也会附带我们引入的依赖：\n\n![image-20240121162231958](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F21\u002FMolKJPq5NpXtwEb.png)\n\n我们来看看其他的导入方式，首先是`compileOnly`，它表示导入的依赖仅在编译时可用，编译完成后不会将依赖一起打包：\n\n```kotlin\ncompileOnly(\"org.springframework:spring-context:6.1.3\")\n```\n\n![image-20240121162312826](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F21\u002FuL6r5GdJIvlX3zf.png)\n\n接着是`runtimeOnly`，它表示导入的依赖仅在运行时可用，比如MySQL驱动这类我们不需要在项目中直接使用，但是在项目运行时又需要用到的依赖就非常适合：\n\n```kotlin\nruntimeOnly(\"org.springframework:spring-context:6.1.3\")\n```\n\n此时，由于此依赖仅存在于运行时，项目中是无法使用的：\n\n![image-20240121162657738](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F21\u002FdfIjNiM2GugL6DK.png)\n\n但是我们去掉这些不可用的代码，在编译之后，这些依赖却会被打包在一起。\n\n### 自定义任务\n\nGradle可以在项目上完成的工作由一个或多个*任务*定义，任务代表构建执行的某些独立工作单元，比如编译一些类，创建jar包，生成Javadoc文档，或将一些内容发布到代码仓库，这些任务通常由插件提供，我们在项目中引入插件后就可以直接执行对应的任务了，在不引入任何插件的情况下，只会包含一些内置的默认任务：\n\n![image-20240118233040479](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F18\u002FhxGTnRjUabF3JDi.png)\n\n在引入`java`插件后，就出现了各种Java相关的任务，比如编译、构建、打包jar包等：\n\n![image-20240118233136583](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F18\u002FAcZ1duvwQNxKXCB.png)\n\n可以看到，这些任务都是由插件为我们提供的，因此，大部分情况下我们引入插件之后就可以直接执行相关的任务了。我们在执行某一个大任务的时候，就会执行一系列的任务，比如`build`命令，它不仅完成构建，还将所有的任务依赖关系进行完整的建立，可以看到：\n\n![image-20240118235556821](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F18\u002FZvUuRSwNWK458do.png)\n\n在整个build任务执行的过程中，依次按顺序执行了各种各样的任务：\n\n1. compileJava：编译所有Java源代码文件。\n2. processResources：处理所有资源文件，由于这个项目没有资源文件，提示 NO-SOURCE\n3. ...\n\n通过这一系列小任务，就完成了编译、构建、打包等一系列操作，只需要执行：`.\u002Fgradlew build` 即可。\n\n当然，如果各位小伙伴觉得插件提供的任务不太够，需要自定义添加，我们也可以在`build.gradle.kts`中编写自定义任务：\n\n![image-20240118232635997](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F18\u002FF7SVxvz5gKX3qDa.png)\n\n注册任务需要使用`register`或`create`函数来完成，一个最简单的任务可以像下面这样编写：\n\n```kotlin\ntasks.register(\"hello\") {   \u002F\u002F第一个参数为任务名称，第二个参数使用Lambda编写任务具体操作\n  \t\u002F\u002F任务包含一个完整的操作列表，我们需要传入对应的Action到队列中，这样就可以依次执行了\n    doFirst {   \u002F\u002F使用doFirst向任务队列首部插入新的Action，也就是要执行的内容\n        println(\"我是自定义的任务开始\")\n    }\n    doLast {   \u002F\u002F向队列尾部插入Action\n        println(\"我是自定义的任务结束\")\n    }\n}\n```\n\n可以看到，刷新之后任务列表就出现了我们自定义的任务，执行之后也是我们自定义的内容：\n\n![image-20240119001057313](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F19\u002FW37YmcbaJ4nz5ZD.png)\n\n我们也可以通过指令的形式直接运行：\n\n![image-20240121165447827](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F21\u002F7Tn5eEvN9GsJOix.png)\n\n在执行命令时，我们还可以添加额外的项目参数，在脚本中可以直接获取：\n\n```\ntasks.register(\"hello\") {\n    println(\"获取到自定义参数: ${project.properties[\"test\"]}\")\n    ...\n}\n```\n\n![image-20240121170219765](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F21\u002F9VRpe8tryTZgYP6.png)\n\n我们还可以配置此任务所属的组别，以及其它描述信息：\n\n![image-20240119001316269](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F19\u002FRstLdo9h4De58Mx.png)\n\n我们还可以将一个任务作为`gradle`执行的默认任务，也就是说直接执行gradle命令就可以运行我们的任务了：\n\n```kotlin\ndefaultTasks(\"hello\")\n```\n\n![image-20240121170332940](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F21\u002FpXudMCE3bhD7oZJ.png)\n\n一个任务还可以依赖于其他任务，比如我们的自定义任务在执行之前需要先完成源代码的编译操作：\n\n```kotlin\ntasks.register(\"hello\") {\n    group = \"build\"\n    description = \"这是一个非常伟大的任务！\"\n    dependsOn(tasks.compileJava)   \u002F\u002F使用dependsOn函数来指定前置任务，可以是其他插件提供的，也可以是我们自己定义的，这个参数也可以传字符串\n    ...\n}\n```\n\n在执行时，会先完成前置任务之后再执行当前任务：\n\n![image-20240119001459192](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F19\u002Fe8powMsRGUlN37T.png)\n\n我们也可以让已经存在的任务来依赖我们的任务或是直接为其添加额外的操作：\n\n```kotlin\ntasks.named(\"build\") {   \u002F\u002F根据名称进行查找\n    dependsOn(\"hello\")   \u002F\u002F直接配置依赖\n  \tdoLast { ... }   \u002F\u002F添加新的Action到列表中\n}\n\ntasks.build {   \u002F\u002F直接从tasks中获取，这仅限于插件提供的任务\n    dependsOn(\"hello\")\n}\n```\n\n这样，我们就为任务`build`添加了前置任务到前置任务列表中：\n\n![image-20240119014237759](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F19\u002FafqnYOIzsVTKljA.png)\n\n在Gradle中，实际上所有的任务都是Task的子类，除了向上面这样直接编写Task类型，包括我们在前面使用的`register`方法，默认也是在Lambda中为我们提供一个Task类型的对象：\n\n```java\n@Override\nTaskProvider\u003CTask> register(String name, Action\u003C? super Task> configurationAction) throws InvalidUserDataException;\n\u002F\u002F这里Action的默认提供类型就是Task\n```\n\n我们也可以自行创建Task的子类，来编写自定义的任务类型：\n\n```kotlin\n\u002F\u002F 继承 DefaultTask 类来创建一个自定义的 HelloTask 类，注意这个类必须要可继承，要么open要么直接抽象类\nopen class HelloTask : DefaultTask() {\n    private var name: String = \"\"\n\n    @TaskAction   \u002F\u002F添加@TaskAction注解来声明此函数为任务的Action\n    fun hello() {\n        println(\"${name}: 卡布奇诺今犹在，不见当年倒茶人\")\n    }\n\n    fun user(name: String) {   \u002F\u002F类中也可以具有其他的函数\n        this.name = name\n    }\n}\n\ntasks.register\u003CHelloTask>(\"hello\") {   \u002F\u002F使用register时指明我们自定义的任务类\n    user(\"卢本伟\")   \u002F\u002F此时this就是HelloTask类型了，我们可以直接使用自定义的函数\n}\n```\n\n通过这种方式也可以实现任务的自定义：\n\n![image-20240119153020247](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F19\u002FZDTKkH2pEji91xt.png)\n\n除了通过插件或是我们自定义的形式编写任务之外，Gradle也为我们提供了一些内置的任务类型，这些任务通常是一些经常会用到的操作，我们可以来使用一下，比如复制任务：\n\n```kotlin\ntasks.register\u003CCopy>(\"hello\") {   \u002F\u002F这里使用Copy类型\n    from(\"build\u002Fclasses\")   \u002F\u002F使用from和into设置复制的目录和目标位置\n    into(\"test\")\n  \tdependsOn(tasks.build)   \u002F\u002F依赖一下build\n}\n```\n\n执行我们自定义的hello任务，就可以完成构建 + 拷贝了：\n\n![image-20240119154344518](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F19\u002FD7Yzq1LygRK6iE5.png)\n\n除了这里演示才Copy操作，开发人员还可以利用许多任务类型，包括`GroovyDoc`、`Zip`、`Jar`、`JacocoReport`、`Sign`或`Delete`等。\n\n### 生命周期钩子\n\n有些时候我们希望在Gradle的整个生命周期中的不同时段执行一些操作，我们可以使用官方提供的生命周期钩子函数。\n\n1. 构建初始阶段\n   * `gradle.settingsEvaluated()` 完成项目的配置阶段之后调用（只能定义在 setting.gradle 或 init.gradle 脚本中）\n   * `gradle.projectsLoaded()` 所有项目加载之后调用（只能定义在 setting.gradle 或 init.gradle 脚本中）\n2. 配置阶段\n   * `gradle.beforeProject()` 每个项目完成配置之前调用（只能定义在 setting.gradle 或 init.gradle 脚本中）\n   * `gradle.afterProject()` 每个项目完成配置之后调用\n   * `gradle.projectEvaluated()` 所有项目全部完成配置之后调用\n   * `gradle.afterEvaluate()`  整个配置阶段完成后调用\n   * `gradle.taskGraph.whenReady` 全部任务图已经构建完成可以就绪后调用\n3. 执行阶段\n   * `gradle.taskGraph.beforeTask` 执行每一个任务之前调用\n   * `gradle.taskGraph.afterTask` 每一个任务执行完成之后调用\n   * `gradle.buildFinished` 整个构建全部结束后调用\n\n这里我们可以尝试编写一个钩子函数试试看：\n\n```kotlin\ngradle.settingsEvaluated {\n    println(\"开始构建\")\n}\n\ngradle.buildFinished {\n    println(\"构建结束\")\n}\n```\n\n![image-20240121004051994](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F21\u002F2COqLUBVWdculsM.png)\n\n这样，我们就可以在不同的阶段执行自定义的内容了。比如，我们可以利用这种特性来统计某一个阶段或是任务耗费的时间：\n\n```kotlin\nvar time: Long = 0\ngradle.taskGraph.beforeTask {\n    time = System.currentTimeMillis()\n}\n\ngradle.taskGraph.afterTask {\n    val takeTime = System.currentTimeMillis() - time\n    println(\"任务: $name 执行耗时: ${takeTime}ms\")\n}\n```\n\n得到的结果就会自动计算每个任务的执行时间了：\n\n![image-20240121004524174](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F21\u002FmVXrN6IqAujdM2p.png)\n\n### 项目发布\n\n我们也可以将自己的的Gradle项目发布到的Maven仓库中，这里我们以发布到本地仓库为例：\n\n```kotlin\nplugins {\n    id(\"java\")\n    id(\"maven-publish\")   \u002F\u002F首先引入maven-publish插件\n}\n```\n\n接着我们来配置发布相关的内容：\n\n```kotlin\npublishing {\n    publications {\n        create\u003CMavenPublication>(\"library\") {   \u002F\u002F发布的相关信息Maven坐标信息\n            groupId = \"cn.itbaima\"\n            artifactId = \"hello\"\n            version = \"0.0.1\"\n            from(components[\"java\"])   \u002F\u002F发布为jar包形式\n        }\n    }\n\n    repositories {\n        mavenLocal()    \u002F\u002F指定为本地Maven仓库\n    }\n}\n```\n\n接着我们执行`publish`任务即可发布项目到本地仓库了：\n\n![image-20240121180552555](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F21\u002FN2aj7PTlDnzWZJA.png)\n\n此时在`.m2`目录中已经存在我们发布的项目了：\n\n![image-20240121180626915](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F21\u002F3MUVFyzL5BasNGS.png)\n\n### 创建SpringBoot项目\n\n通过IDEA或是前往SpringBoot官网就可以自动为我们创建一个基于Gradle的SpringBoot项目：https:\u002F\u002Fstart.spring.io\u002F\n\n![image-20240119174454822](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F19\u002FPKxFYpGQT4m6fXH.png)\n\n创建完成后，使用IDEA打开会自动下载GradleWrapper指定的版本，并同时下载相关的插件：\n\n![image-20240119174822817](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F19\u002FwJNCxUs9iYDZkR3.png)\n\n这里自动生成了项目的`build.gradle.kts`文件：\n\n```kotlin\nplugins {   \u002F\u002F首先是插件配置\n    java  \u002F\u002F最基础的Java插件\n    id(\"org.springframework.boot\") version \"3.2.1\"   \u002F\u002FSpringBoot插件\n    id(\"io.spring.dependency-management\") version \"1.1.4\"   \u002F\u002FSpring依赖管理插件\n}\n\ngroup = \"cn.itbaima\"   \u002F\u002F不用说了吧\nversion = \"0.0.1-SNAPSHOT\"\n\njava {\n    sourceCompatibility = JavaVersion.VERSION_17   \u002F\u002F配置Java源代码编译版本\n}\n\nconfigurations {\n    compileOnly {   \u002F\u002F注解处理相关配置，用于Lombok\n        extendsFrom(configurations.annotationProcessor.get())\n    }\n}\n\nrepositories {   \u002F\u002F使用Maven中心仓库\n    mavenCentral()\n}\n\ndependencies {   \u002F\u002F包含SpringBoot相关依赖，以及Lombok依赖\n    implementation(\"org.springframework.boot:spring-boot-starter-web\")\n    compileOnly(\"org.projectlombok:lombok\")\n    annotationProcessor(\"org.projectlombok:lombok\")\n    testImplementation(\"org.springframework.boot:spring-boot-starter-test\")\n}\n\ntasks.withType\u003CTest> {\n    useJUnitPlatform()\n}\n```\n\n相信各位小伙伴现在应该能够看懂SpringBoot项目为我们自动生成的Gradle构建文件了。\n\n### JVM语言混合编程\n\n对于某些需要使用多种JVM语言进行编程的项目，我们可以同时配置多种插件，这里我们以Java与Kotlin混合编程为例进行讲解：\n\n```kotlin\nplugins {   \u002F\u002F同时配置Java和Kotlin\u002FJVM插件\n    id(\"java\")\n    kotlin(\"jvm\") version \"1.9.21\"\n}\n```\n\n编写好插件后，我们直接刷新项目，接着就可以在src下的main和test目录中同时创建两个源代码目录：\n\n* java：存放Java源代码文件\n* kotlin：存放Kotlin源代码文件\n\n![image-20240120161736076](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F20\u002FW1f5TGlZtr8NqVo.png)\n\n这里我们的项目已经是一个Java项目了，我们可以直接右键`main`点击新建目录，然后直接选择Gradle集源中的kotlin目录：\n\n![image-20240120161857931](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F20\u002FBbM2taVeJOmLGQg.png)\n\n此时就可以在kotlin目录下编写Kotlin相关的源代码了：\n\n```kotlin\n\u002F\u002F位置：src\u002Fmain\u002Fkotlin\u002Fcom\u002Ftest\u002FStudent.kt\n\npackage com.test\n\ndata class Student(var name: String, var age: Int)\n```\n\n```java\n\u002F\u002F位置：src\u002Fmain\u002Fjava\u002Fcom\u002Ftest\u002FMain.java\npackage com.test;\n\npublic class Main {\n    public static void main(String[] args) {\n        Student student = new Student(\"小明\", 18);\n        System.out.println(student);\n    }\n}\n```\n\n编写好之后，点击main方法旁边的运行按钮，可以看到一起编译了Java和Kotlin源代码，并成功运行：\n\n![image-20240120162225738](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F20\u002FbzfDxeAYk7PytBj.png)\n\n如果要打包为可执行的应用，也可以直接使用`application`插件：\n\n```kotlin\nplugins {\n    id(\"application\")\n    kotlin(\"jvm\") version \"1.9.21\"\n}\n```\n\n打包之后，已经自动将Kotlin标准库依赖集成了，也可以直接使用：\n\n![image-20240120163429747](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F20\u002F87L4I1nRU3pla5z.png)\n\n![image-20240120163548383](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F20\u002F6fZdaNr25xhkg4m.png)\n\n这样，我们就可以愉快地进行混合编程了。\n\n## 多项目配置\n\n前面我们介绍的是单个项目的布局，现在我们接着来看多个项目的布局。\n\n通常对于一些大型的分布式项目来说，我们会在一个项目中包含多个模块，不同的模块负责不同的功能，并且不同模块的代码独立进行编写，这时整个Gradle项目就会存在多个子项目：\n\n```kotlin\nlarge-project\n|\n├── auth-service\n│   ...\n│   └── build.gradle.kts\n├── chat-service\n│   ...\n│   └── build.gradle.kts\n└── settings.gradle.kts\n```\n\n可以看到，在根项目下，只存在`settings.gradle.kts`用于全局配置，而具体的`build.gradle.kts`构建文件存在于各个子项目中分别进行编写。\n\n我们也可以按照这样的结构去搭建我们的多模块项目。\n\n### 项目创建\n\n要创建一个多模块项目，我们可以先使用IDEA创建一个简单的Gradle项目出来：\n\n![image-20240120000346974](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F20\u002F5Bphd6IJckCnUH8.png)\n\n接着我们删除不需要的内容，比如：`src`目录、`build.gradle.kts`文件等，接着右键最外层项目，创建一个新的子项目，此时我们需要选择最外层项目为我们的父项目：\n\n![image-20240120000729724](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F20\u002FypvePzdxBmhfj7O.png)\n\n创建完成后，我们可以打开最外层的`settings.gradle.kts`文件，IDEA已经自动为我们添加了模块引入：\n\n```kotlin\nrootProject.name = \"large-project\"\ninclude(\"auth-service\")\n```\n\n此时，父子项目的层级已经明确划分出来了：\n\n![image-20240120001026521](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F20\u002F7CKlBW1su82yweS.png)\n\n可以看到，`build.gradle.kts`现在由子模块进行配置，每个子模块都可以单独处理。\n\n在存在多个子项目的时候，我们可以直接在根项目中执行指令，会生效于全部的子项目：\n\n![image-20240120001948127](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F20\u002F5DXFnTm3UOfdpQz.png)\n\n可以看到，这里的项目名称前面都加了一个`:`符号。\n\n我们也可以配置项目之间的依赖，我们只需在`dependencies`中进行配置：\n\n```kotlin\ndependencies {\n    implementation(project(\":common\"))   \u002F\u002F使用project方法来引用其他项目作为依赖，项目名称前需要添加单引号\n    testImplementation(platform(\"org.junit:junit-bom:5.9.1\"))\n    testImplementation(\"org.junit.jupiter:junit-jupiter\")\n}\n```\n\n有些时候，我们可能需要将一些部分统一进行配置，比如配置代码仓库地址，以及项目用到的插件，我们可以直接在根项目创建一个`build.gradle.kts`来统一编写内容：\n\n```kotlin\nsubprojects {   \u002F\u002Fsubprojects表示对所有的子项目生效\n    apply(plugin = \"java\")   \u002F\u002F定义插件需要使用apply来完成，plugin{}在这里不行\n\n    group = \"cn.itbaima\"   \u002F\u002F定义组\n    version = \"unspecified\"  \u002F\u002F定义版本\n\n    repositories {   \u002F\u002F定义自定义仓库地址\n        maven {\n            setUrl(\"https:\u002F\u002Fmaven.aliyun.com\u002Frepository\u002Fpublic\u002F\")\n        }\n    }\n}\n```\n\n这样，我们即使不在子项目中编写这些，就可以直接得到根项目的配置：\n\n```kotlin\ndependencies {\n    testImplementation(platform(\"org.junit:junit-bom:5.9.1\"))\n    testImplementation(\"org.junit.jupiter:junit-jupiter\")\n}\n\ntasks.test {\n    useJUnitPlatform()\n}\n```\n\n### 依赖的传递\n\n我们在使用多模块时可能会遇到这样的一个问题，现在有三个模块，并且具有以下依赖关联：\n\n* service-a\n* service-b implementation(service-a)\n* service-c implementation(service-b)\n\n此时，从链式的依赖关系上来看，它们长这样：`service-a -> service-b -> service-c `\n\n按照正常的传递关系来说，在B中应该是可以直接使用A中定义的内容的，因为依赖了A模块，同时，由于C依赖了B模块，那么理所应当，C也应该可以直接使用A中定义的内容，我们来看看是否真的如此：\n\n```kotlin\n\u002F\u002FA模块中定义\npublic class Test {\n    public static void test() {\n        System.out.println(\"Hello World!\");\n    }\n}\n```\n\n```java\n\u002F\u002FB模块中定义\npublic class Main {\n    public static void main(String[] args) {\n        Test.test();\n    }\n}\n```\n\n```java\n\u002F\u002FC模块中定义\npublic class Main {\n    public static void main(String[] args) {\n        Test.test();   \u002F\u002F找不到类Test，编译失败\n    }\n}\n```\n\n这就很奇怪了，在Maven中是完全可以实现传递依赖的，为什么到Gradle就不行了呢？这是因为`implementation`不支持依赖传递，因此，即使我们改变了B模块的依赖，此时C也无法通过传递的形式得到B包含的依赖，由于不需要处理传递依赖，在编译时只需要处理一层，因此速度会非常快，大部分情况下非常推荐使用`implementation`来进行依赖导入。\n\n如果各位小伙伴需要实现传递依赖的效果，我们需要使用另一个插件提供的方法来导入依赖：\n\n```kotlin\nplugins {\n    `java-library`   \u002F\u002Fjava-library提供了传递依赖api函数\n}\n\ndependencies {\n    api(project(\":common\"))   \u002F\u002F与implementation效果一样，但是支持传递依赖\n    testImplementation(platform(\"org.junit:junit-bom:5.9.1\"))\n    testImplementation(\"org.junit.jupiter:junit-jupiter\")\n}\n```\n\n此时，模块B的依赖由于使用了api进行导入，我们在模块C中无论是使用`api`导入还是`implementation`导入B，都可以使用B中api函数导入的依赖了。\n\n### 使用buildSrc模块\n\n在Gradle中有一个特殊机制，我们可以创建一个名为`buildSrc`的模块，它支持我们自己定义能够在`kts`脚本中使用的内容。\n\n这里我们直接创建这样的一个项目：\n\n![image-20240120233733441](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F20\u002FUjZmwOTlzKbcWVh.png)\n\n注意在创建之后我们需要移除`settings.gradle.kts`中的包含关系：\n\n```kotlin\nrootProject.name = \"large-project\"\ninclude(\"auth-service\", \"chat-service\", \"common\")\ninclude(\"buildSrc\")   \u002F\u002F不能作为子项目存在\n```\n\n接着我们修改一下`build.gradle.kts`文件：\n\n```kotlin\nplugins {\n    `kotlin-dsl`\n}\n\nrepositories {\n    mavenCentral()\n}\n```\n\n此时我们就可以编写我们需要使用的部分了，比如我们希望吧阿里云Maven仓库封装成一个函数的形式，直接使用，就像`mavenCentral()`那样，不然每次都要写一个完整地址，很麻烦，这里我们直接开一个新的kt文件编写扩展函数：\n\n```kotlin\nimport org.gradle.api.artifacts.dsl.RepositoryHandler\n\n\u002F\u002F由于repositories函数中提供的this为RepositoryHandler类型的对象\n\u002F\u002F这里我们直接为其编写扩展函数\nfun RepositoryHandler.mavenAlibaba() = maven {\n    setUrl(\"https:\u002F\u002Fmaven.aliyun.com\u002Frepository\u002Fpublic\u002F\")\n}\n```\n\n接着，我们就可以在其他的kts脚本中使用我们定义的内容了，比如根项目的配置：\n\n```kotlin\nsubprojects {\n    apply(plugin = \"java\")\n\n    group = \"cn.itbaima\"\n    version = \"unspecified\"\n\n    repositories {\n        mavenAlibaba()   \u002F\u002F直接使用自定义的\n    }\n}\n```\n\n是不是感觉很方便？我们也可以使用这种方式定义所有需要使用的依赖版本，实现统一管理，比如编译版本的管理：\n\n```kotlin\nimport org.gradle.api.JavaVersion\n\nobject Version {\n    val sourceVersion = JavaVersion.VERSION_17\n    val targetVersion = JavaVersion.VERSION_17\n}\n```\n\n```kotlin\nsubprojects {\n    ...\n\n    configure\u003CJavaPluginExtension> {\n        targetCompatibility = Version.targetVersion   \u002F\u002F直接使用Version类获取版本\n        sourceCompatibility = Version.sourceVersion\n    }\n}\n```\n\n包括如果我们需要使用一些第三方的依赖，也可以统一管理版本：\n\n```kotlin\nobject Version {\n    ...\n    val springVersion = \"6.1.3\"   \u002F\u002F统一Spring相关依赖的版本\n}\n```\n\n```kotlin\ndependencies {\n    implementation(\"org.springframework:spring-beans:${Version.springVersion}\")   \u002F\u002F直接从Version中获取版本\n    implementation(\"org.springframework:spring-aop:${Version.springVersion}\")\n    implementation(\"org.springframework:spring-web:${Version.springVersion}\")\n    testImplementation(platform(\"org.junit:junit-bom:5.9.1\"))\n    testImplementation(\"org.junit.jupiter:junit-jupiter\")\n}\n```\n\n除了以上用法之外，我们也可以在buildSrc模块中编写自定义的插件并使用，这里我们创建一个新的插件类：\n\n```kotlin\npackage com.plugin\n\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\n\nclass MyPlugin: Plugin\u003CProject> {\n    override fun apply(target: Project) {   \u002F\u002F插件应用时会直接调用apply函数\n        println(\"插件生效了\")\n    }\n}\n```\n\n接着我们需要在buildSrc的构建脚本中声明此插件：\n\n```kotlin\ngradlePlugin {\n    plugins {\n        create(\"my-custom-plugin\") {   \u002F\u002F创建一个新的Plugin\n            id = \"my-plugin\"  \u002F\u002F插件的ID\n            implementationClass = \"com.plugin.MyPlugin\"   \u002F\u002F插件的实现类\n        }\n    }\n}\n```\n\n声明之后，我们就可以在项目中使用了：\n\n```kotlin\nplugins {\n  \t...\n    id(\"my-plugin\")   \u002F\u002F直接通过id使用插件\n}\n```\n\n![image-20240121175021896](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F21\u002FgSLU2usFqe7QnAd.png)\n\n利用插件，我们可以快速为项目添加各种任务，这里我们魔改一下上面写的插件：\n\n```kotlin\nclass MyPlugin: Plugin\u003CProject> {\n    override fun apply(target: Project) {  \u002F\u002F直接在apply中注册新的任务，这样插件加载之后就具有这些任务了\n        target.tasks.register(\"a\") {\n            doLast { println(\"你干嘛\") }\n        }\n\n        target.tasks.register(\"b\") {\n            doLast { println(\"哎哟\") }\n            dependsOn(target.tasks.named(\"a\"))\n        }\n    }\n}\n```\n\n此时项目中已经存在我们自定义的两个任务了：\n\n![image-20240121175341033](https:\u002F\u002Fs2.loli.net\u002F2024\u002F01\u002F21\u002FAopXNIChP8HKykf.png)\n\n相信大家也应该明白为什么官方插件导入之后会多出来这么多任务了。\n","Gradle构建工具是一个快速、可靠和适应性强的开源构建自动化工具，具有优雅和可扩展的声明性构建语言","2025-07-04 23:44:57",{"data":468,"status":460,"success":461},[469,474],{"id":8,"image":470,"link":471,"name":472,"type":473},"\u002Fimage\u002Fadv\u002Frainyun-2025-06.webp","https:\u002F\u002Fwww.rainyun.com\u002Fitbaima_","雨云优惠购","cloud",{"id":66,"image":475,"link":476,"name":477,"type":478},"\u002Fimage\u002Fadv\u002Fsimcard-2025-11.webp","https:\u002F\u002Fmall.itbaima.cn","号卡优惠","simcard"]