IDEA創(chuàng)建Spring MVC項(xiàng)目全流程:從Maven配置到Tomcat部署
1. 從零到一為什么需要一個(gè)Spring MVC項(xiàng)目如果你剛接觸Java Web開(kāi)發(fā)或者從其他IDE比如Eclipse轉(zhuǎn)過(guò)來(lái)可能會(huì)覺(jué)得在IDEA里新建一個(gè)項(xiàng)目是件挺簡(jiǎn)單的事。但說(shuō)實(shí)話我見(jiàn)過(guò)太多新手包括一些有經(jīng)驗(yàn)的開(kāi)發(fā)者在第一步就踩了坑。他們要么創(chuàng)建了一個(gè)“假”的Spring MVC項(xiàng)目結(jié)構(gòu)混亂依賴(lài)缺失要么配置了半天Tomcat一啟動(dòng)就報(bào)404。這背后的原因往往不是技術(shù)有多難而是對(duì)“項(xiàng)目”這個(gè)概念的理解以及IDEA這個(gè)工具的工作邏輯沒(méi)摸透。所以這篇內(nèi)容我們不只講“點(diǎn)擊哪里”更要講清楚“為什么這么點(diǎn)”。Spring MVC是一個(gè)經(jīng)典的、基于Servlet的Web框架它的核心是DispatcherServlet前端控制器。一個(gè)標(biāo)準(zhǔn)的Spring MVC項(xiàng)目本質(zhì)上是一個(gè)配置了特定Servlet和Spring容器的Web應(yīng)用。在IDEA里創(chuàng)建它意味著我們需要一個(gè)符合Servlet規(guī)范的Web項(xiàng)目結(jié)構(gòu)并正確引入Spring MVC的核心依賴(lài)最后配置好DispatcherServlet。聽(tīng)起來(lái)步驟不少但I(xiàn)DEA通過(guò)項(xiàng)目模板Project Template和模塊Module的概念把這些步驟封裝了起來(lái)。我們的任務(wù)就是理解并正確使用這些封裝而不是被它們迷惑。接下來(lái)我會(huì)帶你完整走一遍流程從項(xiàng)目類(lèi)型選擇、依賴(lài)管理、目錄結(jié)構(gòu)解讀到最終的運(yùn)行和調(diào)試。我會(huì)重點(diǎn)解釋每個(gè)選擇背后的考量以及那些官方文檔不會(huì)寫(xiě)的、容易出錯(cuò)的細(xì)節(jié)。比如為什么我推薦用Maven而不是Gradle作為起步web.xml和注解配置該怎么選lib目錄下的jar包到底從哪來(lái)這些看似基礎(chǔ)的問(wèn)題恰恰是項(xiàng)目能否順利跑起來(lái)的關(guān)鍵。2. 項(xiàng)目創(chuàng)建的十字路口Maven、Gradle與項(xiàng)目模板的抉擇打開(kāi)IDEA點(diǎn)擊“New Project”你會(huì)面臨第一個(gè)關(guān)鍵選擇項(xiàng)目類(lèi)型。這里常見(jiàn)的選項(xiàng)有Maven、Gradle以及老式的“Java Enterprise”它可能會(huì)引導(dǎo)你使用應(yīng)用服務(wù)器提供的模板。對(duì)于Spring MVC新手我強(qiáng)烈建議選擇Maven作為構(gòu)建工具。原因有三第一Maven的pom.xml配置文件結(jié)構(gòu)清晰依賴(lài)聲明一目了然是學(xué)習(xí)依賴(lài)管理的最佳教材第二網(wǎng)絡(luò)上的Spring MVC教程和解決方案絕大多數(shù)基于Maven遇到問(wèn)題更容易搜索到答案第三IDEA對(duì)Maven的支持已經(jīng)非常成熟和穩(wěn)定能減少很多工具層面的干擾。注意雖然Gradle更現(xiàn)代、構(gòu)建速度更快但其基于Groovy或Kotlin DSL的構(gòu)建腳本對(duì)新手來(lái)說(shuō)學(xué)習(xí)曲線更陡。先掌握Maven以后再遷移到Gradle會(huì)容易得多。在Maven項(xiàng)目中IDEA通常會(huì)提供一個(gè)“archetype”原型列表。這里不要選擇任何Spring相關(guān)的archetype比如spring-boot-starter。我們要?jiǎng)?chuàng)建的是傳統(tǒng)的、非Spring Boot的Spring MVC項(xiàng)目所以直接使用最基礎(chǔ)的maven-archetype-webapp即可。這個(gè)原型會(huì)生成一個(gè)最基礎(chǔ)的Web應(yīng)用骨架包含標(biāo)準(zhǔn)的src/main/webapp目錄和web.xml文件這正是我們需要的起點(diǎn)。如果找不到這個(gè)原型或者你想從絕對(duì)空白開(kāi)始也可以直接創(chuàng)建一個(gè)“Maven”項(xiàng)目不選擇任何原型。創(chuàng)建完成后手動(dòng)創(chuàng)建Web應(yīng)用所需的目錄結(jié)構(gòu)。但使用maven-archetype-webapp能省去不少手動(dòng)配置的麻煩。關(guān)鍵步驟與參數(shù)解析GroupId ArtifactId: 這是Maven坐標(biāo)。GroupId通常用公司或組織域名的反寫(xiě)如com.exampleArtifactId是項(xiàng)目名如springmvc-demo。這會(huì)影響你的包名和最終生成的jar/war文件名。Version: 保持默認(rèn)的1.0-SNAPSHOT即可。項(xiàng)目位置: 選擇一個(gè)干凈的目錄。避免路徑中包含中文或特殊字符這是所有Java項(xiàng)目的通用準(zhǔn)則可以避免很多潛在的編碼和路徑解析問(wèn)題。點(diǎn)擊“Finish”后IDEA會(huì)開(kāi)始創(chuàng)建項(xiàng)目并下載Maven wrapper如果勾選了相關(guān)選項(xiàng)。首次創(chuàng)建可能會(huì)慢一些因?yàn)樾枰獜倪h(yuǎn)程倉(cāng)庫(kù)下載Maven核心插件。3. 核心依賴(lài)注入pom.xml的精準(zhǔn)配置藝術(shù)項(xiàng)目創(chuàng)建好后打開(kāi)根目錄下的pom.xml文件。這是整個(gè)項(xiàng)目的“心臟”所有依賴(lài)和構(gòu)建配置都在這里。一個(gè)典型的、用于學(xué)習(xí)目的的Spring MVC項(xiàng)目需要以下核心依賴(lài)properties spring.version5.3.23/spring.version !-- 建議選擇一個(gè)穩(wěn)定的5.3.x版本 -- servlet-api.version4.0.1/servlet-api.version junit.version5.9.1/junit.version /properties dependencies !-- Spring MVC 核心 -- dependency groupIdorg.springframework/groupId artifactIdspring-webmvc/artifactId version${spring.version}/version /dependency !-- Servlet API (provided scope因?yàn)門(mén)omcat會(huì)提供) -- dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version${servlet-api.version}/version scopeprovided/scope /dependency !-- JSP API (provided scope) -- dependency groupIdjavax.servlet.jsp/groupId artifactIdjavax.servlet.jsp-api/artifactId version2.3.3/version scopeprovided/scope /dependency !-- JSTL 標(biāo)簽庫(kù)用于在JSP中簡(jiǎn)化邏輯 -- dependency groupIdjavax.servlet/groupId artifactIdjstl/artifactId version1.2/version /dependency !-- 日志門(mén)面Spring默認(rèn)使用commons-logging這里用SLF4JLogback更佳 -- dependency groupIdorg.slf4j/groupId artifactIdslf4j-api/artifactId version1.7.36/version /dependency dependency groupIdch.qos.logback/groupId artifactIdlogback-classic/artifactId version1.2.11/version /dependency !-- 單元測(cè)試 -- dependency groupIdorg.junit.jupiter/groupId artifactIdjunit-jupiter/artifactId version${junit.version}/version scopetest/scope /dependency !-- 為了讓Spring TestContext能運(yùn)行需要此依賴(lài) -- dependency groupIdorg.springframework/groupId artifactIdspring-test/artifactId version${spring.version}/version scopetest/scope /dependency /dependencies為什么這么配置版本管理使用properties統(tǒng)一管理版本號(hào)便于后續(xù)升級(jí)。Spring 5.3.x是一個(gè)長(zhǎng)期支持且非常穩(wěn)定的分支適合學(xué)習(xí)。spring-webmvc這個(gè)依賴(lài)是核心它本身會(huì)傳遞性引入spring-context,spring-aop,spring-beans,spring-core,spring-web等所有必要的Spring模塊。你不需要單獨(dú)引入它們。provided作用域Servlet和JSP的API在項(xiàng)目運(yùn)行時(shí)由Tomcat這類(lèi)Servlet容器提供。標(biāo)記為provided意味著Maven只在編譯和測(cè)試時(shí)使用這些依賴(lài)打包成WAR時(shí)不會(huì)包含進(jìn)去避免與容器中的庫(kù)沖突。JSTL雖然現(xiàn)代項(xiàng)目可能更多使用Thymeleaf等模板引擎但JSPJSTL仍是理解Spring MVC視圖層最直接的方式。它允許你在JSP中使用類(lèi)似c:forEach的標(biāo)簽避免在頁(yè)面中編寫(xiě)大量的Java腳本片段。日志Spring內(nèi)部使用commons-logging作為日志門(mén)面但它會(huì)自動(dòng)適配當(dāng)前類(lèi)路徑上的具體日志實(shí)現(xiàn)如Logback。我們直接配置好SLF4J和Logback就能看到Spring內(nèi)部的詳細(xì)日志對(duì)于調(diào)試至關(guān)重要。保存pom.xml后IDEA右上角通常會(huì)彈出提示問(wèn)你是否要導(dǎo)入更改Enable Auto-Import。務(wù)必點(diǎn)擊“Enable Auto-Import”這樣IDEA會(huì)在后臺(tái)自動(dòng)下載這些依賴(lài)庫(kù)。你可以在右側(cè)邊欄的“Maven”工具窗口中查看下載進(jìn)度和依賴(lài)樹(shù)。4. 目錄結(jié)構(gòu)的重塑從Maven標(biāo)準(zhǔn)到可運(yùn)行的Web應(yīng)用使用maven-archetype-webapp生成的項(xiàng)目目錄結(jié)構(gòu)可能不完全符合我們的習(xí)慣尤其是源代碼目錄。我們需要手動(dòng)調(diào)整和完善。標(biāo)準(zhǔn)且推薦的目錄結(jié)構(gòu)如下springmvc-demo (項(xiàng)目根目錄) ├── pom.xml ├── src │ ├── main │ │ ├── java -- 手動(dòng)創(chuàng)建存放Java源代碼 │ │ │ └── com │ │ │ └── example │ │ │ └── controller │ │ │ └── HelloController.java │ │ ├── resources -- 手動(dòng)創(chuàng)建存放配置文件 │ │ │ ├── spring │ │ │ │ └── spring-mvc.xml │ │ │ └── logback.xml │ │ └── webapp -- 原型已創(chuàng)建Web應(yīng)用根目錄 │ │ ├── WEB-INF │ │ │ ├── web.xml -- 部署描述符 │ │ │ └── views -- 手動(dòng)創(chuàng)建存放JSP視圖 │ │ │ └── hello.jsp │ │ └── index.jsp │ └── test -- 測(cè)試代碼目錄 │ ├── java │ └── resources手動(dòng)創(chuàng)建與配置要點(diǎn)在src/main下右鍵新建目錄Directory分別創(chuàng)建java和resources目錄。關(guān)鍵一步將這兩個(gè)新建的目錄標(biāo)記為源代碼根和資源根。右鍵點(diǎn)擊src/main/java目錄 - “Mark Directory as” - “Sources Root”。同樣右鍵點(diǎn)擊src/main/resources- “Mark Directory as” - “Resources Root”。這樣IDEA才會(huì)識(shí)別這些目錄下的文件并進(jìn)行編譯和資源處理。在webapp/WEB-INF下創(chuàng)建views目錄用于存放我們的JSP文件。將視圖放在WEB-INF下是一種安全實(shí)踐因?yàn)閃EB-INF下的文件不能直接被客戶(hù)端瀏覽器訪問(wèn)必須通過(guò)控制器(Controller)轉(zhuǎn)發(fā)這符合MVC模式。5. 配置文件的交響曲web.xml與Spring MVC配置詳解接下來(lái)是配置的核心部分涉及兩個(gè)主要文件web.xml和Spring的配置文件如spring-mvc.xml。這里我們采用基于web.xml的配置結(jié)合注解驅(qū)動(dòng)的方式這是理解Spring MVC工作原理最清晰的方式。5.1 配置web.xml前端控制器的入口web.xml是Servlet規(guī)范的部署描述符我們需要在這里聲明和配置Spring MVC的核心——DispatcherServlet。?xml version1.0 encodingUTF-8? web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaee xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd version4.0 !-- 1. 配置Spring的上下文監(jiān)聽(tīng)器用于加載根應(yīng)用上下文非Web層Bean如Service, Dao -- !-- 本例為簡(jiǎn)化將所有配置都放在DispatcherServlet中故可省略ContextLoaderListener -- !-- listener.../listener -- !-- 2. 配置字符編碼過(guò)濾器解決POST請(qǐng)求中文亂碼問(wèn)題 -- filter filter-namecharacterEncodingFilter/filter-name filter-classorg.springframework.web.filter.CharacterEncodingFilter/filter-class init-param param-nameencoding/param-name param-valueUTF-8/param-value /init-param init-param param-nameforceEncoding/param-name param-valuetrue/param-value /init-param /filter filter-mapping filter-namecharacterEncodingFilter/filter-name url-pattern/*/url-pattern /filter-mapping !-- 3. 配置Spring MVC的核心控制器DispatcherServlet -- servlet servlet-namedispatcherServlet/servlet-name servlet-classorg.springframework.web.servlet.DispatcherServlet/servlet-class !-- 指定Spring MVC配置文件的位置和名稱(chēng) -- init-param param-namecontextConfigLocation/param-name param-valueclasspath:spring/spring-mvc.xml/param-value /init-param !-- 設(shè)置容器啟動(dòng)時(shí)立即加載此Servlet而不是第一次請(qǐng)求時(shí) -- load-on-startup1/load-on-startup /servlet !-- 4. 將DispatcherServlet映射到所有請(qǐng)求/ -- servlet-mapping servlet-namedispatcherServlet/servlet-name url-pattern//url-pattern /servlet-mapping /web-app關(guān)鍵解析CharacterEncodingFilter這是一個(gè)非常實(shí)用且容易被忽略的過(guò)濾器。它確保了請(qǐng)求request和響應(yīng)response的編碼都設(shè)置為UTF-8從根本上杜絕了中文亂碼問(wèn)題。forceEncoding參數(shù)確保即使請(qǐng)求頭已指定編碼也強(qiáng)制使用我們?cè)O(shè)置的UTF-8。DispatcherServletload-on-startup1讓它在Web容器Tomcat啟動(dòng)時(shí)就初始化并加載其對(duì)應(yīng)的Spring IoC容器WebApplicationContext。contextConfigLocation參數(shù)指向我們即將創(chuàng)建的Spring MVC配置文件。如果不指定默認(rèn)會(huì)去/WEB-INF/下找名為servlet-name-servlet.xml的文件即dispatcherServlet-servlet.xml。url-pattern//url-pattern這個(gè)映射非常關(guān)鍵。它表示DispatcherServlet將處理所有到達(dá)應(yīng)用的請(qǐng)求除了像.jsp這樣的由Servlet容器默認(rèn)處理的請(qǐng)求。注意這里不是/*/*會(huì)匹配包括.jsp在內(nèi)的所有路徑可能導(dǎo)致一些意外行為。使用/是Spring MVC的推薦做法。5.2 配置spring-mvc.xmlSpring MVC的大腦在src/main/resources/spring/目錄下創(chuàng)建spring-mvc.xml。?xml version1.0 encodingUTF-8? beans xmlnshttp://www.springframework.org/schema/beans xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xmlns:contexthttp://www.springframework.org/schema/context xmlns:mvchttp://www.springframework.org/schema/mvc xsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd !-- 1. 自動(dòng)掃描指定包下的組件Controller, Service等并注冊(cè)為Bean -- context:component-scan base-packagecom.example.controller/ !-- 2. 開(kāi)啟注解驅(qū)動(dòng)替代傳統(tǒng)的處理器映射器和適配器配置 -- !-- 它會(huì)自動(dòng)注冊(cè)RequestMappingHandlerMapping、RequestMappingHandlerAdapter等 -- mvc:annotation-driven/ !-- 3. 配置靜態(tài)資源處理。將對(duì)/css/**, /js/**, /images/**的請(qǐng)求映射到對(duì)應(yīng)目錄 -- !-- 不經(jīng)過(guò)DispatcherServlet由容器默認(rèn)Servlet處理提升性能 -- mvc:resources mapping/static/** location/static// !-- 4. 配置視圖解析器ViewResolver -- bean classorg.springframework.web.servlet.view.InternalResourceViewResolver !-- 前綴視圖文件所在的目錄 -- property nameprefix value/WEB-INF/views// !-- 后綴視圖文件的擴(kuò)展名 -- property namesuffix value.jsp/ /bean !-- 5. 配置文件上傳解析器如果需要的話 -- !-- bean idmultipartResolver classorg.springframework.web.multipart.commons.CommonsMultipartResolver.../bean -- /beans為什么這么配置context:component-scan這是Spring“約定優(yōu)于配置”理念的體現(xiàn)。它讓Spring自動(dòng)去com.example.controller包及其子包下掃描帶有Controller、Service、Repository、Component等注解的類(lèi)并將它們實(shí)例化、管理起來(lái)。無(wú)需在XML中手動(dòng)聲明每一個(gè)Bean。mvc:annotation-driven這是Spring MVC注解驅(qū)動(dòng)模式的核心開(kāi)關(guān)。打開(kāi)它Spring MVC就會(huì)自動(dòng)配置好處理RequestMapping、RequestBody、ResponseBody等注解所必需的組件。沒(méi)有它你的Controller注解將不起作用。mvc:resources這是一個(gè)性能優(yōu)化點(diǎn)。對(duì)于CSS、JavaScript、圖片等靜態(tài)資源不應(yīng)該經(jīng)過(guò)復(fù)雜的Spring MVC調(diào)度而應(yīng)該由Web容器直接返回。此配置告訴DispatcherServlet“凡是匹配/static/**路徑的請(qǐng)求直接去/static/目錄下找文件我不處理了。”InternalResourceViewResolver視圖解析器。當(dāng)控制器方法返回一個(gè)邏輯視圖名如hello時(shí)解析器會(huì)將其解析為具體的物理視圖路徑/WEB-INF/views/hello.jsp。這樣控制器就無(wú)需關(guān)心視圖的具體位置和類(lèi)型。6. 編寫(xiě)第一個(gè)控制器與視圖讓Hello World跑起來(lái)現(xiàn)在骨架和神經(jīng)都已就位是時(shí)候添加血肉了。我們來(lái)創(chuàng)建一個(gè)最簡(jiǎn)單的控制器和視圖。6.1 創(chuàng)建控制器HelloController.java在src/main/java/com/example/controller包下創(chuàng)建類(lèi)HelloController.java。package com.example.controller; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; Controller // 標(biāo)記這是一個(gè)Spring MVC控制器 public class HelloController { // 處理GET請(qǐng)求路徑為 /hello GetMapping(/hello) public String sayHello(RequestParam(value name, required false, defaultValue World) String name, Model model) { // 向模型中添加數(shù)據(jù)鍵為“userName”值為傳入的name參數(shù) model.addAttribute(userName, name); // 返回邏輯視圖名視圖解析器會(huì)將其解析為 /WEB-INF/views/hello.jsp return hello; } }代碼解讀Controller聲明這是一個(gè)控制器會(huì)被context:component-scan掃描到。GetMapping(“/hello”)一個(gè)組合注解等價(jià)于RequestMapping(value “/hello”, method RequestMethod.GET)。它將該方法映射到處理GET /hello請(qǐng)求。RequestParam用于獲取請(qǐng)求參數(shù)。value“name”指定參數(shù)名requiredfalse表示非必傳defaultValue提供默認(rèn)值。Model一個(gè)接口用于控制器向視圖傳遞數(shù)據(jù)。這里我們添加了一個(gè)屬性u(píng)serName。返回值“hello”這是一個(gè)邏輯視圖名由InternalResourceViewResolver解析為/WEB-INF/views/hello.jsp。6.2 創(chuàng)建視圖hello.jsp在src/main/webapp/WEB-INF/views/目錄下創(chuàng)建hello.jsp。% page contentTypetext/html;charsetUTF-8 languagejava % % taglib prefixc urihttp://java.sun.com/jsp/jstl/core % html head titleSpring MVC Demo/title /head body h1Hello, c:out value${userName}/!/h1 pThis is your first Spring MVC page./p /body /html視圖要點(diǎn)% page ... %設(shè)置頁(yè)面編碼為UTF-8。% taglib ... %引入JSTL核心標(biāo)簽庫(kù)這樣我們才能使用c:out等標(biāo)簽。${userName}這是EL表達(dá)式Expression Language用于從請(qǐng)求屬性、會(huì)話屬性等作用域中獲取數(shù)據(jù)。它對(duì)應(yīng)控制器中model.addAttribute(“userName”, name)設(shè)置的屬性。c:out value“${userName}”/使用JSTL標(biāo)簽輸出內(nèi)容。c:out默認(rèn)會(huì)對(duì)內(nèi)容進(jìn)行XML/HTML轉(zhuǎn)義是一種防止XSS攻擊的好習(xí)慣。7. 配置與運(yùn)行Tomcat集成與項(xiàng)目部署代碼寫(xiě)完了怎么運(yùn)行我們需要一個(gè)Servlet容器這里以Tomcat為例。7.1 在IDEA中配置Tomcat點(diǎn)擊IDEA右上角的“Add Configuration...”。點(diǎn)擊“”號(hào)選擇“Tomcat Server” - “Local”。在“Server”標(biāo)簽頁(yè)點(diǎn)擊“Configure...”指定你的Tomcat安裝目錄需要提前下載Tomcat 9.x或更高版本。在“Deployment”標(biāo)簽頁(yè)點(diǎn)擊“” - “Artifact”選擇你的項(xiàng)目生成的War包通常名為springmvc-demo:war exploded。務(wù)必選擇帶exploded的版本這代表“展開(kāi)的War”支持熱部署修改代碼和JSP后無(wú)需重啟Tomcat即可生效僅限部分更新。在“Application context”處可以設(shè)置訪問(wèn)路徑例如設(shè)置為/demo那么應(yīng)用訪問(wèn)地址就是http://localhost:8080/demo。如果留空或設(shè)置為/則直接訪問(wèn)http://localhost:8080/。7.2 啟動(dòng)與訪問(wèn)點(diǎn)擊綠色的運(yùn)行或調(diào)試按鈕IDEA會(huì)啟動(dòng)Tomcat并部署你的應(yīng)用。觀察控制臺(tái)日志如果沒(méi)有報(bào)錯(cuò)看到類(lèi)似“Initializing Spring DispatcherServlet ‘dispatcherServlet’”和“Completed initialization in XXX ms”的日志說(shuō)明Spring MVC上下文已成功啟動(dòng)。打開(kāi)瀏覽器訪問(wèn)http://localhost:8080/[你的應(yīng)用上下文]/hello。例如如果你設(shè)置了上下文為/demo就訪問(wèn)http://localhost:8080/demo/hello。你應(yīng)該能看到頁(yè)面顯示“Hello, World!”。嘗試在URL后加上參數(shù)http://localhost:8080/demo/hello?nameSpring頁(yè)面會(huì)顯示“Hello, Spring!”。8. 深度排錯(cuò)與進(jìn)階配置指南項(xiàng)目跑起來(lái)了但真正的挑戰(zhàn)往往在后面。這里分享幾個(gè)我踩過(guò)的坑和對(duì)應(yīng)的解決方案。8.1 常見(jiàn)啟動(dòng)失敗問(wèn)題排查問(wèn)題啟動(dòng)Tomcat時(shí)報(bào)ClassNotFoundException或NoClassDefFoundError通常是javax.servlet相關(guān)的類(lèi)。原因與解決檢查pom.xml中Servlet API的依賴(lài)是否設(shè)置了scopeprovided/scope。如果沒(méi)有Maven會(huì)將其打包進(jìn)WAR可能與Tomcat自帶的Servlet庫(kù)沖突。確保作用域正確。問(wèn)題訪問(wèn)URL報(bào)404錯(cuò)誤但Tomcat啟動(dòng)日志正常。排查鏈檢查應(yīng)用上下文路徑確認(rèn)瀏覽器訪問(wèn)的URL中的路徑與IDEA中Tomcat配置的“Application context”一致。檢查控制器映射確認(rèn)GetMapping(“/hello”)中的路徑是否正確是否包含了不必要的上下文。檢查web.xml中的url-pattern確認(rèn)是/而不是/*。檢查視圖解析器前綴后綴確認(rèn)InternalResourceViewResolver配置的prefix和suffix能正確拼接出JSP文件的物理路徑。可以嘗試在控制器方法中直接返回完整的JSP路徑如“/WEB-INF/views/hello.jsp”來(lái)繞過(guò)視圖解析器測(cè)試是否是解析器配置問(wèn)題。查看Tomcat日志IDEA的Run或Debug控制臺(tái)會(huì)輸出Tomcat的訪問(wèn)日志查看是否有對(duì)應(yīng)的請(qǐng)求記錄和可能的錯(cuò)誤信息。8.2 日志配置與查看清晰的日志是調(diào)試的生命線。我們?cè)趐om.xml中引入了Logback現(xiàn)在在src/main/resources下創(chuàng)建logback.xml來(lái)配置它。?xml version1.0 encodingUTF-8? configuration !-- 控制臺(tái)輸出 -- appender nameCONSOLE classch.qos.logback.core.ConsoleAppender encoder pattern%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n/pattern /encoder /appender !-- 將Spring框架的日志級(jí)別設(shè)為INFO或WARN避免過(guò)多DEBUG日志 -- logger nameorg.springframework levelINFO/ !-- 將我們自己的應(yīng)用包設(shè)為DEBUG級(jí)別便于調(diào)試 -- logger namecom.example levelDEBUG/ root levelINFO appender-ref refCONSOLE / /root /configuration啟動(dòng)項(xiàng)目時(shí)你會(huì)在控制臺(tái)看到格式化的日志輸出。通過(guò)調(diào)整不同包的日志級(jí)別可以精準(zhǔn)定位問(wèn)題。8.3 徹底告別JSP轉(zhuǎn)向Thymeleaf模板引擎雖然JSP是入門(mén)的好選擇但它在現(xiàn)代Spring Boot項(xiàng)目中已不是首選。Thymeleaf語(yǔ)法更自然原生支持HTML不依賴(lài)Servlet容器功能也更強(qiáng)大。遷移到Thymeleaf非常簡(jiǎn)單修改pom.xml添加Thymeleaf依賴(lài)dependency groupIdorg.thymeleaf/groupId artifactIdthymeleaf-spring5/artifactId version3.1.1.RELEASE/version /dependency修改spring-mvc.xml替換視圖解析器bean idtemplateResolver classorg.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver property nameprefix value/WEB-INF/views// property namesuffix value.html/ property nametemplateMode valueHTML/ property namecharacterEncoding valueUTF-8/ property namecacheable valuefalse/ !-- 開(kāi)發(fā)時(shí)設(shè)為false生產(chǎn)環(huán)境設(shè)為true -- /bean bean idtemplateEngine classorg.thymeleaf.spring5.SpringTemplateEngine property nametemplateResolver reftemplateResolver/ /bean bean classorg.thymeleaf.spring5.view.ThymeleafViewResolver property nametemplateEngine reftemplateEngine/ property namecharacterEncoding valueUTF-8/ /bean將hello.jsp重命名為hello.html并使用Thymeleaf語(yǔ)法!DOCTYPE html html xmlns:thhttp://www.thymeleaf.org head meta charsetUTF-8 titleSpring MVC Demo/title /head body h1Hello, span th:text${userName}World/span!/h1 pThis is your first Spring MVC page with Thymeleaf./p /body /html控制器代碼完全無(wú)需修改。Thymeleaf的th:text屬性會(huì)動(dòng)態(tài)替換標(biāo)簽體內(nèi)的文本。8.4 關(guān)于web.xml與純注解配置我們上面使用了web.xml。但Servlet 3.0規(guī)范支持用Java代碼實(shí)現(xiàn)WebApplicationInitializer接口完全替代web.xml。Spring提供了便捷的抽象類(lèi)AbstractAnnotationConfigDispatcherServletInitializer。使用純注解配置更簡(jiǎn)潔也是Spring Boot的默認(rèn)方式。但對(duì)于初學(xué)者從web.xml開(kāi)始能更直觀地理解Servlet和Filter的配置流程之后再學(xué)習(xí)純注解配置會(huì)更容易理解其背后的原理。

