Google App Engine (GAE) 早期僅支援 Python,Java 支援的推出讓廣大 Java 開發者得以使用熟悉的技術棧構建雲應用。Struts 2 作為企業級 Web 框架佔有率極高,將其移植至 GAE 能大幅降低遷移成本。本文記錄 Struts 2.1.6 在 GAE/Java 環境下的完整適配實踐。
Struts 2.1.6 穩定版所需核心 JAR(放入 WEB-INF/lib):
使用 IntelliJ IDEA 創建項目時,勾選 Struts 2.1.6 支援可自動導入上述依賴。
GAE Servlet 容器施加嚴格限制:禁止線程操作、禁止文件系統讀寫、安全管理器限制。Struts 2 核心依賴 OGNL 表達式語言,其默認安全管理器檢查會導致啟動失敗。
package com.example.gae.adapter;
import ognl.OgnlRuntime;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class StrutsAppEngineAdapter implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
// 關鍵:移除 OGNL 安全管理器限制,允許在 GAE 沙箱中運行
OgnlRuntime.setSecurityManager(null);
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
// 清理資源(如有)
}
}
在 web.xml 註冊監聽器(需置於 Struts Filter 之前):
<web-app>
<listener>
<listener-class>com.example.gae.adapter.StrutsAppEngineAdapter</listener-class>
</listener>
<!-- Struts 2 Filter 配置 -->
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
Struts 2 的 TypeConverter 機制可自動處理請求參數與領域對象雙向轉換。針對 GAE Datastore 常用 Text、Key 類型編寫轉換器,極大簡化 Action 代碼。
package com.example.gae.converter;
import com.google.appengine.api.datastore.Text;
import ognl.DefaultTypeConverter;
import java.util.Map;
public class TextConverter extends DefaultTypeConverter {
@Override
public Object convertValue(Map<String, Object> context, Object value, Class<?> toType) {
if (toType == Text.class) {
String[] vals = (String[]) value;
return new Text(vals[0]); // String[] → Text
} else if (toType == String.class) {
Text text = (Text) value;
return text.getValue(); // Text → String
}
return null;
}
}
package com.example.gae.converter;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import ognl.DefaultTypeConverter;
import java.util.Map;
public class KeyConverter extends DefaultTypeConverter {
@Override
public Object convertValue(Map<String, Object> context, Object value, Class<?> toType) {
if (toType == Key.class) {
String[] vals = (String[]) value;
return KeyFactory.stringToKey(vals[0]); // String → Key
} else if (toType == String.class) {
Key key = (Key) value;
return KeyFactory.keyToString(key); // Key → String
}
return null;
}
}
在類路徑根目錄創建 xwork-conversion.properties:
com.google.appengine.api.datastore.Text=com.example.gae.converter.TextConverter
com.google.appengine.api.datastore.Key=com.example.gae.converter.KeyConverter
效果: Action 中直接聲明 Key id、Text content 屬性,無需手動解析字符串,框架自動完成雙向綁定。
Struts 2 「零配置」利器 Convention Plugin 依賴掃描類路徑文件系統,而 GAE 禁止文件讀寫,導致插件無法正常工作(截至 2.1.6 版本)。
建議策略:
action 包、WEB-INF/content/ 視圖)struts.xml 映射 Action 與結果src/
├── main/
│ ├── java/
│ │ ├── com.example.gae.action/ # Action 類
│ │ ├── com.example.gae.adapter/ # GAE 適配器
│ │ ├── com.example.gae.converter/ # 類型轉換器
│ │ └── com.example.gae.model/ # 領域模型
│ ├── resources/
│ │ └── xwork-conversion.properties # 轉換器註冊
│ └── webapp/
│ ├── WEB-INF/
│ │ ├── web.xml
│ │ ├── struts.xml # 手動配置映射
│ │ └── lib/ # 依賴 JAR
│ └── content/ # 視圖模板
└── test/
通過禁用 OGNL 安全管理器、實現 Datastore 核心類型換器、規避 Convention Plugin 限制,Struts 2 可在 GAE/Java 上穩定運行。此方案保留了 Struts 2 豐富的攔截器、驗證、國際化等企業級特性,同時適配雲原生約束,是存量應用上雲的低成本路徑。