
1. 項目背景與技術選型解析這個基于ThymeleafMavenServletMySQL的圖書管理系統框架是典型的Java Web應用開發實踐案例。作為系列教程的第三部分共九章它聚焦于企業級應用開發中最核心的技術棧組合。為什么選擇這個技術組合我在實際項目中多次驗證過它的可靠性Thymeleaf相比JSP它天然支持HTML5能與前端開發工具無縫配合且不依賴Servlet容器Maven解決依賴管理的噩夢特別是處理MySQL Connector這類經常需要版本適配的庫Servlet 3.1保持輕量級的同時支持注解配置避免web.xml的繁瑣MySQL 5.7事務完整性和JSON字段支持已足夠成熟提示初學者常犯的錯誤是直接使用最新版本組件。建議初學者采用穩定組合Thymeleaf 3.0 Servlet 4.0 MySQL 5.7 Maven 3.62. 環境準備與項目初始化2.1 Maven環境配置要點在IDEA中創建Maven項目時我推薦使用以下原型archetype groupIdorg.apache.maven.archetypes/groupId artifactIdmaven-archetype-webapp/artifactId version1.4/version /archetype關鍵配置項說明settings.xml鏡像配置解決國內下載慢問題mirror idaliyunmaven/id mirrorOf*/mirrorOf name阿里云公共倉庫/name urlhttps://maven.aliyun.com/repository/public/url /mirrorpom.xml基礎依賴dependencies !-- Servlet API -- dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version4.0.1/version scopeprovided/scope /dependency !-- Thymeleaf -- dependency groupIdorg.thymeleaf/groupId artifactIdthymeleaf/artifactId version3.0.12.RELEASE/version /dependency !-- MySQL Connector -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.27/version /dependency /dependencies2.2 數據庫初始化腳本創建圖書管理核心表CREATE TABLE books ( id int(11) NOT NULL AUTO_INCREMENT, title varchar(100) NOT NULL, author varchar(50) NOT NULL, isbn varchar(20) UNIQUE, publish_date date, price decimal(10,2), stock int(11) DEFAULT 0, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 添加測試數據 INSERT INTO books VALUES (1,Java編程思想,Bruce Eckel,9787111213826,2007-06-01,108.00,50), (2,Spring實戰,Craig Walls,9787115480661,2018-10-01,89.00,30);3. Servlet與Thymeleaf集成架構3.1 視圖解析器配置創建ThymeleafViewResolver是整合的關鍵public class ThymeleafConfig { private static TemplateEngine templateEngine; static { ServletContextTemplateResolver resolver new ServletContextTemplateResolver(); resolver.setPrefix(/WEB-INF/templates/); resolver.setSuffix(.html); resolver.setTemplateMode(TemplateMode.HTML); resolver.setCharacterEncoding(UTF-8); templateEngine new TemplateEngine(); templateEngine.setTemplateResolver(resolver); } public static TemplateEngine getTemplateEngine() { return templateEngine; } }3.2 基礎Servlet實現圖書列表展示的Servlet示例WebServlet(/book/list) public class BookListServlet extends HttpServlet { private BookService bookService new BookServiceImpl(); protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ListBook books bookService.getAllBooks(); req.setAttribute(books, books); WebContext ctx new WebContext( req, resp, req.getServletContext(), req.getLocale()); ctx.setVariable(books, books); ThymeleafConfig.getTemplateEngine() .process(book/list, ctx, resp.getWriter()); } }對應的Thymeleaf模板/WEB-INF/templates/book/list.html!DOCTYPE html html xmlns:thhttp://www.thymeleaf.org head meta charsetUTF-8 title圖書列表/title /head body table tr th:eachbook : ${books} td th:text${book.id}/td td th:text${book.title}/td td th:text${book.author}/td td th:text${#numbers.formatDecimal(book.price,1,2)}/td /tr /table /body /html4. 數據訪問層實現4.1 JDBC工具類封裝避免在每個DAO中重復創建連接public class JdbcUtil { private static DataSource dataSource; static { try { Context ctx new InitialContext(); dataSource (DataSource) ctx.lookup(java:comp/env/jdbc/BookDB); } catch (NamingException e) { throw new RuntimeException(數據源配置錯誤, e); } } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } public static void close(ResultSet rs, Statement stmt, Connection conn) { try { if(rs ! null) rs.close(); } catch (SQLException ignored) {} try { if(stmt ! null) stmt.close(); } catch (SQLException ignored) {} try { if(conn ! null) conn.close(); } catch (SQLException ignored) {} } }4.2 DAO模式實現圖書DAO接口與實現public interface BookDao { ListBook findAll() throws SQLException; Book findById(int id) throws SQLException; int insert(Book book) throws SQLException; int update(Book book) throws SQLException; int delete(int id) throws SQLException; } public class BookDaoImpl implements BookDao { Override public ListBook findAll() throws SQLException { Connection conn null; PreparedStatement stmt null; ResultSet rs null; ListBook books new ArrayList(); try { conn JdbcUtil.getConnection(); stmt conn.prepareStatement(SELECT * FROM books); rs stmt.executeQuery(); while(rs.next()) { Book book new Book(); book.setId(rs.getInt(id)); book.setTitle(rs.getString(title)); book.setAuthor(rs.getString(author)); book.setIsbn(rs.getString(isbn)); book.setPublishDate(rs.getDate(publish_date)); book.setPrice(rs.getBigDecimal(price)); book.setStock(rs.getInt(stock)); books.add(book); } } finally { JdbcUtil.close(rs, stmt, conn); } return books; } // 其他方法實現類似... }5. 常見問題排查指南5.1 Thymeleaf模板不渲染問題癥狀頁面直接顯示Thymeleaf表達式如${books}排查步驟檢查模板文件是否放在/WEB-INF/templates/目錄下確認HTML標簽添加了命名空間html xmlns:thhttp://www.thymeleaf.org查看Servlet是否正確設置了模板引擎檢查IDEA是否識別Thymeleaf語法需安裝Thymeleaf插件5.2 MySQL連接失敗處理典型錯誤信息Communications link failure解決方案矩陣錯誤類型可能原因解決方法連接超時數據庫地址/端口錯誤檢查jdbc.url中的IP和端口拒絕訪問用戶名密碼錯誤核對application.properties配置SSL錯誤MySQL 8.0默認啟用SSL在連接字符串添加?useSSLfalse時區問題服務器時區未設置添加serverTimezoneAsia/Shanghai5.3 Maven依賴沖突解決使用以下命令分析依賴樹mvn dependency:tree -Dverbose常見沖突處理Servlet API沖突確保scope為provided日志框架沖突排除沖突的logging依賴exclusions exclusion groupIdcommons-logging/groupId artifactIdcommons-logging/artifactId /exclusion /exclusions6. 性能優化實踐6.1 數據庫連接池配置推薦使用HikariCP替代傳統DBCPdependency groupIdcom.zaxxer/groupId artifactIdHikariCP/artifactId version4.0.3/version /dependency配置示例context.xmlResource namejdbc/BookDB authContainer typecom.zaxxer.hikari.HikariDataSource factorycom.zaxxer.hikari.HikariJNDIFactory jdbcUrljdbc:mysql://localhost:3306/bookdb usernameroot password123456 maximumPoolSize20 connectionTimeout30000/6.2 Thymeleaf模板緩存開發階段應禁用緩存resolver.setCacheable(false); // 開發環境設置為false生產環境配置resolver.setCacheTTLMs(3600000L); // 1小時緩存6.3 靜態資源處理正確配置靜態資源映射web.xmlservlet-mapping servlet-namedefault/servlet-name url-pattern/static/*/url-pattern /servlet-mapping7. 安全防護措施7.1 SQL注入防護必須使用PreparedStatement// 錯誤示范存在注入風險 String sql SELECT * FROM books WHERE title title ; // 正確做法 PreparedStatement stmt conn.prepareStatement( SELECT * FROM books WHERE title ?); stmt.setString(1, title);7.2 XSS防護Thymeleaf默認會進行HTML轉義特殊場景需要取消轉義時div th:text${#strings.escapeXml(book.title)}/div !-- 或 -- div th:utext${book.title}/div !-- 慎用 --7.3 CSRF防護實現簡單的CSRF Token機制// 生成Token String csrfToken UUID.randomUUID().toString(); session.setAttribute(csrfToken, csrfToken); // 驗證Token String requestToken request.getParameter(csrfToken); String sessionToken (String) session.getAttribute(csrfToken); if(!requestToken.equals(sessionToken)) { throw new SecurityException(CSRF驗證失敗); }8. 項目結構規范建議推薦的標準目錄結構src/ ├── main/ │ ├── java/ │ │ └── com/ │ │ └── example/ │ │ ├── config/ # 配置類 │ │ ├── controller/ # Servlet │ │ ├── dao/ # 數據訪問層 │ │ ├── model/ # 實體類 │ │ ├── service/ # 業務邏輯 │ │ └── util/ # 工具類 │ ├── resources/ │ │ ├── db/ # SQL腳本 │ │ └── config.properties # 配置文件 │ └── webapp/ │ ├── static/ # 靜態資源 │ │ ├── css/ │ │ ├── js/ │ │ └── images/ │ └── WEB-INF/ │ ├── templates/ # Thymeleaf模板 │ └── web.xml └── test/ # 測試代碼9. 擴展功能實現思路9.1 分頁查詢優化前端傳遞分頁參數int page Integer.parseInt(request.getParameter(page)); int size Integer.parseInt(request.getParameter(size)); ListBook books bookService.findByPage(page, size);MySQL分頁SQLSELECT * FROM books LIMIT ?, ?9.2 文件上傳功能Servlet 3.0的文件上傳處理WebServlet(/upload) MultipartConfig public class UploadServlet extends HttpServlet { protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Part filePart req.getPart(file); String fileName Paths.get(filePart.getSubmittedFileName()) .getFileName().toString(); InputStream fileContent filePart.getInputStream(); // 保存文件到指定目錄 Files.copy(fileContent, Paths.get(/uploads, fileName)); } }9.3 日志系統集成Logback配置示例logback.xmlconfiguration appender nameFILE classch.qos.logback.core.FileAppender filelogs/bookstore.log/file encoder pattern%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n/pattern /encoder /appender root levelINFO appender-ref refFILE / /root /configuration在項目開發過程中我發現很多初學者容易在Maven依賴版本管理上栽跟頭。建議鎖定關鍵依賴的版本號而不是使用動態版本如RELEASE、LATEST。對于數據庫操作一定要記得在finally塊中關閉資源否則隨著系統運行時間增長會出現連接泄漏問題。Thymeleaf的模板緩存機制在開發階段應該關閉否則每次修改模板都需要重啟服務器才能生效