相關(guān)新聞

數(shù)學(xué)建模實(shí)戰(zhàn):基于MILP與啟發(fā)式算法的疫苗生產(chǎn)排程優(yōu)化

數(shù)學(xué)建模實(shí)戰(zhàn):基于MILP與啟發(fā)式算法的疫苗生產(chǎn)排程優(yōu)化

1. 項(xiàng)目概述:從一道賽題到一套完整的工業(yè)優(yōu)化方案2021年“五一杯”數(shù)學(xué)建模競(jìng)賽的A題“疫苗生產(chǎn)問(wèn)題”,在當(dāng)時(shí)那個(gè)特殊的時(shí)期,無(wú)疑是一個(gè)極具現(xiàn)實(shí)意義和挑戰(zhàn)性的題目。它不僅僅是一道數(shù)學(xué)題,更是對(duì)當(dāng)時(shí)全球面臨的疫苗生產(chǎn)與分配瓶…

2026/8/2 8:45:19 閱讀更多
全棧開(kāi)發(fā)的信息基礎(chǔ)

全棧開(kāi)發(fā)的信息基礎(chǔ)

對(duì)于全棧開(kāi)發(fā)的需求 我們要理解這么幾個(gè)東西 1.開(kāi)發(fā)需求 對(duì)于業(yè)務(wù)需求和原型圖需求 2.開(kāi)發(fā)環(huán)境和工具 3.開(kāi)發(fā)經(jīng)驗(yàn)和開(kāi)發(fā)節(jié)奏 4.部署服務(wù)器的相關(guān)信息 你列出的這四個(gè)維度非常精準(zhǔn),基本覆蓋了全棧項(xiàng)目從“紙面”到“線上”的全生命周期。作為全棧開(kāi)發(fā)者(或…

2026/8/2 8:45:19 閱讀更多
普本生進(jìn)廠指南:從技術(shù)員到工程師的制造業(yè)職業(yè)進(jìn)階路徑

普本生進(jìn)廠指南:從技術(shù)員到工程師的制造業(yè)職業(yè)進(jìn)階路徑

1. 從“進(jìn)廠打螺絲”說(shuō)起:一個(gè)被誤解的職業(yè)起點(diǎn)“普本畢業(yè),準(zhǔn)備進(jìn)廠打螺絲了!!!”——這句話在社交媒體上,常常帶著一絲自嘲、無(wú)奈,甚至是對(duì)未來(lái)的迷茫。作為一個(gè)在制造業(yè)一線摸爬滾打多年的“過(guò)…

2026/8/2 9:35:20 閱讀更多
IPv6折騰記——光貓?jiān)O(shè)置

