境配置到IoC與DI實(shí)戰(zhàn))
剛?cè)胄心顷囎游一瞬簧贂r(shí)間折騰Spring。那時(shí)候網(wǎng)上資料遠(yuǎn)不如現(xiàn)在豐富大部分教程都是基于IDEA的用Eclipse的教程要么太老要么講得云里霧里。做Java開(kāi)發(fā)這些年從SSH到SSM再到Spring BootSpring這個(gè)框架始終是繞不開(kāi)的核心。如果你還在用Eclipse或者公司項(xiàng)目強(qiáng)制要求Eclipse這篇文章就是為你準(zhǔn)備的——我會(huì)帶你從零開(kāi)始在Eclipse里裝好Spring、跑通第一個(gè)程序順便把IoC、DI這些核心概念講清楚最后再附上我這些年積累的排錯(cuò)經(jīng)驗(yàn)幫你少走彎路。很多新手包括當(dāng)年的我最容易犯的錯(cuò)誤是一上來(lái)就直奔框架的高級(jí)特性結(jié)果連環(huán)境都沒(méi)搭明白。Spring說(shuō)到底是一個(gè)輕量級(jí)的容器框架它的核心思想就兩個(gè)控制反轉(zhuǎn)IoC和面向切面編程AOP。在Eclipse里把Spring跑起來(lái)其實(shí)只需要三個(gè)東西JDK環(huán)境、Eclipse IDE、Spring相關(guān)的jar包。但恰恰是這三個(gè)東西的版本搭配就能勸退一大批人。這篇文章適合Eclipse使用者、Spring初學(xué)者也適合那些想在Eclipse里快速搭建Spring項(xiàng)目但總被各種報(bào)錯(cuò)卡住的人??赐昴銜?huì)對(duì)整個(gè)流程有個(gè)清晰的認(rèn)知每一步怎么做、為什么這么做我都會(huì)掰開(kāi)揉碎講清楚。1. 準(zhǔn)備工作工具鏈的選擇比想象中重要1.1 JDK、Eclipse和Spring的版本怎么搭先說(shuō)JDK。這里我強(qiáng)烈建議新項(xiàng)目直接用JDK 8或者JDK 11不要一上來(lái)就追最新的JDK 17甚至更高。原因很簡(jiǎn)單Spring Framework 5.x對(duì)JDK 8的支持非常成熟穩(wěn)定你在網(wǎng)上搜到的大部分教程、遇到的報(bào)錯(cuò)解決方案基本都基于這個(gè)組合。如果你用的是Spring 6.x那需要JDK 17以上但Spring 6帶來(lái)的變化對(duì)新手來(lái)說(shuō)意義不大反而會(huì)增加配置難度。Eclipse版本方面建議選擇Eclipse IDE for Enterprise Java and Web Developers也就是我們常說(shuō)的Java EE版本。這個(gè)版本內(nèi)置了Web開(kāi)發(fā)相關(guān)的插件后面如果你要從Spring Core升級(jí)到Spring MVC會(huì)省掉很多麻煩。Eclipse 2020-06到2022-09之間的版本我都用過(guò)說(shuō)實(shí)話差別不大選一個(gè)你順手的即可。但有一點(diǎn)要特別注意Eclipse和JDK的位數(shù)必須一致都是64位或者都是32位否則可能出現(xiàn)一些莫名其妙的問(wèn)題。Spring的版本選擇有一個(gè)小經(jīng)驗(yàn)不要使用最新的release版本尤其是剛發(fā)布的那種。Spring Framework的版本迭代非常穩(wěn)定沒(méi)必要追求新5.2.x或者5.3.x系列都很好用網(wǎng)上資料也最多。記得有一次我手賤用了當(dāng)時(shí)剛出的5.3.0結(jié)果一個(gè)和CGLIB相關(guān)的兼容性問(wèn)題折騰了整整一個(gè)下午最后老老實(shí)實(shí)退回5.2.9世界安靜了。1.2 提前裝好Maven省掉手動(dòng)找jar包的麻煩以前我們學(xué)Spring的時(shí)候第一步是上官網(wǎng)下載jar包然后用Eclipse手動(dòng)Add External Jars一個(gè)個(gè)往上加?,F(xiàn)在有Maven或Gradle這個(gè)好用的工具千萬(wàn)別再手動(dòng)管理jar包了。Maven能自動(dòng)幫你下載Spring及所有依賴的jar包什么spring-core、spring-beans、spring-context、spring-aop它全都會(huì)處理好。如果你之前沒(méi)裝過(guò)Maven可以去Apache官網(wǎng)下載一個(gè)解壓版然后配一下環(huán)境變量新建MAVEN_HOME指向Maven目錄再把%MAVEN_HOME%\bin加到Path里。配好后在命令行敲一下mvn -v能正常輸出版本號(hào)就算成功了。Eclipse里集成Maven很簡(jiǎn)單Eclipse較新版本已經(jīng)內(nèi)置了Maven插件m2e不需要額外安裝。我們只需要在Eclipse里關(guān)聯(lián)好自己下載的Maven打開(kāi)Window - Preferences - Maven - Installation點(diǎn)Add指定你下載的Maven目錄即可。再設(shè)置一下Settings指向你本地Maven的settings.xml。這里有個(gè)國(guó)內(nèi)開(kāi)發(fā)者的痛點(diǎn)Maven默認(rèn)從中央倉(cāng)庫(kù)下載jar包速度像老牛拉破車。強(qiáng)烈建議修改settings.xml添加阿里云鏡像這是開(kāi)發(fā)中正常的加速手段和網(wǎng)絡(luò)代理完全是兩碼事。配置大致長(zhǎng)這樣mirror idaliyunmaven/id mirrorOfcentral/mirrorOf nameAliyun Maven Mirror/name urlhttps://maven.aliyun.com/repository/central/url /mirror配置好之后下載速度會(huì)有質(zhì)的飛躍。這一步是很多新手容易忽略的等真正開(kāi)始建項(xiàng)目時(shí)才意識(shí)到j(luò)ar包下載慢到讓人懷疑人生。2. Eclipse里搭建第一個(gè)Spring項(xiàng)目2.1 新建項(xiàng)目選擇正確的工程類型打開(kāi)EclipseFile - New - Maven Project在彈窗里勾選Create a simple project跳過(guò)骨架選擇然后填上Group Id一般用公司域名倒寫比如com.example和Artifact Id項(xiàng)目名比如spring-demoPackage那欄填成com.example.demo。到這里一個(gè)干凈的Maven項(xiàng)目就建好了目錄結(jié)構(gòu)里有src/main/java、src/main/resources和pom.xml。這一步很容易踩一個(gè)坑默認(rèn)的Maven項(xiàng)目沒(méi)有src/main/resources這個(gè)目錄。你需要在項(xiàng)目上右鍵 - New - Source Folder手動(dòng)創(chuàng)建一個(gè)路徑填src/main/resources。很多教程會(huì)默認(rèn)你有這個(gè)目錄結(jié)果你照著教程寫代碼發(fā)現(xiàn)ClassPathXmlApplicationContext一直報(bào)找不到配置文件原因就是配置文件沒(méi)放在資源目錄下。pom.xml是整個(gè)項(xiàng)目的核心Spring的相關(guān)依賴都寫在這。我們先加最基礎(chǔ)的兩個(gè)dependency groupIdorg.springframework/groupId artifactIdspring-context/artifactId version5.2.9.RELEASE/version /dependency只需要加spring-context這一個(gè)就夠了因?yàn)樗鼤?huì)把spring-core、spring-beans、spring-aop這些核心依賴一起拉下來(lái)。這是Maven的好處不用你手動(dòng)去算哪個(gè)依賴依賴哪個(gè)。2.2 引入Spring依賴三種方式的取舍在Eclipse里給Spring項(xiàng)目添加依賴大致有三種方式我三種都試過(guò)體驗(yàn)差別非常大。第一種就是上面說(shuō)的Maven依賴管理這是最推薦的方式。你的pom.xml里寫了什么依賴Maven會(huì)幫你下載好Eclipse的Maven插件會(huì)自動(dòng)把jar包加到項(xiàng)目的ClassPath里。這種方式的好處是依賴版本清晰、傳遞依賴自動(dòng)處理、項(xiàng)目換電腦重建時(shí)不需要保存一堆jar包。第二種是手動(dòng)下載jar包然后Build Path。這種方式特別坑因?yàn)镾pring各個(gè)模塊之間還有依賴關(guān)系你少一個(gè)jar包它就可能報(bào)某個(gè)ClassNotFound你能做的只能是一個(gè)個(gè)試。更麻煩的是Spring還依賴commons-logging、CGLIB等第三方庫(kù)少了任何一個(gè)都會(huì)出問(wèn)題。除非你的網(wǎng)絡(luò)環(huán)境實(shí)在下載不了Maven依賴否則我不建議你這么做。第三種方式是用Eclipse里的Spring Tools插件STS來(lái)快速創(chuàng)建項(xiàng)目它會(huì)自動(dòng)幫你生成Spring項(xiàng)目骨架和一些配置模板。但我不太推薦新手在第一步就使用STS因?yàn)椴寮?huì)隱藏太多細(xì)節(jié)導(dǎo)致你可能根本看不懂項(xiàng)目是怎么跑起來(lái)的。就像學(xué)開(kāi)車手動(dòng)擋可能會(huì)熄火但你能理解離合原理自動(dòng)擋雖然省事但出了問(wèn)題你完全不知道從哪兒排查。2.3 第一個(gè)Spring應(yīng)用從配置文件到運(yùn)行依賴配好了接下來(lái)就是寫一個(gè)能運(yùn)行的Demo。步驟非常簡(jiǎn)單但有三個(gè)環(huán)節(jié)缺一不可。第一步創(chuàng)建一個(gè)普通的Java類就叫UserService吧里面寫一個(gè)簡(jiǎn)單方法package com.example.demo; public class UserService { public void sayHello() { System.out.println(Hello, Spring!); } }這個(gè)類沒(méi)有任何Spring的痕跡就是一個(gè)普通的POJO。這也是Spring的一個(gè)優(yōu)點(diǎn)非侵入式設(shè)計(jì)你的業(yè)務(wù)類不需要繼承任何父類或者實(shí)現(xiàn)任何特定接口。第二步在src/main/resources下創(chuàng)建一個(gè)Spring的配置文件一般命名為applicationContext.xml?xml version1.0 encodingUTF-8? beans xmlnshttp://www.springframework.org/schema/beans xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd bean iduserService classcom.example.demo.UserService / /beans這個(gè)配置的意思很直白告訴Spring容器幫我創(chuàng)建一個(gè)UserService的實(shí)例并給它起個(gè)名字叫userService。Spring讀取到這個(gè)配置后會(huì)自動(dòng)去實(shí)例化這個(gè)對(duì)象把它放進(jìn)容器里。第三步寫一個(gè)帶main方法的測(cè)試類來(lái)運(yùn)行package com.example.demo; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class SpringDemo { public static void main(String[] args) { ApplicationContext context new ClassPathXmlApplicationContext(applicationContext.xml); UserService userService (UserService) context.getBean(userService); userService.sayHello(); } }右鍵 - Run As - Java Application控制臺(tái)如果輸出Hello, Spring!說(shuō)明你的Spring環(huán)境已經(jīng)成功跑起來(lái)了。聽(tīng)上去很簡(jiǎn)單對(duì)不對(duì)但就是這么簡(jiǎn)單的流程當(dāng)年我身邊還是有好幾個(gè)同事卡在配置文件上不是namespace沒(méi)寫全就是找不到XML文件的路徑問(wèn)題。所以這個(gè)Demo雖然基礎(chǔ)卻能幫你驗(yàn)證整個(gè)工具鏈?zhǔn)欠裢〞场?. 搞清楚Spring在背后做了什么3.1 IoC容器對(duì)象不再由你new剛才的Demo能夠運(yùn)行你可能覺(jué)得平平無(wú)奇不就new了一個(gè)對(duì)象嗎但實(shí)際上userService這個(gè)對(duì)象并不是我們主動(dòng)new出來(lái)的而是Spring通過(guò)讀取applicationContext.xml后幫我們創(chuàng)建出來(lái)的這就是控制反轉(zhuǎn)IoC最直觀的體現(xiàn)。傳統(tǒng)開(kāi)發(fā)模式下對(duì)象之間的依賴關(guān)系是由代碼自己維護(hù)的A要使用B就在A里new一個(gè)B出來(lái)。這會(huì)導(dǎo)致什么后果如果B的構(gòu)造函數(shù)變了或者B需要依賴C那所有new了B的地方都要改代碼耦合度極高。而IoC的思想是把對(duì)象的創(chuàng)建和管理交給容器誰(shuí)需要什么就向容器要容器負(fù)責(zé)把該對(duì)象需要的依賴也一并注入進(jìn)去。ApplicationContext就是容器的核心接口。在Eclipse里編寫時(shí)你會(huì)發(fā)現(xiàn)applicationContext.xml被加載之后我們其實(shí)有多種獲取Bean的方式按id獲取、按類型獲取、按id類型獲取。在實(shí)際開(kāi)發(fā)中推薦按類型獲取這樣連強(qiáng)轉(zhuǎn)都省了UserService userService context.getBean(UserService.class);當(dāng)然前提是這個(gè)類型在容器中只注冊(cè)了一個(gè)實(shí)例否則Spring會(huì)直接拋出NoUniqueBeanDefinitionException告訴你這個(gè)類型的Bean有歧義這就是檢查依賴是否重復(fù)定義的很好的機(jī)會(huì)。3.2 依賴注入的兩種落地方式IoC解決了對(duì)象創(chuàng)建的問(wèn)題依賴注入DI則解決對(duì)象之間怎么協(xié)作的問(wèn)題。最常見(jiàn)的兩種方式是構(gòu)造函數(shù)注入和setter注入。我寫個(gè)示例說(shuō)明public class OrderService { private UserService userService; // 構(gòu)造函數(shù)注入 public OrderService(UserService userService) { this.userService userService; } }對(duì)應(yīng)XML配置bean idorderService classcom.example.demo.OrderService constructor-arg refuserService / /bean也可以改成setter注入public class OrderService { private UserService userService; public void setUserService(UserService userService) { this.userService userService; } }對(duì)應(yīng)XML配置bean idorderService classcom.example.demo.OrderService property nameuserService refuserService / /bean你可能會(huì)問(wèn)這兩種注入方式到底有什么區(qū)別在早期Spring版本中setter注入是主流因?yàn)殪`活但在現(xiàn)在的Spring 4.x、5.x中官方更推薦構(gòu)造函數(shù)注入因?yàn)檫@種方式能保證依賴的完整性——對(duì)象一旦創(chuàng)建成功它的依賴就已經(jīng)就緒不會(huì)出現(xiàn)某個(gè)依賴還沒(méi)set就半初始化的情況。而且構(gòu)造函數(shù)注入對(duì)不可變對(duì)象支持更好也更方便做單元測(cè)試。我自己的習(xí)慣是核心依賴必須存在用構(gòu)造函數(shù)注入可選依賴可能有默認(rèn)值用setter注入。時(shí)間久了你會(huì)發(fā)現(xiàn)這個(gè)習(xí)慣能幫你在代碼review里省掉不少麻煩。3.3 Bean的生命周期與scope選擇不管是XML配置還是注解配置每個(gè)Bean在容器中都有明確的生命周期實(shí)例化、屬性填充、初始化、使用、銷毀。Spring允許你在初始化和銷毀時(shí)掛上自定義邏輯XML里可以配置init-method和destroy-method注解方式可以用PostConstruct和PreDestroy。關(guān)于Bean的作用域scope這是新手特別容易搞混的概念。Eclipse下右鍵查看spring的xsd你就明白了Spring中Bean默認(rèn)是singleton單例的也就是整個(gè)容器中只有一份實(shí)例所有地方拿到的是同一個(gè)對(duì)象。另外一種常見(jiàn)的是prototype原型每次getBean都會(huì)創(chuàng)建一個(gè)新實(shí)例。怎么選擇呢拿我實(shí)際開(kāi)發(fā)中的例子來(lái)說(shuō)無(wú)狀態(tài)的Service、DAO層全部用singleton就夠了因?yàn)镾pring容器自己會(huì)處理線程安全問(wèn)題。但如果你有一個(gè)類內(nèi)部維護(hù)了狀態(tài)比如計(jì)數(shù)器、臨時(shí)緩存那還當(dāng)單例用就可能出問(wèn)題這時(shí)候就應(yīng)該考慮prototype或者干脆你換個(gè)思路不要在單例Bean里維護(hù)會(huì)變化的狀態(tài)。bean iduserService classcom.example.demo.UserService scopesingleton / bean idtaskService classcom.example.demo.TaskService scopeprototype /搞清楚lifecycle和scope之后你對(duì)Spring的掌控才算是真正上了一層樓。4. 從Spring Core走向Spring MVC4.1 引入spring-webmvc需要什么跑通Core只是第一步開(kāi)發(fā)Web應(yīng)用時(shí)你真正天天打交道的其實(shí)是Spring MVC。在Eclipse里從Core往MVC過(guò)渡其實(shí)就是一句話的事在pom.xml里再加一個(gè)依賴。dependency groupIdorg.springframework/groupId artifactIdspring-webmvc/artifactId version5.2.9.RELEASE/version /dependency這個(gè)依賴會(huì)把你Web開(kāi)發(fā)所需的大部分Spring模塊都引進(jìn)來(lái)。然后再加一個(gè)Servlet API這里要注意一定要用provided scope因?yàn)門omcat里已經(jīng)自帶Servlet容器了你再加一遍會(huì)導(dǎo)致jar包沖突dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version4.0.1/version scopeprovided/scope /dependency如果你想在Eclipse里直接運(yùn)行Web項(xiàng)目還需要把項(xiàng)目轉(zhuǎn)成Dynamic Web Project。具體操作是右鍵項(xiàng)目 - Properties - Project Facets勾選Dynamic Web Module并設(shè)置好WebContent目錄。這一步在Maven項(xiàng)目中會(huì)自動(dòng)生成src/main/webapp你的JSP、HTML就放在這里。4.2 配置DispatcherServlet和注解驅(qū)動(dòng)Spring MVC是基于前端控制器模式設(shè)計(jì)的所有請(qǐng)求先到達(dá)DispatcherServlet再由它分發(fā)到具體的Controller。說(shuō)句大白話DispatcherServlet是整個(gè)Web應(yīng)用的入口和交通警察。它的配置在web.xml中servlet servlet-namedispatcher/servlet-name servlet-classorg.springframework.web.servlet.DispatcherServlet/servlet-class init-param param-namecontextConfigLocation/param-name param-valueclasspath:spring-mvc.xml/param-value /init-param load-on-startup1/load-on-startup /servlet servlet-mapping servlet-namedispatcher/servlet-name url-pattern//url-pattern /servlet-mapping這里有個(gè)細(xì)節(jié)load-on-startup必須設(shè)為1否則容器啟動(dòng)時(shí)不會(huì)初始化DispatcherServlet你可能等請(qǐng)求訪問(wèn)了才去加載到時(shí)候各種奇怪問(wèn)題都來(lái)了。我在調(diào)試?yán)享?xiàng)目的過(guò)程中發(fā)現(xiàn)這個(gè)坑真是經(jīng)典值得記下來(lái)。然后在spring-mvc.xml里開(kāi)啟注解驅(qū)動(dòng)和組件掃描context:component-scan base-packagecom.example.demo.controller / mvc:annotation-driven /有了這兩行配置你就可以直接用Controller和RequestMapping寫接口了Controller public class HelloController { RequestMapping(/hello) ResponseBody public String hello() { return Hello Spring MVC; } }在Eclipse里配置好Tomcat服務(wù)器Run on Server瀏覽器訪問(wèn)http://localhost:8080/項(xiàng)目名/hello看到Hello Spring MVC出現(xiàn)在頁(yè)面上的那一刻你基本已經(jīng)摸到Spring Web開(kāi)發(fā)的門兒了。5. 常見(jiàn)問(wèn)題與排查技巧實(shí)錄5.1 問(wèn)題速查表我在寫Spring項(xiàng)目的過(guò)程中被各種報(bào)錯(cuò)折磨的次數(shù)太多了。這里把出現(xiàn)頻率高的幾個(gè)都拉出來(lái)方便你直接對(duì)號(hào)入座。報(bào)錯(cuò)信息常見(jiàn)原因排查思路ClassNotFoundException: org.springframework.context.ApplicationContext缺少spring-context依賴檢查Maven的pom.xml是否配置正確右鍵Maven - Update Project刷新依賴ClassNotFoundError: org.apache.catalina.startup.BootstrapTomcat類找不到檢查項(xiàng)目是否配置了Server Runtime右鍵Properties - Targetted Runtimes勾選TomcatNoSuchBeanDefinitionExceptionBean沒(méi)有注冊(cè)或者id寫錯(cuò)檢查XML中bean定義是否存在用Autowired時(shí)檢查類是否被掃描到NoUniqueBeanDefinitionException同一類型有多個(gè)Bean加上Qualifier指定具體bean名稱或xml中使用primary屬性BeanCreationExceptionBean創(chuàng)建失敗屬性注入不了看Caused by的詳細(xì)堆棧重點(diǎn)排查構(gòu)造函數(shù)參數(shù)有沒(méi)有匹配的Bean配置文件找不到XML路徑寫錯(cuò)ClassPathXmlApplicationContext用的是classpath相對(duì)路徑別把src/main/java下的文件路徑當(dāng)classpath中文亂碼編碼不一致統(tǒng)一Eclipse的Workspace編碼為UTF-8XML文件頭部也要聲明UTF-8這張表是我精簡(jiǎn)后保留下來(lái)的實(shí)際上還有很多細(xì)碎的問(wèn)題。遇到任何報(bào)錯(cuò)記住一條萬(wàn)能法則先看最底下那個(gè)Caused by前面的信息往往都是包裝過(guò)的最底下的才是真正的原因。Eclipse的控制臺(tái)不會(huì)說(shuō)謊只是你需要習(xí)慣它的表達(dá)方式。5.2 我踩過(guò)的那些坑和排查思路第一個(gè)坑Eclipse里明明Maven依賴都引入了代碼里卻不報(bào)錯(cuò)但不顯示Spring的Maven Dependencies庫(kù)。這種情況通常是你只寫了pom.xml但沒(méi)觸發(fā)Eclipse的Maven插件去解析它。解決辦法右鍵項(xiàng)目 - Maven - Update Project彈窗里勾選Force Update of Snapshots/Releases點(diǎn)OKEclipse就會(huì)強(qiáng)制刷新依賴。第二個(gè)坑ClassPathXmlApplicationContext在啟動(dòng)時(shí)一直提示找不到applicationContext.xml。原因多半是你把XML放錯(cuò)目錄了。在Maven項(xiàng)目中配置文件必須放在src/main/resources下不能放src/main/java下。前者會(huì)編譯到classpath根路徑后者是Java源碼目錄編譯器不會(huì)把它當(dāng)資源文件拷貝過(guò)去。第三個(gè)坑Spring和CGLIB版本不匹配。這種報(bào)錯(cuò)在你的代碼反復(fù)檢查都正確時(shí)會(huì)讓人特別抓狂。我的經(jīng)驗(yàn)是如果你用了XML配置的aop:aspectj-autoproxy或者手動(dòng)寫了CGLIB代理相關(guān)代碼需要確認(rèn)spring核心依賴?yán)镆氲腸glib版本本身沒(méi)問(wèn)題不要自己去手動(dòng)添加cglib依賴讓Maven管理傳遞依賴反而更可靠。第四個(gè)坑是Tomcat啟動(dòng)時(shí)報(bào)端口被占用。這個(gè)和Spring本身關(guān)系不大但很常見(jiàn)。在Eclipse里改一下Server配置即可雙擊Servers視圖里的Tomcat實(shí)例在Ports那一欄把8080改成8081。如果還不行Windows下可以打開(kāi)任務(wù)管理器把占用端口的java進(jìn)程結(jié)束掉再試。第五個(gè)坑很隱蔽Eclipse的小版本、插件版本和Spring的jar包版本三者對(duì)不上。比如你的Eclipse是2019版本的用了較新的插件解析不了Spring 5.3中某些新引入的XML schema定義啟動(dòng)時(shí)報(bào)sax解析錯(cuò)誤。這種問(wèn)題基本只能靠回退版本解決要么升級(jí)Eclipse要么退回Spring舊版本。我的建議是保持Spring版本相對(duì)穩(wěn)定不要頻繁跟著升級(jí)穩(wěn)定性大于新鮮感。6. 一些用得上的開(kāi)發(fā)習(xí)慣與建議6.1 Eclipse里調(diào)試Spring項(xiàng)目的實(shí)用技巧寫Spring代碼離不開(kāi)調(diào)試而Eclipse的調(diào)試功能其實(shí)相當(dāng)強(qiáng)大。我常用的方式是打斷點(diǎn)然后以Debug模式啟動(dòng)。在Eclipse的Debug視圖中你可以看到當(dāng)前調(diào)用棧、所有變量的值甚至可以直接選中一個(gè)表達(dá)式右鍵-Inspect快速查看表達(dá)式的值。調(diào)試Spring項(xiàng)目還有一個(gè)竅門在Eclipse的Preferences里搜索Java - Debug勾選Show Variable Details的選項(xiàng)可以讓調(diào)試器展示變量?jī)?nèi)部詳細(xì)結(jié)構(gòu)。當(dāng)你調(diào)試IoC容器初始化的代碼時(shí)能看到Spring創(chuàng)建的Bean對(duì)象以及它們之間的引用關(guān)系那種豁然開(kāi)朗的感覺(jué)比看十遍文檔都管用。另外強(qiáng)烈建議設(shè)置Eclipse自動(dòng)編譯并且在Project - Build Automatically上打勾。Spring項(xiàng)目很吃編譯路徑漏編譯會(huì)導(dǎo)致代碼明明改了卻不生效。如果是Web項(xiàng)目更新了代碼右鍵Tomcat - Clean再把項(xiàng)目同步到Server能有效避免某些舊代碼殘留的問(wèn)題。6.2 XML配置和注解配置怎么取舍雖然Spring現(xiàn)在推薦全注解但在Eclipse維護(hù)老項(xiàng)目時(shí)你大概率還是要讀懂XML。我個(gè)人的建議是小項(xiàng)目、臨時(shí)Demo用注解以速度和直觀為主大型項(xiàng)目、團(tuán)隊(duì)協(xié)作建議XML與注解混用核心數(shù)據(jù)源、事務(wù)管理用XML統(tǒng)一管理、不易分散業(yè)務(wù)Bean用注解開(kāi)發(fā)快、代碼簡(jiǎn)潔。舉個(gè)實(shí)際的例子數(shù)據(jù)源的配置如果寫在XML里運(yùn)維或其他人接手時(shí)能快速看到數(shù)據(jù)源連接信息不用一個(gè)類一個(gè)類翻。而Controller、Service這類業(yè)務(wù)組件寫注解既效率高又方便類內(nèi)部邏輯的閱讀。兩者結(jié)合才能算真正的生產(chǎn)級(jí)實(shí)踐。如果你正在做的項(xiàng)目還是全XML也別急著推翻重來(lái)老項(xiàng)目穩(wěn)定性永遠(yuǎn)是第一位的。還要聊一下scope的選擇。在Eclipse的Spring視角下你可以用Spring的Bean Graph工具直觀地看到所有Bean之間的依賴關(guān)系如果發(fā)現(xiàn)Bean比較多可以按模塊分組來(lái)管理。這在排查循環(huán)依賴的時(shí)候很有用一眼就能看出A引用B、B又引用了A這種問(wèn)題。Spring三級(jí)緩存能解決大部分循環(huán)依賴問(wèn)題但那是框架層面的兜底業(yè)務(wù)層面盡量別自己制造循環(huán)依賴這是設(shè)計(jì)問(wèn)題不是框架問(wèn)題。這個(gè)標(biāo)題雖然只是個(gè)起點(diǎn)但它確實(shí)是每個(gè)Java工程師都會(huì)經(jīng)歷的一步?,F(xiàn)在回過(guò)頭看Eclipse搭配Spring的環(huán)境搭建過(guò)程算不上復(fù)雜無(wú)非是版本匹配、依賴管理、配置文件三件事。但只要這三件事有一條線沒(méi)理清就會(huì)卡住你很久。我也一直在用Eclipse做一些小工具和原型驗(yàn)證快速跑通想法的效率確實(shí)高。Spring作為一個(gè)優(yōu)秀且龐大的生態(tài)它的價(jià)值遠(yuǎn)不是IoC容器這么簡(jiǎn)單但每一個(gè)框架都是從第一個(gè)Hello Spring開(kāi)始的。你在Eclipse里踩過(guò)的每一個(gè)坑都會(huì)變成你理解Spring深度的墊腳石。