适用版本:Struts 2.5+(主流稳定版本) + FreeMarker 2.3.x
目标:掌握 Struts2 与 FreeMarker 的集成机制,实现高效、安全、可维护的视图渲染
一、核心概念
1. Struts2 视图技术概览
Struts2 支持多种视图技术:
- JSP(默认)
- FreeMarker(推荐)
- Velocity(已过时)
- Thymeleaf(需额外集成)
FreeMarker 是 Struts2 官方支持的首选替代视图技术,因其性能优于 JSP,且语法更清晰。
2. FreeMarker 在 Struts2 中的角色
- 模板引擎:
.ftl
文件替代.jsp
文件。 - 数据绑定:Action 中的属性自动注入到 FreeMarker 数据模型。
- 标签支持:通过
@
语法调用 Struts2 标签(如@s.form
)。 - 主题与 UI 标签:支持 Struts2 的
xhtml
、simple
等主题。
3. 集成原理
Struts2 使用 FreeMarkerManager
管理 FreeMarker 配置,通过 FreemarkerResult
执行模板渲染,Action 的属性自动成为模板的变量。
public class UserAction extends ActionSupport {
private User user;
// getter/setter
}
模板中可直接使用:
<p>用户名:${user.name}</p>
二、操作步骤(非常详细)
步骤 1:添加 Maven 依赖
<dependencies>
<!-- Struts2 Core -->
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-core</artifactId>
<version>2.5.30</version>
</dependency>
<!-- Struts2 FreeMarker 支持(关键) -->
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-freemarker-plugin</artifactId>
<version>2.5.30</version>
</dependency>
<!-- FreeMarker(通常由插件传递依赖,可显式声明) -->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.32</version>
</dependency>
</dependencies>
✅
struts2-freemarker-plugin
提供了FreemarkerResult
和FreeMarkerManager
。
步骤 2:配置 web.xml
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
version="3.1">
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 可选:设置默认页面 -->
<welcome-file-list>
<welcome-file>index.action</welcome-file>
</welcome-file-list>
</web-app>
步骤 3:创建 struts.xml
配置文件
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<!-- 设置默认视图为 FreeMarker -->
<constant name="struts.ui.templateSuffix" value="ftl" />
<!-- 可选:设置模板目录 -->
<constant name="struts.ui.templateDir" value="template" />
<!-- 开发模式 -->
<constant name="struts.devMode" value="true" />
<package name="default" extends="struts-default" namespace="/">
<!-- Action 定义 -->
<action name="userProfile" class="com.example.UserAction">
<result name="success">/WEB-INF/templates/user-profile.ftl</result>
<result name="input">/WEB-INF/templates/edit-user.ftl</result>
</action>
</package>
</struts>
✅
struts.ui.templateSuffix=ftl
:全局设置模板后缀为.ftl
。
步骤 4:创建 Action 类
package com.example;
import com.opensymphony.xwork2.ActionSupport;
public class UserAction extends ActionSupport {
private User user;
private List<String> roles;
public String execute() {
// 模拟数据加载
this.user = new User("张三", "zhangsan@example.com");
this.roles = Arrays.asList("admin", "user");
return SUCCESS;
}
// Getter and Setter
public User getUser() { return user; }
public void setUser(User user) { this.user = user; }
public List<String> getRoles() { return roles; }
public void setRoles(List<String> roles) { this.roles = roles; }
}
步骤 5:创建 FreeMarker 模板(.ftl
)
路径:src/main/webapp/WEB-INF/templates/user-profile.ftl
<!DOCTYPE html>
<html>
<head>
<title>用户资料</title>
<#-- 使用 Struts2 CSS -->
<@s.head />
</head>
<body>
<h1>用户资料</h1>
<!-- 直接访问 Action 属性 -->
<p>姓名:${user.name}</p>
<p>邮箱:${user.email}</p>
<!-- 使用 Struts2 标签 -->
<@s.form action="updateUser">
<@s.textfield name="user.name" label="姓名" value="${user.name}" />
<@s.password name="user.password" label="密码" />
<@s.select name="roles" label="角色" list=roles />
<@s.submit value="保存" />
</@s.form>
<!-- 条件判断 -->
<#if user.isAdmin>
<p><strong>管理员权限</strong></p>
</#if>
<!-- 遍历集合 -->
<ul>
<#list roles as role>
<li>${role}</li>
</#list>
</ul>
</body>
</html>
✅
@s.xxx
:调用 Struts2 FreeMarker 标签库。
步骤 6:部署并访问
- 打包为 WAR 部署到 Tomcat。
- 访问:
http://localhost:8080/your-app/userProfile.action
- 页面应正常显示用户信息和表单。
三、常见错误与解决方案
错误现象 | 原因 | 解决方案 |
---|---|---|
TemplateNotFoundException |
模板路径错误 | 检查 result 路径是否以 / 开头,文件是否存在 |
Expression user not found |
Action 属性未 getter | 确保有 getUser() 方法 |
@s.form not found |
标签库未加载 | 确认 struts2-freemarker-plugin 已引入 |
页面乱码 | 编码未设置 | 在 struts.xml 中添加 <constant name="struts.i18n.encoding" value="UTF-8"/> |
标签不渲染 | @s.head 缺失 |
在 <head> 中添加 <@s.head/> 加载 CSS/JS |
devMode=false 但模板不更新 |
缓存未关闭 | 开发期设置 struts.freemarker.templatesCache=false |
四、注意事项
- 模板路径必须以
/
开头:表示 Web 应用根目录。 - Action 属性必须有 public getter:FreeMarker 通过 OGNL 访问。
- 使用
@s.xxx
而非<s:xxx>
:FreeMarker 使用@
语法。 - 避免在模板中写 Java 代码:逻辑应在 Action 或 Service 层。
- OGNL 表达式安全:避免用户可控的 OGNL 注入(如
?${...}
参数)。 - 模板缓存:生产环境开启,开发环境关闭。
五、使用技巧
1. 自定义 FreeMarker 配置
通过 struts.properties
或 struts.xml
覆盖默认配置:
<constant name="struts.freemarker.manager.classname"
value="com.example.CustomFreeMarkerManager"/>
<constant name="struts.freemarker.templatesCache" value="false"/>
<constant name="struts.freemarker.contentType" value="text/html;charset=UTF-8"/>
2. 添加自定义共享变量
public class CustomFreeMarkerManager extends FreeMarkerManager {
@Override
protected void setDefaultSharedVariables(Configuration config) throws TemplateException {
super.setDefaultSharedVariables(config);
config.setSharedVariable("appVersion", "1.0.0");
config.setSharedVariable("now", new Date());
}
}
模板中使用:
版本:${appVersion},当前时间:${now?datetime}
3. 使用 #include
复用模板片段
<!-- header.ftl -->
<div class="header">欢迎,${user.name!}</div>
<!-- main template -->
<#include "/WEB-INF/templates/header.ftl">
4. 国际化(i18n)
<@s.text name="welcome.message" />
${getText("error.required")}
确保 ApplicationResources.properties
存在。
六、最佳实践
实践 | 说明 |
---|---|
✅ 使用 .ftl 替代 .jsp |
更清晰,性能更好 |
✅ Action 属性命名规范 | 提供 getter/setter |
✅ 模板放在 /WEB-INF/templates/ |
防止直接访问 |
✅ 使用 @s 标签保持 UI 一致性 |
如表单、验证 |
✅ 开发期关闭模板缓存 | struts.freemarker.templatesCache=false |
✅ 生产期开启缓存 | 提升性能 |
✅ 避免 OGNL 注入 | 不信任用户输入 |
✅ 使用 ! 处理 null 值 |
${user.name!'N/A'} |
七、性能优化
优化项 | 说明 |
---|---|
✅ 开启模板缓存(生产) | struts.freemarker.templatesCache=true |
✅ 合理设置缓存参数 | struts.freemarker.template_update_delay (如 5s) |
✅ 减少模板复杂度 | 避免深层嵌套、复杂计算 |
✅ 预计算数据模型 | 在 Action 中准备好数据 |
✅ 使用 #assign 缓存计算结果 |
避免重复表达式 |
✅ 静态资源分离 | CSS/JS 单独部署 |
八、高级集成:与 Spring Boot(可选)
若使用 Struts2 + Spring Boot:
@SpringBootApplication
@ImportResource("classpath:struts.xml")
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
并确保 struts2-spring-plugin
存在。
九、总结:Struts2 + FreeMarker 检查清单 ✅
项目 | 是否完成 |
---|---|
添加 struts2-freemarker-plugin 依赖 |
☐ |
设置 struts.ui.templateSuffix=ftl |
☐ |
Action 属性有 getter | ☐ |
模板使用 @s.xxx 标签 |
☐ |
模板路径正确(/WEB-INF/... ) |
☐ |
开发期关闭模板缓存 | ☐ |
生产期开启缓存 | ☐ |
使用 ! 处理 null 值 |
☐ |
避免 OGNL 注入风险 | ☐ |
✅ 一句话总结:
Struts2 集成 FreeMarker 的关键是 “插件引入 + 模板配置 + @s 标签使用”,通过 Action 属性自动注入,实现简洁高效的 MVC 视图层。