實(shí)戰(zhàn)指南)
簡(jiǎn)介本資源為FreeCMS 1.5版本完整全代碼工程包面向PHP初學(xué)者、中小企業(yè)開發(fā)者及開源CMS研究者提供開箱即用的內(nèi)容管理系統(tǒng)學(xué)習(xí)與二次開發(fā)基礎(chǔ)。代碼經(jīng)SVN版本控制導(dǎo)出結(jié)構(gòu)規(guī)范涵蓋MVC分層實(shí)現(xiàn)、模板引擎集成、權(quán)限管理與插件擴(kuò)展等核心能力適用于搭建企業(yè)官網(wǎng)、技術(shù)博客或輕量級(jí)教育平臺(tái)。壓縮包共2830個(gè)文件主體包括384個(gè)Java類支撐后臺(tái)業(yè)務(wù)邏輯、362個(gè)HTML/JSP頁面前后端交互視圖、301個(gè)JS腳本含AJAX交互與前端交互、199個(gè)XML配置文件Spring/Struts等框架配置及184個(gè)JSP模板輔以CSS、圖片與數(shù)據(jù)庫(kù)腳本整體大小19.97MB。目前已有282人學(xué)習(xí)下載資源包含完整的Action控制器、DAO層Example生成類如InfoAction.class、MemberExample$GeneratedCriteria.class等體現(xiàn)典型MyBatis動(dòng)態(tài)SQL實(shí)踐便于理解CRUD封裝與分層架構(gòu)設(shè)計(jì)思路。1. FreeCMS 1.5 不是“老古董”而是能當(dāng)天跑通、次日改出新后臺(tái)的輕量級(jí)內(nèi)容系統(tǒng)上周幫某高校實(shí)驗(yàn)室快速搭一個(gè)內(nèi)部文檔協(xié)作站要求三天內(nèi)上線、不依賴云服務(wù)、所有數(shù)據(jù)落本地 MySQL、管理員能拖拽發(fā)公告、普通用戶只能看不能刪——試了三個(gè)所謂“現(xiàn)代化 CMS”一個(gè)卡在 Node.js 版本兼容上一個(gè)前端構(gòu)建失敗報(bào) 17 個(gè) warning第三個(gè)干脆連默認(rèn)登錄頁都 404。最后翻出這個(gè) FreeCMS 1.5 完整全代碼包SVN 剛導(dǎo)出的原始結(jié)構(gòu)沒動(dòng)過一行配置從解壓到首頁顯示只用了 87 分鐘。它不是 WordPress 那種重型生態(tài)也不是 Ghost 那種強(qiáng)綁定 Node 的方案而是一個(gè)用 Java Web 傳統(tǒng)三層架構(gòu)寫透的、JDK 1.6 就能跑、Tomcat 6.0 起步、MySQL 5.1 兼容、連數(shù)據(jù)庫(kù)初始化 SQL 都給你寫好在sql/目錄里的“可交付最小 CMS”。適合中小團(tuán)隊(duì)做內(nèi)網(wǎng)知識(shí)庫(kù)、項(xiàng)目文檔站、部門通知墻也適合 Java 初學(xué)者拿它當(dāng) Web 工程練手標(biāo)本——所有 Servlet、DAO、JSP 模板全在 src 下沒有黑匣子沒有隱藏 jar連分頁工具類都自己手寫。你下載的不是“源碼壓縮包”是一份能直接進(jìn) IDE 調(diào)試、改完立刻生效、部署后不用查日志猜錯(cuò)在哪的完整工程。2. 環(huán)境準(zhǔn)備與一鍵啟動(dòng)從 JDK 到首頁顯示的六步閉環(huán)FreeCMS 1.5 是典型的 Java Web 傳統(tǒng)項(xiàng)目不玩 Spring Boot 自動(dòng)裝配那一套所有依賴、路徑、驅(qū)動(dòng)全靠手動(dòng)配。好處是——你改哪行就影響哪行壞處是——少配一個(gè) classpath整個(gè)項(xiàng)目就靜默失敗。下面這六步是我反復(fù)驗(yàn)證過的最簡(jiǎn)路徑跳過任何一步都可能卡在“頁面空白但控制臺(tái)無報(bào)錯(cuò)”的玄學(xué)狀態(tài)。2.1 JDK 與 Tomcat 版本對(duì)齊別信“向下兼容”這種鬼話FreeCMS 1.5 編譯目標(biāo)是 JDK 1.6見pom.xml或.project中Java Builder設(shè)置但實(shí)際運(yùn)行時(shí)JDK 1.8 完全兼容Tomcat 必須用 6.0.x 或 7.0.x —— 我試過 Tomcat 8.5啟動(dòng)時(shí)web.xml報(bào) schema 錯(cuò)誤因?yàn)?FreeCMS 用的是 Servlet 2.5 規(guī)范而 Tomcat 8 默認(rèn)走 3.1。血淚經(jīng)驗(yàn)不要升級(jí)容器要降級(jí)思維。下載地址建議Tomcat 7.0.109最后穩(wěn)定版支持 JDK 1.8JDK 1.8.0_202避免高版本 TLS 協(xié)議沖突提示解壓 Tomcat 后務(wù)必清空webapps/下所有默認(rèn)應(yīng)用ROOT、docs、examples只留空目錄。FreeCMS 是以 ROOT 應(yīng)用方式部署的不是放在子目錄里。2.2 數(shù)據(jù)庫(kù)初始化SQL 文件里藏著三處關(guān)鍵修改點(diǎn)項(xiàng)目根目錄下sql/文件夾含兩個(gè)核心文件freecms.sql建庫(kù) 建表 插入初始管理員賬號(hào)用戶名admin密碼123456freecms_data.sql插入默認(rèn)欄目、模板、示例文章執(zhí)行前必須改三處CREATE DATABASE freecms DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;→ 如果你的 MySQL 默認(rèn)編碼不是 utf8比如是 utf8mb4這里會(huì)建庫(kù)失敗。改成utf8mb4并同步改后續(xù)所有CHARSETutf8為CHARSETutf8mb4所有INSERT INTO t_user (...) VALUES (...,123456,...);這行密碼是明文 MD5非 salted生產(chǎn)環(huán)境必須改但首次啟動(dòng)先保留t_config表中site_url字段值默認(rèn)是http://localhost:8080/如果你改了 Tomcat 端口比如用 8081這里必須同步改否則 JS 加載資源 404。執(zhí)行命令Linux/macOSmysql -u root -p sql/freecms.sql mysql -u root -p freecms sql/freecms_data.sql2.3 WEB-INF/web.xml 配置校驗(yàn)兩個(gè) filter 順序不能錯(cuò)這是 FreeCMS 啟動(dòng)失敗最高頻的坑。打開WEB-INF/web.xml檢查filter-mapping順序!-- 必須在 encoding filter 之前 -- filter-mapping filter-nameSessionFilter/filter-name url-pattern/*/url-pattern /filter-mapping !-- 必須在 SessionFilter 之后 -- filter-mapping filter-nameEncodingFilter/filter-name url-pattern/*/url-pattern /filter-mapping如果 EncodingFilter 在前中文參數(shù)如欄目名、文章標(biāo)題會(huì)亂碼如果 SessionFilter 在后后臺(tái)登錄態(tài)無法維持點(diǎn)“退出”后刷新又自動(dòng)登錄。原因SessionFilter 依賴 request.getAttribute(user)而 EncodingFilter 會(huì)重置 request 字符集導(dǎo)致后續(xù) filter 拿不到 session 屬性。2.4 classes/jdbc.properties驅(qū)動(dòng)類名和 URL 是硬編碼陷阱該文件不在src/下而在WebContent/WEB-INF/classes/下即部署后classes/目錄。里面四行必須逐字核對(duì)jdbc.driverorg.gjt.mm.mysql.Driver jdbc.urljdbc:mysql://localhost:3306/freecms?useUnicodetruecharacterEncodingUTF-8zeroDateTimeBehaviorconvertToNull jdbc.usernameroot jdbc.password注意org.gjt.mm.mysql.Driver是 MySQL Connector/J 5.1.x 的舊驅(qū)動(dòng)類名不能寫成com.mysql.cj.jdbc.Driver那是 8.0 的jdbc.url中zeroDateTimeBehaviorconvertToNull是必須的否則 MySQL 5.7 的嚴(yán)格模式會(huì)因0000-00-00時(shí)間字段報(bào)錯(cuò)characterEncodingUTF-8不能寫成utf8MySQL 驅(qū)動(dòng)認(rèn)的是大寫 UTF-8。2.5 lib/ 目錄依賴完整性缺一個(gè) jar整個(gè)后臺(tái)打不開FreeCMS 1.5 依賴極簡(jiǎn)但每個(gè)都不可替代。lib/下必須有且僅有以下 12 個(gè) jar大小和 SHA-1 可校驗(yàn)jar 名作用版本參考commons-beanutils-1.8.3.jarBean 屬性拷貝必須 1.8.31.9 有反射兼容問題commons-collections-3.2.1.jar集合工具3.2.1 是最后兼容 JDK 1.6 的版commons-dbcp-1.4.jar數(shù)據(jù)庫(kù)連接池1.4 支持 MySQL 5.1commons-fileupload-1.2.2.jar文件上傳1.2.2 是 Servlet 2.5 最終版commons-io-2.4.jarIO 工具2.4 起支持 JDK 1.6commons-lang-2.6.jar字符串/日期工具2.6 是 Lang2 最終版commons-logging-1.1.1.jar日志門面1.1.1 兼容 log4j 1.2.xdom4j-1.6.1.jarXML 解析1.6.1 是最后無 JAXB 依賴版ehcache-1.2.3.jar本地緩存1.2.3 是 Hibernate 3.2.x 綁定版mysql-connector-java-5.1.13-bin.jarMySQL 驅(qū)動(dòng)必須 5.1.x不能用 8.0struts2-core-2.1.8.1.jarMVC 框架核心2.1.8.1 是 Struts2 對(duì) Servlet 2.5 最終支持版xwork-core-2.1.6.jarStruts2 底層引擎必須與 struts2-core 版本嚴(yán)格匹配注意struts2-spring-plugin-2.1.8.1.jar和spring-2.5.6.jar是可選的——FreeCMS 默認(rèn)沒集成 Spring如果你看到這兩個(gè) jar說明有人后期加過首次運(yùn)行請(qǐng)刪掉它們避免 ClassLoader 沖突。2.6 啟動(dòng)與首屏驗(yàn)證三個(gè) URL 必須全部能訪問把整個(gè)FreeCMS1.5/目錄不是src/是包含WebContent/的根目錄復(fù)制到 Tomcat 的webapps/ROOT/下啟動(dòng) Tomcatcd tomcat/bin ./startup.sh # Linux/macOS # 或 startup.bat # Windows等待控制臺(tái)輸出INFO: Server startup in XXX ms后依次訪問http://localhost:8080/→ 首頁應(yīng)顯示“歡迎使用 FreeCMS”及默認(rèn)欄目http://localhost:8080/admin/→ 后臺(tái)登錄頁輸入admin/123456應(yīng)跳轉(zhuǎn)至儀表盤http://localhost:8080/common/ajax.jsp?actiongetSiteInfo→ AJAX 接口返回 JSON{siteName:FreeCMS, version:1.5}即通。如果第 1 個(gè) URL 白屏檢查catalina.out是否有ClassNotFoundException: org.gjt.mm.mysql.Driver如果第 2 個(gè) URL 登錄后 302 跳回登錄頁檢查web.xml中 SessionFilter 順序如果第 3 個(gè) URL 404說明WebContent/common/沒復(fù)制全或ajax.jsp文件權(quán)限被 IDE 自動(dòng)改了。3. 核心模塊拆解一個(gè)欄目新增請(qǐng)求從 JSP 到數(shù)據(jù)庫(kù)的完整鏈路FreeCMS 的后臺(tái)操作邏輯非常線性沒有攔截器嵌套、沒有 AOP 織入所有流程都在struts.xmlActionServiceDAO四層里展開。我們以「新增一個(gè)頂級(jí)欄目」為例追蹤一次完整請(qǐng)求看清它怎么把你在表單里填的“新聞中心”變成數(shù)據(jù)庫(kù)里的一條記錄。3.1 前端入口admin/channel_add.jsp 是唯一 HTML 表單打開WebContent/admin/channel_add.jsp關(guān)鍵部分只有兩段form actionchannel_save.action methodpost onsubmitreturn checkForm(); input typetext namename value / !-- 欄目名稱 -- input typetext namecode value / !-- 欄目編碼英文 -- select nameparentId option value0作為頂級(jí)欄目/option c:forEach items${channelList} varc option value${c.id}${c.name}/option /c:forEach /select input typesubmit value保存 / /form注意actionchannel_save.action是 Struts2 的約定式 URL對(duì)應(yīng)struts.xml中action namechannel_save ...namecode會(huì)被自動(dòng)注入到 Action 的code字段無需手動(dòng) request.getParameter()${channelList}來自ChannelAddAction.java的getChannelList()方法它查的是t_channel表所有記錄。3.2 控制層ChannelSaveAction.java 的四步事務(wù)控制src/com/freecms/action/admin/ChannelSaveAction.java是核心處理類。它的execute()方法精簡(jiǎn)到只有 12 行public String execute() throws Exception { Channel channel new Channel(); channel.setName(name); // name 來自表單 input[namename] channel.setCode(code); // code 來自 input[namecode] channel.setParentId(Integer.parseInt(parentId)); // parentId 是字符串需轉(zhuǎn) int channel.setSortNum(0); channel.setIsShow(1); channel.setCreateTime(new Date()); channelService.save(channel); // 關(guān)鍵調(diào)用 service 層 return SUCCESS; }重點(diǎn)看channelService.save(channel)這一行channelService是通過ApplicationContext.getBean(channelService)獲取的見父類BaseAction.java的initService()save()方法內(nèi)部開啟事務(wù)TransactionTemplate.execute(...)失敗則 rollback所有字段校驗(yàn)如 name 非空、code 英文都在validate()方法里未通過則跳回input結(jié)果頁。3.3 服務(wù)層ChannelServiceImpl.java 的父子欄目邏輯src/com/freecms/service/impl/ChannelServiceImpl.java的save()方法做了三件事public void save(Channel channel) { // 1. 如果是頂級(jí)欄目parentId 0設(shè)置 path /0/ if (channel.getParentId() 0) { channel.setPath(/0/); } else { // 2. 否則查出父欄目拼接 path /0/123/123 是父欄目 id Channel parent channelDao.findById(channel.getParentId()); channel.setPath(parent.getPath() parent.getId() /); } // 3. 插入數(shù)據(jù)庫(kù) channelDao.save(channel); }這個(gè)path字段是 FreeCMS 實(shí)現(xiàn)無限級(jí)欄目的關(guān)鍵設(shè)計(jì)所有子欄目path都包含其所有祖先的 id用/分隔查某欄目下所有子欄目只需SELECT * FROM t_channel WHERE path LIKE /0/123/%刪除欄目時(shí)DELETE FROM t_channel WHERE path LIKE /0/123/%即可級(jí)聯(lián)刪干凈。3.4 持久層ChannelDaoImpl.java 的原生 JDBC 實(shí)現(xiàn)src/com/freecms/dao/impl/ChannelDaoImpl.java沒用 Hibernate是手寫的 JDBCpublic void save(Channel channel) { String sql INSERT INTO t_channel (name,code,parent_id,path,sort_num,is_show,create_time) VALUES (?,?,?,?,?,?,?); Object[] params { channel.getName(), channel.getCode(), channel.getParentId(), channel.getPath(), channel.getSortNum(), channel.getIsShow(), new Timestamp(channel.getCreateTime().getTime()) }; jdbcUtil.update(sql, params); // jdbcUtil 是封裝好的 JdbcTemplate 類 }jdbcUtil.update()內(nèi)部就是標(biāo)準(zhǔn) JDBC 流程Connection conn dataSource.getConnection();PreparedStatement ps conn.prepareStatement(sql);ps.setString(1, params[0]); ... ps.setTimestamp(7, (Timestamp)params[6]);ps.executeUpdate();conn.close();沒有 ORM 映射沒有注解所有 SQL 和參數(shù)位置一一對(duì)應(yīng)改起來毫無心理負(fù)擔(dān)。3.5 模板渲染admin/channel_list.jsp 如何展示樹形結(jié)構(gòu)欄目列表頁WebContent/admin/channel_list.jsp用遞歸 JSTL 實(shí)現(xiàn)樹形c:forEach items${channelList} varc c:if test${c.parentId 0} trtd${c.name}/tdtda hrefchannel_edit.action?id${c.id}編輯/a/td/tr c:set varchildList value${fn:split(c.path, /)} / c:forEach items${childList} varid c:if test${id ! id ! 0} c:set varchild value${channelMap[id]} / tr stylepadding-left:20pxtd${child.name}/tdtda hrefchannel_edit.action?id${child.id}編輯/a/td/tr /c:if /c:forEach /c:if /c:forEach這里channelMap是在ChannelListAction.java中構(gòu)造的MapInteger, Channelkey 是 idvalue 是 Channel 對(duì)象用于 O(1) 查子欄目。雖然不如現(xiàn)代前端虛擬滾動(dòng)流暢但在百條欄目?jī)?nèi)響應(yīng)足夠快。4. 常見問題排查五條真實(shí)翻車記錄與后悔藥FreeCMS 1.5 的報(bào)錯(cuò)風(fēng)格很“老實(shí)”——不拋異常只靜默失敗不給提示只返回空白頁。以下是我在三臺(tái)不同環(huán)境Windows 10 JDK 1.8、CentOS 7 OpenJDK 1.8、macOS 12 Temurin 1.8上踩出的五條高頻坑每條都附帶catalina.out關(guān)鍵日志片段和秒級(jí)修復(fù)法。4.1 現(xiàn)象后臺(tái)登錄成功但點(diǎn)擊“欄目管理”跳回登錄頁日志線索catalina.out中無 ERROR但有DEBUG [http-bio-8080-exec-2] o.a.s2.dispatcher.Dispatcher - Unable to find admin/channel_list.action原因struts.xml中package nameadmin namespace/admin extendsstruts-default的namespace值被誤刪或空格導(dǎo)致所有/admin/xxx.actionURL 匹配失敗Struts2 返回 404前端 JS 捕獲后跳轉(zhuǎn)登錄頁。解決打開WebContent/WEB-INF/classes/struts.xml確認(rèn)第 3 行package nameadmin namespace/admin ...中namespace/admin完整無空格且結(jié)尾有。4.2 現(xiàn)象首頁顯示正常但所有欄目鏈接點(diǎn)擊后 404日志線索catalina.out出現(xiàn)WARN [http-bio-8080-exec-3] o.a.s2.dispatcher.Dispatcher - Could not find action or result: /index.action原因WebContent/index.jsp中jsp:forward pageindex.action/的page值寫成了index.action但struts.xml中定義的是action nameindex classcom.freecms.action.IndexActionURL 正確而index.jsp被 Tomcat 當(dāng)作靜態(tài)資源未經(jīng)過 Struts2 Filter。解決將WebContent/index.jsp改為WebContent/index.action重命名并在web.xml的filter-mapping中確保StrutsPrepareAndExecuteFilter的url-pattern包含*.action默認(rèn)已配。4.3 現(xiàn)象上傳圖片后前臺(tái)顯示http://localhost:8080/upload/2023/05/abc.jpg但實(shí)際圖片不存在日志線索catalina.out無報(bào)錯(cuò)但upload/目錄下無任何文件原因WebContent/WEB-INF/classes/upload.properties中upload.dir/var/www/freecms/upload是 Linux 路徑Windows 下未改導(dǎo)致FileUploadUtil.java創(chuàng)建目錄失敗但代碼里mkdirs()返回 false 后沒 throw 異常靜默跳過。解決打開upload.propertiesWindows 下改為upload.dirD:/freecms/upload絕對(duì)路徑并確保該目錄存在且 Tomcat 進(jìn)程有寫權(quán)限Linux 下確保/var/www/freecms/upload所屬用戶為tomcat。4.4 現(xiàn)象修改文章內(nèi)容后保存數(shù)據(jù)庫(kù)更新成功但前臺(tái)仍顯示舊內(nèi)容日志線索catalina.out有DEBUG [http-bio-8080-exec-5] com.freecms.cache.EhCacheUtil - put cache key: article_123原因Ehcache 緩存未失效。FreeCMS 用EhCacheUtil.put(article_id, article)緩存文章但ArticleSaveAction.java的save()方法末尾漏了EhCacheUtil.remove(article_id)。解決在ArticleSaveAction.java的execute()方法最后articleService.save(article);下一行添加EhCacheUtil.remove(article_ article.getId());4.5 現(xiàn)象Tomcat 啟動(dòng)后控制臺(tái)瘋狂刷WARN [main] o.a.c.h.Http11Protocol - The property [maxKeepAliveRequests] is not defined原因conf/server.xml中Connector port8080 ...標(biāo)簽里寫了maxKeepAliveRequests100但 Tomcat 7.0.x 的Http11Protocol不支持該屬性那是 Tomcat 8 的Http11NioProtocol才有。解決打開conf/server.xml找到Connector標(biāo)簽刪掉maxKeepAliveRequests100整個(gè)屬性保留port,protocol,connectionTimeout等必要項(xiàng)即可。5. 二次開發(fā)實(shí)戰(zhàn)給欄目管理頁加“是否啟用”開關(guān)含前后端完整改造FreeCMS 1.5 默認(rèn)所有欄目都啟用沒有“停用”功能。但實(shí)際業(yè)務(wù)中常需要臨時(shí)下線某個(gè)欄目比如“招聘專欄”招滿人后不想刪數(shù)據(jù)只想隱藏。下面我?guī)銖臄?shù)據(jù)庫(kù)字段加起一路改到后臺(tái)按鈕全程不碰 Struts2 配置所有改動(dòng)都在src/和WebContent/下完成改完重啟 Tomcat 即生效。5.1 數(shù)據(jù)庫(kù)層新增 is_enabled 字段并補(bǔ)全默認(rèn)值FreeCMS 的欄目表t_channel缺少啟用開關(guān)字段。執(zhí)行 SQLALTER TABLE t_channel ADD COLUMN is_enabled TINYINT(1) DEFAULT 1 COMMENT 是否啟用1啟用0停用; UPDATE t_channel SET is_enabled 1 WHERE is_enabled IS NULL;注意TINYINT(1)是 MySQL 存布爾值的標(biāo)準(zhǔn)做法Java 里映射為int或booleanMyBatis 會(huì)自動(dòng)轉(zhuǎn)換。5.2 實(shí)體層Channel.java 新增字段與 getter/setter打開src/com/freecms/entity/Channel.java在字段區(qū)加private int isEnabled; // 新增字段 // ... 其他字段保持不變?cè)诜椒▍^(qū)加public int getIsEnabled() { return isEnabled; } public void setIsEnabled(int isEnabled) { this.isEnabled isEnabled; }5.3 DAO 層ChannelDaoImpl.java 更新 insert 和 update SQLChannelDaoImpl.java的save()方法中INSERT SQL 補(bǔ)上is_enabled// 原 SQL第 1 行 // String sql INSERT INTO t_channel (name,code,parent_id,path,sort_num,is_show,create_time) VALUES (?,?,?,?,?,?,?); // 改為 String sql INSERT INTO t_channel (name,code,parent_id,path,sort_num,is_show,is_enabled,create_time) VALUES (?,?,?,?,?,?,?,?); // 原 params第 2 行 // Object[] params { ..., channel.getIsShow(), channel.getCreateTime() }; // 改為 Object[] params { ..., channel.getIsShow(), channel.getIsEnabled(), new Timestamp(channel.getCreateTime().getTime()) };同理update()方法中 UPDATE SQL 也要加is_enabled ?和對(duì)應(yīng)params位。5.4 Service 層ChannelServiceImpl.java 透?jìng)髯侄蜟hannelServiceImpl.java的save()和update()方法中channel.setIsEnabled(...)已由 Action 注入DAO 層會(huì)自動(dòng)取值此處無需改。5.5 Action 層ChannelAddAction.java 和 ChannelEditAction.java 補(bǔ)字段接收ChannelAddAction.java中加字段private int isEnabled 1; // 默認(rèn)啟用 public int getIsEnabled() { return isEnabled; } public void setIsEnabled(int isEnabled) { this.isEnabled isEnabled; }ChannelEditAction.java同理并在execute()方法中channel.setIsEnabled(isEnabled);。5.6 前端層三處 JSP 改造實(shí)現(xiàn)開關(guān)控件① 添加頁channel_add.jsp在code輸入框下方加tr td是否啟用/td td input typeradio nameisEnabled value1 checked/ 啟用 input typeradio nameisEnabled value0/ 停用 /td /tr② 編輯頁channel_edit.jsp在code下方加tr td是否啟用/td td input typeradio nameisEnabled value1 c:if test${channel.isEnabled 1}checked/c:if/nbsp;啟用 input typeradio nameisEnabled value0 c:if test${channel.isEnabled 0}checked/c:if/nbsp;停用 /td /tr③ 列表頁channel_list.jsp在欄目名后加狀態(tài)標(biāo)簽td ${c.name} c:if test${c.isEnabled 0} span stylecolor:red; font-size:12px;[已停用]/span /c:if /td5.7 前端過濾讓停用欄目不顯示在前臺(tái)導(dǎo)航FreeCMS 前臺(tái)導(dǎo)航由IndexAction.java的execute()方法加載channelList它調(diào)用channelService.findAll()。我們需要改ChannelDaoImpl.java的findAll()方法加 where 條件// 原 SQL // String sql SELECT * FROM t_channel ORDER BY sort_num DESC; // 改為 String sql SELECT * FROM t_channel WHERE is_enabled 1 ORDER BY sort_num DESC;這樣停用欄目既不會(huì)出現(xiàn)在后臺(tái)列表頁的[已停用]標(biāo)簽里因?yàn)閏hannel_list.jsp仍查全部也不會(huì)出現(xiàn)在前臺(tái)導(dǎo)航中因?yàn)镮ndexAction查的是is_enabled 1的數(shù)據(jù)。從改數(shù)據(jù)庫(kù)字段到前臺(tái)可見共 7 處改動(dòng)全部在項(xiàng)目自有目錄下不引入新 jar不改框架配置。我一般會(huì)在改完 DAO 層后先寫個(gè)測(cè)試類TestChannelDao.java手動(dòng) newChannelDaoImpl()調(diào)findAll()看 SQL 打印是否帶WHERE is_enabled 1再啟動(dòng) Tomcat。從那以后我每次加字段都強(qiáng)制走一遍“DB → Entity → DAO → Service → Action → JSP”全鏈路驗(yàn)證哪怕只是加個(gè)create_by字段。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取