IPv6折騰記——光貓?jiān)O(shè)置

書(shū)接上文,我們可以ping通家里的網(wǎng)絡(luò)設(shè)備,但是依舊無(wú)法訪問(wèn)家里的網(wǎng)絡(luò)設(shè)備(比如NAS,監(jiān)控設(shè)備),因?yàn)樗麄儌鬏斔玫膮f(xié)議用的是TCP/UDP。而這兩種協(xié)議會(huì)被光貓或者路由器的防火墻所攔截到,傳輸不到…

2026/8/2 9:35:20 閱讀更多
樹(shù)莓派Pico驅(qū)動(dòng)2.66英寸電子墨水屏:SPI通信與MicroPython實(shí)戰(zhàn)

樹(shù)莓派Pico驅(qū)動(dòng)2.66英寸電子墨水屏:SPI通信與MicroPython實(shí)戰(zhàn)

1. 項(xiàng)目概述:Pico e-Paper 2.66 是什么? 如果你手頭有一塊樹(shù)莓派 Pico 或者類(lèi)似的 RP2040 開(kāi)發(fā)板,又恰好對(duì)低功耗、類(lèi)紙顯示效果感興趣,那么“Pico e-Paper 2.66”這個(gè)項(xiàng)目絕對(duì)值得你花時(shí)間折騰一下。簡(jiǎn)單來(lái)說(shuō),這就是一…

