SSH 是 Java Web 經典三層架構組合:Struts2 (表現層) + Spring (業務層/容器) + Hibernate (持久層)。雖然現代項目多遷移至 Spring Boot,但大量存量專案仍在維護。本文梳理 MyEclipse 環境下 SSH 整合標準流程、關鍵配置、依賴管理、常見坑點。
src/
├── main/
│ ├── java/
│ │ ├── com.app.action/ # Struts2 Action
│ │ ├── com.app.service/ # Service 接口/實現
│ │ ├── com.app.dao/ # DAO 接口/實現
│ │ ├── com.app.model/ # 實體類
│ │ └── com.app.util/ # 工具類
│ ├── resources/
│ │ ├── struts.xml # Struts2 核心配置
│ │ ├── applicationContext.xml # Spring 核心配置
│ │ ├── hibernate.cfg.xml # Hibernate 配置 (可選,併入 Spring)
│ │ └── *.hbm.xml # 映射文件 (或註解)
│ └── webapp/
│ ├── WEB-INF/
│ │ ├── web.xml # 部署描述符
│ │ └── lib/ # JAR 包 (Maven 管理則無)
│ └── *.jsp
<properties>
<struts2.version>2.5.30</struts2.version>
<spring.version>5.3.21</spring.version>
<hibernate.version>5.6.10.Final</hibernate.version>
</properties>
<dependencies>
<!-- Struts2 核心 -->
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-core</artifactId>
<version>${struts2.version}</version>
</dependency>
<!-- Struts2-Spring 整合插件 (關鍵) -->
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-spring-plugin</artifactId>
<version>${struts2.version}</version>
</dependency>
<!-- Spring 核心 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Hibernate -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate.version}</version>
</dependency>
<!-- 連接池 -->
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>commons-pool</groupId>
<artifactId>commons-pool</artifactId>
<version>1.6</version>
</dependency>
<!-- 數據庫驅動 (以 MySQL 為例) -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<!-- JSON/日誌/工具 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.9</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.11</version>
</dependency>
</dependencies>
<web-app xmlns="https://xmlns.jcp.org/xml/ns/javaee"
version="3.1">
<!-- 1. Spring 容器啟動監聽器 (必須) -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- 2. Struts2 核心過濾器 -->
<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>
<!-- 3. 編碼過濾器 (建議第一個) -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xmlns:context="https://www.springframework.org/schema/context"
xmlns:tx="https://www.springframework.org/schema/tx"
xmlns:aop="https://www.springframework.org/schema/aop"
xsi:schemaLocation="
https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
https://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
https://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd
https://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 1. 掃描註解 (Service, DAO, Component) -->
<context:component-scan base-package="com.app" />
<!-- 2. 數據源 (DBCP) -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/dbname?useSSL=false&serverTimezone=UTC" />
<property name="username" value="root" />
<property name="password" value="password" />
<property name="initialSize" value="5" />
<property name="maxActive" value="20" />
</bean>
<!-- 3. SessionFactory (Hibernate 5) -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="com.app.model" /> <!-- 掃描實體註解 -->
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL8Dialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.current_session_context_class">org.springframework.orm.hibernate5.SpringSessionContext</prop>
</props>
</property>
</bean>
<!-- 4. 事務管理器 -->
<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- 5. 註解驅動事務 -->
<tx:annotation-driven transaction-manager="transactionManager" />
<!-- 6. Struts2 Action 交由 Spring 管理 (關鍵) -->
<bean id="userAction" class="com.app.action.UserAction" scope="prototype">
<property name="userService" ref="userService" />
</bean>
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"https://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<!-- 開發模式:熱加載配置、詳細錯誤 -->
<constant name="struts.devMode" value="true" />
<constant name="struts.i18n.encoding" value="UTF-8" />
<constant name="struts.objectFactory" value="spring" /> <!-- 關鍵:Action 由 Spring 創建 -->
<package name="default" namespace="/" extends="struts-default">
<action name="user_*" class="userAction" method="{1}">
<result name="success">/WEB-INF/jsp/user_list.jsp</result>
<result name="input">/WEB-INF/jsp/user_form.jsp</result>
<result name="json" type="json">
<param name="root">data</param>
</result>
</action>
</package>
</struts>
| 整合點 | 配置關鍵 | 作用 |
|---|---|---|
| Spring 管理 Action | struts.objectFactory=spring + Spring 定義 scope="prototype" Action Bean | Action 實例由 Spring 容器創建,支持依賴注入 Service |
| Spring 管理 Hibernate | LocalSessionFactoryBean + HibernateTransactionManager + tx:annotation-driven | Session 與 Spring 事務綁定,DAO 獲取 sessionFactory.getCurrentSession() |
| Open Session In View | web.xml 添加 OpenSessionInViewFilter (可選) | 視圖渲染階段保持 Session 開啟,支持延遲加載 |
| 註解替代 XML | 實體 @Entity、DAO @Repository、Service @Service/@Transactional、Action @Component | 減少 XML,開發效率高 |
// DAO
@Repository
public class UserDaoImpl implements UserDao {
@Autowired private SessionFactory sessionFactory;
public void save(User user) {
sessionFactory.getCurrentSession().save(user);
}
}
// Service
@Service
@Transactional
public class UserServiceImpl implements UserService {
@Autowired private UserDao userDao;
public void register(User user) {
userDao.save(user);
// 業務邏輯...
}
}
// Action (Spring Prototype)
@Component("userAction")
@Scope("prototype")
public class UserAction extends ActionSupport {
@Autowired private UserService userService;
private User user;
public String register() {
userService.register(user);
return SUCCESS;
}
// getter/setter...
}
core、orm、tx、aop、web,選 Copy checked library jars to project folderhibernate.cfg.xml 位置struts2-spring-plugin JARweb.xml、applicationContext.xml、struts.xml 關鍵整合配置| 錯誤現象 | 可能原因 | 解決方案 |
|---|---|---|
NoClassDefFoundError: org/springframework/... | JAR 缺失/版本衝突 | Maven 統一版本;手動管理檢查 WEB-INF/lib |
BeanCreationException: sessionFactory | 數據源/方言/映射配置錯誤 | 檢查 URL、驅動、方言、packagesToScan |
Action class not found | Struts 未找到 Action Bean | 確認 struts.objectFactory=spring、Spring 掃描包含 Action、Bean id 匹配 |
No Session found for current thread | 事務未生效/Session 綁定失敗 | 檢查 @Transactional、tx:annotation-driven、current_session_context_class |
LazyInitializationException | 視圖層訪問延遲關聯 | 啟用 OSIV Filter 或 Service 層預加載 |
總結:SSH 整合核心在於 Spring 統一容器管理 Struts Action 與 Hibernate SessionFactory。關鍵三點:struts.objectFactory=spring、LocalSessionFactoryBean + HibernateTransactionManager、@Transactional 事務邊界。新項目強烈建議遷移 Spring Boot (自動配置、內嵌容器、零 XML);存量專案按此規範維護,穩定可控。