适用版本: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 的 xhtmlsimple 等主题。

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 提供了 FreemarkerResultFreeMarkerManager


步骤 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:部署并访问

  1. 打包为 WAR 部署到 Tomcat。
  2. 访问:http://localhost:8080/your-app/userProfile.action
  3. 页面应正常显示用户信息和表单。

三、常见错误与解决方案

错误现象 原因 解决方案
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

四、注意事项

  1. 模板路径必须以 / 开头:表示 Web 应用根目录。
  2. Action 属性必须有 public getter:FreeMarker 通过 OGNL 访问。
  3. 使用 @s.xxx 而非 <s:xxx>:FreeMarker 使用 @ 语法。
  4. 避免在模板中写 Java 代码:逻辑应在 Action 或 Service 层。
  5. OGNL 表达式安全:避免用户可控的 OGNL 注入(如 ?${...} 参数)。
  6. 模板缓存:生产环境开启,开发环境关闭。

五、使用技巧

1. 自定义 FreeMarker 配置

通过 struts.propertiesstruts.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 视图层。