2026/8/2 9:35:20 閱讀更多
Godot狀態(tài)圖開(kāi)發(fā)實(shí)戰(zhàn):從概念到應(yīng)用,解決復(fù)雜狀態(tài)管理難題

Godot狀態(tài)圖開(kāi)發(fā)實(shí)戰(zhàn):從概念到應(yīng)用,解決復(fù)雜狀態(tài)管理難題

1. 項(xiàng)目概述:為什么我們需要State Charts?如果你正在用Godot做游戲,尤其是涉及到角色行為、UI流程或者任何有復(fù)雜狀態(tài)切換的邏輯,那你大概率已經(jīng)體會(huì)過(guò)傳統(tǒng)狀態(tài)機(jī)(Finite State Machine, FSM)的痛點(diǎn)了。狀態(tài)…

2026/8/2 9:35:20 閱讀更多
TPFanCtrl2 v2.3.3雙風(fēng)扇嵌入式控制器深度配置指南

TPFanCtrl2 v2.3.3雙風(fēng)扇嵌入式控制器深度配置指南

TPFanCtrl2 v2.3.3雙風(fēng)扇嵌入式控制器深度配置指南 【免費(fèi)下載鏈接】TPFanCtrl2 ThinkPad Fan Control 2 (Dual Fan) for Windows 10 and 11 項(xiàng)目地址: https://gitcode.com/gh_mirrors/tp/TPFanCtrl2 TPFanCtrl2是一款基于Windows平臺(tái)的ThinkPad筆記本風(fēng)扇控制解決方案…

