
簡介本資源是一套完整的SpringBoot集成Freemarker實戰(zhàn)項目源碼包面向Java Web開發(fā)初學者與中級工程師解決模板引擎在現(xiàn)代Spring生態(tài)中快速落地與深度配置的常見痛點。壓縮包共275個文件涵蓋82個Freemarker模板.ftl、71個Java控制器與配置類、35個前端交互腳本.js、21個樣式文件.css及配套圖片、XML配置、SQL腳本等完整呈現(xiàn)前后端協(xié)同渲染的工程結構包體大小2.39MB輕量易導入。已有144人學習下載。資源直接提供可運行的項目骨架包含標準目錄組織、全量配置項說明如template-loader-path、cache策略、典型FTL語法示例條件判斷、列表遍歷、日期格式化、自定義指令預留接口以及BootstrapFont Awesome等主流CSS庫集成助開發(fā)者零調試啟動并理解視圖層最佳實踐。1. SpringBoot Freemarker 不是“配個 suffix 就完事”的模板集成而是視圖層工程化落地的關鍵一環(huán)很多剛從 SpringMVC 遷移過來的開發(fā)者看到spring-boot-starter-freemarker就以為只是把.jsp換成.ftl改個后綴、加個依賴、寫個return index就能跑通——結果在真實項目里卡在靜態(tài)資源 404、中文亂碼、日期格式不生效、自定義指令報TemplateException甚至上線后發(fā)現(xiàn)模板緩存沒關導致熱更新失效。這不是 Freemarker 本身的問題而是 SpringBoot 對模板引擎的抽象層FreeMarkerViewResolverFreeMarkerConfigurer與傳統(tǒng)配置方式存在隱式契約它默認啟用緩存、強制校驗模板路徑合法性、對Model數(shù)據(jù)序列化有嚴格類型約束。本項目springboot-freemarker-master.rar所含的完整前端資源包bootstrap.css、animate.css、datepicker3.css、chosen.css等共 9 類 CSS 文件恰恰說明一個可交付的 Freemarker 視圖工程必須同時解決「模板語法正確性」「靜態(tài)資源路徑一致性」「瀏覽器端 JS/CSS 加載時序」「服務端數(shù)據(jù)渲染邊界控制」四大問題。適合正在做后臺管理界面、內部運營系統(tǒng)、PDF 報表生成或需要強定制化 HTML 輸出的 Java 開發(fā)者尤其適用于不能使用 Vue/React 做前后端分離、但又要求 UI 層具備響應式與交互能力的中型政企項目。2. Freemarker 在 SpringBoot 中的加載機制與路徑解析邏輯深度拆解SpringBoot 并非簡單地將.ftl文件當作純文本讀取而是通過FreeMarkerViewResolver構建完整的視圖解析鏈路。理解其加載順序和路徑映射規(guī)則是避免TemplateNotFoundException和靜態(tài)資源錯位的根本前提。2.1 模板加載器TemplateLoader的三級查找路徑Freemarker 的TemplateLoader實際由SpringTemplateLoader封裝其查找邏輯遵循classpath → file → URL優(yōu)先級。但在 SpringBoot 中默認僅啟用ClassTemplateLoader即只從 classpath 下加載。關鍵在于spring.freemarker.template-loader-path的值如何影響TemplateLoader初始化若配置為classpath:/templates/推薦則FreeMarkerConfigurer會創(chuàng)建ClassTemplateLoader根路徑為classpath:/templates/若配置為file:/opt/app/templates/則啟用FileTemplateLoader此時需確保應用有對應目錄讀取權限且該路徑不參與 jar 包打包若未顯式配置template-loader-pathSpringBoot 2.3 會 fallback 到classpath:/templates/但 SpringBoot 2.2 及更早版本 fallback 為classpath:/極易導致模板被誤加載到static/或public/目錄下而失敗提示application.yml中的配置必須嚴格匹配路徑語義。例如spring: freemarker: template-loader-path: classpath:/templates/ suffix: .ftl content-type: text/html charset: UTF-8注意template-loader-path末尾的/不可省略否則FreeMarkerViewResolver會將index解析為classpath:/templatesindex而非classpath:/templates/index.ftl2.2 視圖解析器ViewResolver的命名匹配規(guī)則與前綴/后綴作用域FreeMarkerViewResolver的prefix和suffix并非字符串拼接那么簡單而是參與View實例構建的元數(shù)據(jù)。其解析流程如下Controller 返回邏輯視圖名indexFreeMarkerViewResolver調用getPrefix() viewName getSuffix()得到模板路徑index.ftlTemplateLoader根據(jù)template-loader-path查找classpath:/templates/index.ftl若找到返回FreeMarkerView實例若未找到拋出TemplateNotFoundException這里的關鍵陷阱在于prefix是路徑前綴不是文件名前綴。例如spring: freemarker: prefix: admin/ template-loader-path: classpath:/templates/則index會被解析為classpath:/templates/admin/index.ftl而非classpath:/templates/index.ftl。項目中提供的bootstrap-datetimepicker.css等資源若放在templates/下會導致 CSS 路徑錯誤必須明確區(qū)分模板文件放templates/靜態(tài)資源放static/。2.3 靜態(tài)資源與 Freemarker 模板的協(xié)同加載機制項目壓縮包中包含bootstrap.css、animate.css等 9 個 CSS 文件它們絕不能放在templates/目錄下。SpringBoot 的ResourceHttpRequestHandler默認將classpath:/static/、classpath:/public/、classpath:/resources/、classpath:/META-INF/resources/映射為/路徑。因此正確組織方式為src/main/resources/ ├── templates/ │ └── index.ftl ← Freemarker 模板 └── static/ ├── css/ │ ├── bootstrap.css │ ├── animate.css │ └── datepicker3.css └── js/ └── chosen.js在index.ftl中引用方式必須為絕對路徑link relstylesheet href/css/bootstrap.css link relstylesheet href/css/animate.css script src/js/chosen.js/script注意Freemarker 模板中不能使用th:href{/css/bootstrap.css}Thymeleaf 語法也不能用contextPathJSP 語義。SpringBoot 的靜態(tài)資源映射是 Servlet 容器級行為與模板引擎無關直接/開頭即可。2.4 字符編碼與 Content-Type 的雙重校驗鏈spring.freemarker.charsetUTF-8僅控制 Freemarker 引擎讀取.ftl文件時的解碼方式而spring.freemarker.content-typetext/html決定 HTTP 響應頭Content-Type。二者必須一致否則瀏覽器可能因 BOM 或編碼聲明沖突導致中文亂碼。驗證方法啟動應用后訪問/index用瀏覽器開發(fā)者工具查看 Network → Response Headers →Content-Type是否為text/html;charsetUTF-8再檢查index.ftl文件屬性是否為 UTF-8 無 BOM 編碼。實際操作中常因 IDE 默認保存為 GBK 導致模板內中文顯示為??。解決方案IntelliJ IDEAFile → Settings → Editor → File Encodings設置Global Encoding和Project Encoding均為 UTF-8勾選Transparent native-to-ascii conversionMaven 編譯插件強制編碼plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-compiler-plugin/artifactId configuration encodingUTF-8/encoding /configuration /plugin3. Freemarker 模板語法在 SpringBoot 上的實戰(zhàn)約束與安全邊界Freemarker 語法強大但在 SpringBoot 環(huán)境中并非所有特性都開箱即用。項目中sweetalert.css和chosen.css的存在暗示了需要在模板中嵌入 JS 交互邏輯這直接觸發(fā) Freemarker 的表達式求值邊界、HTML 轉義策略、以及 Model 數(shù)據(jù)序列化限制。3.1${}表達式求值的三層上下文與空值處理SpringBoot 默認啟用 Freemarker 的classic_compatible模式SpringBoot 2.2這意味著${user.name}在user為 null 時會拋出NullPointerException而非返回空字符串。這是與老版本 Freemarker 的關鍵差異。必須顯式使用!操作符處理空值!-- 安全寫法 -- p用戶名${user.name!匿名用戶}/p p郵箱${user.email!未填寫}/p !-- 危險寫法可能 500 錯誤 -- p用戶名${user.name}/p更進一步SpringBoot 的FreeMarkerView會對 Model 中的java.util.Date、java.time.LocalDateTime等類型自動注冊DefaultObjectWrapper但不會自動注冊java.time.format.DateTimeFormatter。因此${now?string(yyyy-MM-dd HH:mm:ss)}要求now必須是Date或Calendar類型若傳入LocalDateTime會報freemarker.core.NonHashException。解決方案Controller 層統(tǒng)一轉換GetMapping(/dashboard) public String dashboard(Model model) { model.addAttribute(now, Date.from(Instant.now())); // 轉為 Date model.addAttribute(items, Arrays.asList(A, B, C)); return dashboard; }3.2#list遍歷中的集合判空與分頁控制項目含chosen.css典型用于多選下拉組件意味著模板中需渲染selectoption列表。Freemarker 的#list要求集合非 null否則報錯。常見錯誤寫法#list users as user option value${user.id}${user.name}/option /#list當users為null時崩潰。正確寫法必須結合??判空與!默認值#if users?? users?size 0 select classchosen-select #list users as user option value${user.id!}${user.name!未知}/option /#list /select #else p暫無用戶數(shù)據(jù)/p /#if注意users?size是 Freemarker 內置函數(shù)但users.size()是 Java 方法調用在 SpringBoot 默認配置下被禁用出于安全考慮。若需啟用方法調用必須在application.yml中顯式配置spring: freemarker: settings: classic_compatible: false object_wrapper: freemarker.ext.beans.BeansWrapper3.3 自定義指令Directive的注冊與 SpringBean 注入限制datepicker3.css對應日期選擇器常需封裝為datePicker idstart /形式。Freemarker 自定義指令需實現(xiàn)TemplateDirectiveModel接口但無法直接注入 Spring Bean因為指令實例由 Freemarker 引擎創(chuàng)建不受 Spring IoC 管理。標準做法是在FreeMarkerConfigurerBean 中注冊指令并通過Configuration獲取 Spring 上下文Configuration public class FreemarkerConfig { Autowired private ApplicationContext applicationContext; Bean public FreeMarkerConfigurer freeMarkerConfigurer() { FreeMarkerConfigurer configurer new FreeMarkerConfigurer(); configurer.setTemplateLoaderPath(classpath:/templates/); configurer.setFreemarkerSettings(Collections.singletonMap( shared_variables, Collections.singletonMap(datePicker, new DatePickerDirective(applicationContext)) )); return configurer; } }DatePickerDirective構造器接收ApplicationContext在execute()方法中通過applicationContext.getBean()獲取 Servicepublic class DatePickerDirective implements TemplateDirectiveModel { private final ApplicationContext context; public DatePickerDirective(ApplicationContext context) { this.context context; } Override public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body) throws TemplateException, IOException { // 從 Spring 容器獲取 service DateService dateService context.getBean(DateService.class); String html dateService.generatePickerHtml((String) params.get(id)); env.getOut().write(html); } }3.4 模板緩存策略與開發(fā)/生產環(huán)境差異化配置項目未提供application-dev.yml/application-prod.yml但必須明確Freemarker 默認開啟緩存spring.freemarker.cachetrue這在開發(fā)階段會導致修改.ftl后必須重啟應用才能生效。而生產環(huán)境必須開啟緩存以提升性能。正確配置方式# application-dev.yml spring: freemarker: cache: false settings: template_update_delay: 0s # 立即檢測模板變更 # application-prod.yml spring: freemarker: cache: true settings: template_update_delay: 3600s # 1小時檢查一次 number_format: 0.########## # 避免科學計數(shù)法驗證緩存是否生效啟動應用后修改index.ftl內容刷新頁面。若內容未變則緩存生效若立即變化則cachefalse生效。4. SpringBoot Freemarker 工程化落地的 5 個硬性檢查清單一個可交付的 Freemarker 視圖工程不能只滿足“能跑”而要通過以下 5 項硬性檢查。每項失敗都會導致線上故障或維護成本飆升。4.1 模板路徑合法性校驗防TemplateNotFoundExceptionSpringBoot 2.3 對template-loader-path做了嚴格校驗路徑必須以classpath:或file:開頭且不能包含..路徑穿越。執(zhí)行以下命令驗證# 打包后檢查 jar 包內 templates 目錄結構 jar -tf target/springboot-freemarker-master.jar | grep templates/ # 輸出應包含templates/index.ftl、templates/admin/user.ftl 等若輸出為空說明maven-resources-plugin未將src/main/resources/templates/復制進 jar。檢查pom.xml是否遺漏build resources resource directorysrc/main/resources/directory includes include**/*.ftl/include include**/*.properties/include /includes /resource /resources /build4.2 靜態(tài)資源 HTTP 狀態(tài)碼驗證防 404使用 curl 直接測試 CSS/JS 資源是否可訪問curl -I http://localhost:8080/css/bootstrap.css # 正確響應應為 # HTTP/1.1 200 OK # Content-Type: text/css # Content-Length: 198720 curl -I http://localhost:8080/css/missing.css # 正確響應應為 # HTTP/1.1 404 Not Found若返回404但路徑確認存在檢查spring.web.resources.static-locations是否被覆蓋# 錯誤配置會覆蓋默認值 spring: web: resources: static-locations: classpath:/custom-static/ # 正確配置追加而非覆蓋 spring: web: resources: static-locations: classpath:/static/,classpath:/public/,classpath:/resources/4.3 Freemarker 表達式安全沙箱驗證防 XSSFreemarker 默認對${}輸出做 HTML 轉義但#escape x as x?html塊內可關閉轉義。項目含sweetalert.css常配合 JS 彈窗需確保用戶輸入不被直接?no_esc渲染!-- 危險用戶可控內容未轉義 -- ${userInput?no_esc} !-- 安全默認已轉義無需額外操作 -- ${userInput}驗證方法在 Controller 中傳入scriptalert(1)/script觀察頁面源碼是否被轉義為lt;scriptgt;alert(1)lt;/scriptgt;。若未轉義檢查spring.freemarker.settings是否誤設output_formatHTML應為HTMLOutputFormat實例。4.4 日期/數(shù)字格式化全局一致性檢查項目含datepicker3.css和bootstrap-datetimepicker.css說明存在大量時間展示場景。必須統(tǒng)一?string格式避免不同模板用不同格式如yyyy-MM-ddvsyyyy/MM/dd。在application.yml中配置全局格式spring: freemarker: settings: datetime_format: yyyy-MM-dd HH:mm:ss date_format: yyyy-MM-dd time_format: HH:mm:ss number_format: 0.00然后在模板中直接使用${order.createTime?string} ← 輸出2024-05-20 14:30:22 ${order.amount?string} ← 輸出123.454.5 Freemarker 版本兼容性矩陣驗證spring-boot-starter-freemarker的版本與底層 Freemarker 引擎強綁定。SpringBoot 2.7.x 使用 Freemarker 2.3.31而 SpringBoot 3.2.x 使用 Freemarker 2.3.32。若手動升級 Freemarker 版本可能觸發(fā)TemplateException: Unknown directive如新版本支持#ftl ...指令舊版不識別。驗證當前版本mvn dependency:tree | grep freemarker # 輸出示例[INFO] - org.springframework.boot:spring-boot-starter-freemarker:jar:2.7.18:compile # [INFO] | \- org.freemarker:freemarker:jar:2.3.31:compile若需降級如適配老項目必須同步調整dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-freemarker/artifactId exclusions exclusion groupIdorg.freemarker/groupId artifactIdfreemarker/artifactId /exclusion /exclusions /dependency dependency groupIdorg.freemarker/groupId artifactIdfreemarker/artifactId version2.3.28/version !-- 與 SpringBoot 2.3.x 兼容 -- /dependency5. 基于springboot-freemarker-master.rar的快速初始化腳手架構建拿到springboot-freemarker-master.rar后不要直接解壓覆蓋現(xiàn)有項目。應將其作為標準化腳手架按以下步驟初始化新工程確保結構清晰、職責分離、可維護性強。5.1 資源目錄標準化遷移流程解壓springboot-freemarker-master.rar提取 CSS/JS 文件按 SpringBoot 規(guī)范重建目錄# 創(chuàng)建標準目錄結構 mkdir -p src/main/resources/templates mkdir -p src/main/resources/static/css mkdir -p src/main/resources/static/js # 遷移 CSS保留原始文件名不重命名 cp style.css bootstrap.css bootstrap.min.css animate.css \ datepicker3.css font-awesome.css sweetalert.css \ bootstrap-datetimepicker.css bootstrap-datetimepicker.min.css \ src/main/resources/static/css/ # 遷移 JS項目未提供 JS但 chosen.css 需配套 chosen.js wget https://cdnjs.cloudflare.com/ajax/libs/chosen/1.9.1/chosen.jquery.min.js \ -O src/main/resources/static/js/chosen.jquery.min.js5.2application.yml最小化安全配置模板基于項目需求生成生產就緒的application.ymlspring: profiles: active: prod freemarker: template-loader-path: classpath:/templates/ suffix: .ftl content-type: text/html charset: UTF-8 cache: true request-context-attribute: request expose-spring-macro-helpers: true settings: template_update_delay: 3600s datetime_format: yyyy-MM-dd HH:mm:ss date_format: yyyy-MM-dd time_format: HH:mm:ss number_format: 0.00 output_format: HTMLOutputFormat api_builtin_enabled: false # 禁用危險內置函數(shù) web: resources: add-mappings: true cache: period: 3600 chain: gzip: true # 生產環(huán)境強制關閉 devtools spring.devtools.restart.enabled: false management.endpoints.web.exposure.include: health,info,metrics5.3index.ftl基礎骨架與資源加載驗證模板創(chuàng)建src/main/resources/templates/index.ftl集成所有 CSS 并驗證加載!DOCTYPE html html langzh-CN head meta charsetUTF-8 titleFreemarker 主頁/title !-- Bootstrap 核心 CSS -- link relstylesheet href/css/bootstrap.min.css !-- 動畫支持 -- link relstylesheet href/css/animate.css !-- 日期選擇器 -- link relstylesheet href/css/datepicker3.css link relstylesheet href/css/bootstrap-datetimepicker.min.css !-- 圖標字體 -- link relstylesheet href/css/font-awesome.css !-- SweetAlert 彈窗 -- link relstylesheet href/css/sweetalert.css !-- Chosen 下拉增強 -- link relstylesheet href/css/chosen.css /head body classanimated fadeIn div classcontainer mt-5 h1SpringBoot Freemarker 已就緒/h1 p當前時間strong${.now?string(yyyy-MM-dd HH:mm:ss)}/strong/p !-- 驗證 Chosen 初始化 -- select classform-control chosen-select>// Application.java SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }// IndexController.java Controller public class IndexController { GetMapping(/) public String home(Model model) { model.addAttribute(now, new Date()); return index; // 自動匹配 templates/index.ftl } }啟動應用后訪問http://localhost:8080/若頁面正常顯示、動畫生效、Chosen 下拉框可展開、瀏覽器控制臺無 404 報錯則腳手架構建成功。此時可基于此結構按業(yè)務模塊在templates/下創(chuàng)建admin/、user/子目錄實現(xiàn)視圖分層。本文還有配套的精品資源點擊獲取