2026/8/2 9:35:20 閱讀更多
CM4S核心板深度解析:RK3568工業(yè)模塊與可插拔eMMC設(shè)計(jì)實(shí)戰(zhàn)

CM4S核心板深度解析:RK3568工業(yè)模塊與可插拔eMMC設(shè)計(jì)實(shí)戰(zhàn)

1. 項(xiàng)目概述:CM4S是什么,以及為什么它值得關(guān)注 最近在折騰嵌入式開(kāi)發(fā)板的朋友圈里,CM4S這個(gè)詞的熱度有點(diǎn)高。乍一看,很多人會(huì)把它和樹(shù)莓派基金會(huì)那款經(jīng)典的Compute Module 4(CM4)搞混,但實(shí)際上&…

2026/8/2 9:25:20 閱讀更多
MoneyPrinterPlus實(shí)戰(zhàn)指南:AI視頻批量生成與自動(dòng)化發(fā)布完整解決方案

MoneyPrinterPlus實(shí)戰(zhàn)指南:AI視頻批量生成與自動(dòng)化發(fā)布完整解決方案

MoneyPrinterPlus實(shí)戰(zhàn)指南:AI視頻批量生成與自動(dòng)化發(fā)布完整解決方案 【免費(fèi)下載鏈接】MoneyPrinterPlus AI一鍵批量生成各類(lèi)短視頻,自動(dòng)批量混剪短視頻,自動(dòng)把視頻發(fā)布到抖音,快手,小紅書(shū),視頻號(hào)上,賺錢(qián)從來(lái)沒(méi)有這么容易過(guò)! 支持本地語(yǔ)音模型chatTTS,fasterwhisper,…

2026/8/2 0:04:00 閱讀更多
3分鐘搞定!QQ空間歷史說(shuō)說(shuō)完整備份終極指南

3分鐘搞定!QQ空間歷史說(shuō)說(shuō)完整備份終極指南

3分鐘搞定!QQ空間歷史說(shuō)說(shuō)完整備份終極指南 【免費(fèi)下載鏈接】GetQzonehistory 獲取QQ空間發(fā)布的歷史說(shuō)說(shuō) 項(xiàng)目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory 你是否曾想過(guò),那些年發(fā)過(guò)的QQ空間說(shuō)說(shuō),那些記錄青春的文字…

2026/8/2 0:04:01 閱讀更多
MoneyPrinterPlus實(shí)戰(zhàn)指南:AI視頻批量生成與自動(dòng)化發(fā)布完整解決方案

MoneyPrinterPlus實(shí)戰(zhàn)指南:AI視頻批量生成與自動(dòng)化發(fā)布完整解決方案

MoneyPrinterPlus實(shí)戰(zhàn)指南:AI視頻批量生成與自動(dòng)化發(fā)布完整解決方案 【免費(fèi)下載鏈接】MoneyPrinterPlus AI一鍵批量生成各類(lèi)短視頻,自動(dòng)批量混剪短視頻,自動(dòng)把視頻發(fā)布到抖音,快手,小紅書(shū),視頻號(hào)上,賺錢(qián)從來(lái)沒(méi)有這么容易過(guò)! 支持本地語(yǔ)音模型chatTTS,fasterwhisper,…

2026/8/2 0:04:00 閱讀更多
3分鐘搞定!QQ空間歷史說(shuō)說(shuō)完整備份終極指南

3分鐘搞定!QQ空間歷史說(shuō)說(shuō)完整備份終極指南

3分鐘搞定!QQ空間歷史說(shuō)說(shuō)完整備份終極指南 【免費(fèi)下載鏈接】GetQzonehistory 獲取QQ空間發(fā)布的歷史說(shuō)說(shuō) 項(xiàng)目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory 你是否曾想過(guò),那些年發(fā)過(guò)的QQ空間說(shuō)說(shuō),那些記錄青春的文字…

2026/8/2 0:04:01 閱讀更多
AMAT 0100-02186 I/O 分配 PCB

AMAT 0100-02186 I/O 分配 PCB

AMAT 0100-02186 I/O分配PCB板是應(yīng)用材料(Applied Materials)公司生產(chǎn)的一款用于半導(dǎo)體設(shè)備的I/O信號(hào)分配電路板。該型號(hào)(0100-02186)的核心特點(diǎn)如下:專(zhuān)用于Endura等半導(dǎo)體工藝腔室。集成信號(hào)路由與分配功能。連接控制…

2026/8/2 2:51:21 閱讀更多
Nissei Corp FFMN-32L-10-T0 40AX 三相異步電動(dòng)機(jī)

Nissei Corp FFMN-32L-10-T0 40AX 三相異步電動(dòng)機(jī)

Nissei Corp FFMN-32L-10-T0 40AX 三相異步電動(dòng)機(jī)是日本日清(Nissei)品牌的一款工業(yè)用三相異步電機(jī),適用于自動(dòng)化設(shè)備及通用機(jī)械驅(qū)動(dòng)。該型號(hào)(FFMN-32L-10-T0 40AX)的核心特點(diǎn)如下:三相交流異步電動(dòng)機(jī)。額定…

2026/8/2 2:52:49 閱讀更多