本案例實現會員管理核心業務:註冊、登錄驗證、信息查詢列表、修改、刪除。採用經典三層架構(Action → Service → DAO),Spring 管理所有 Bean 生命週期與依賴注入,Hibernate 4 通過 SessionFactory 進行 ORM 操作,Struts 2 處理 Web 請求分發。
<web-app version="3.1" xmlns="https://xmlns.jcp.org/xml/ns/javaee">
<display-name>ssh-demo</display-name>
<!-- Struts2 核心過濾器 -->
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Spring 根容器啟動監聽器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
關鍵點:ContextLoaderListener 啟動根容器加載 applicationContext.xml;Struts2 過濾器攔截所有請求,配合 struts.objectFactory=spring 將 Action 實例化交給 Spring。
package com.entity;
public class Member {
private Long id;
private String name, password, address, email;
private Integer level;
// 構造器、getter/setter 省略
}
<hibernate-mapping>
<class name="com.entity.Member" table="MEMBER">
<id name="id" type="long">
<column name="ID"/>
<generator class="identity"/> <!-- 自增主鍵 -->
</id>
<property name="name" column="NAME"/>
<property name="password" column="PASSWORD"/>
<property name="address" column="ADDRESS"/>
<property name="email" column="EMAIL"/>
<property name="level" column="LEVEL" type="int"/>
</class>
</hibernate-mapping>
public interface MemberDAO {
void add(Member m); void update(Member m); void delete(Long id);
Member findByName(String name, String pwd);
Member findById(Long id);
List<Member> findAll();
}
@Repository
public class MemberDAOImpl implements MemberDAO {
private final SessionFactory sessionFactory;
@Autowired
public MemberDAOImpl(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; }
private Session currentSession() { return sessionFactory.openSession(); }
@Override public void add(Member m) {
Session s = currentSession(); Transaction tx = s.beginTransaction();
try { s.save(m); tx.commit(); } catch (Exception e) { tx.rollback(); throw e; } finally { s.close(); }
}
// update、delete、findById、findAll 同理,均使用 HQL 參數綁定防注入
@Override public Member findByName(String name, String pwd) {
Session s = currentSession(); Transaction tx = s.beginTransaction();
try {
String hql = "FROM Member m WHERE m.name=:n AND m.password=:p";
Member m = (Member) s.createQuery(hql)
.setParameter("n", name).setParameter("p", pwd).uniqueResult();
tx.commit(); return m;
} finally { s.close(); }
}
}
public interface MemberService {
void add(Member m); void update(Member m); void delete(Long id);
Member findByName(String name, String pwd);
Member findById(Long id); List<Member> findAll();
}
@Service
public class MemberServiceImpl implements MemberService {
private MemberDAO memberDao;
@Autowired public void setMemberDao(MemberDAO memberDao) { this.memberDao = memberDao; }
@Override @Transactional // 建議在 Service 層聲明式事務
public void add(Member m) { if (memberDao.findById(m.getId()) == null) memberDao.add(m); }
// update、delete 同理先判斷存在性
@Override public Member findByName(String name, String pwd) { return memberDao.findByName(name, pwd); }
// 其他委託方法省略
}
所有 Action 繼承 ActionSupport,屬性驅動接收表單數據,Setter 注入 Service。
execute() 調用 memberService.add(member),成功重定向到 memberQuery。SUCCESS 跳轉 welcome.jsp,失敗返回 ERROR 回 register.jsp。request 屬性 memberList,轉發 displayAll.jsp。showMember() 根據 ID 查詢回顯 update.jsp;execute() 執行更新後重定向列表。id 參數刪除後重定向列表。<struts>
<constant name="struts.devMode" value="true"/>
<constant name="struts.objectFactory" value="spring"/> <!-- 關鍵:Action 由 Spring 管理 -->
<package name="default" namespace="/" extends="struts-default">
<action name="memberRegister" class="memberRegisterAction">
<result name="success" type="redirectAction">memberQuery</result>
</action>
<action name="memberLogin" class="memberLoginAction">
<result name="success">/welcome.jsp</result>
<result name="error">/register.jsp</result>
</action>
<action name="memberQuery" class="memberQueryAction">
<result name="success">/displayAll.jsp</result>
</action>
<action name="memberDelete" class="memberDeleteAction">
<result name="success" type="redirectAction">memberQuery</result>
</action>
<action name="memberShow" class="memberUpdateAction" method="showMember">
<result name="success">/update.jsp</result>
</action>
<action name="memberUpdate" class="memberUpdateAction">
<result name="success" type="redirectAction">memberQuery</result>
</action>
</package>
</struts>
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:context="https://www.springframework.org/schema/context"
xsi:schemaLocation=".../spring-beans-4.3.xsd .../spring-context-4.3.xsd">
<!-- C3P0 數據源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/zzh?useUnicode=true&characterEncoding=UTF-8"/>
<property name="user" value="root"/> <property name="password" value="root"/>
<property name="maxPoolSize" value="40"/> <property name="minPoolSize" value="1"/>
</bean>
<!-- Hibernate 4 SessionFactory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mappingResources">
<list><value>com/entity/Member.hbm.xml</value></list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.current_session_context_class">org.springframework.orm.hibernate4.SpringSessionContext</prop>
</props>
</property>
</bean>
<!-- Bean 定義與依賴注入 -->
<bean id="memberDao" class="com.dao.MemberDAOImpl">
<constructor-arg ref="sessionFactory"/>
</bean>
<bean id="memberService" class="com.service.MemberServiceImpl">
<property name="memberDao" ref="memberDao"/>
</bean>
<bean id="memberRegisterAction" class="com.action.MemberRegisterAction">
<property name="memberService" ref="memberService"/>
</bean>
<!-- 其他 Action Bean 省略,均注入 memberService -->
</beans>
<s:form action="memberRegister" method="post">
<s:textfield name="member.name" label="會員姓名" required="true"/>
<s:password name="member.password" label="會員口令"/>
<s:textfield name="member.address" label="會員地址"/>
<s:textfield name="member.email" label="會員郵箱"/>
<s:textfield name="member.level" label="會員級別"/>
<s:submit value="提交"/>
</s:form>
<table border="1">
<tr><th>ID</th><th>姓名</th><th>密碼</th><th>地址</th><th>郵箱</th><th>級別</th><th>操作</th></tr>
<s:iterator value="#request.memberList" var="mb">
<tr>
<td><s:property value="#mb.id"/></td>
<td><s:property value="#mb.name"/></td>
<td><s:property value="#mb.password"/></td>
<td><s:property value="#mb.address"/></td>
<td><s:property value="#mb.email"/></td>
<td><s:property value="#mb.level"/></td>
<td>
<a href="<s:url action="memberDelete"><s:param name="id" value="#mb.id"/></s:url>">刪除</a>
<a href="<s:url action="memberShow"><s:param name="id" value="#mb.id"/></s:url>">修改</a>
</td>
</tr>
</s:iterator>
</table>
register.jsp 註冊會員。displayAll.jsp 列表,測試修改、刪除鏈接。NoClassDefFoundError 檢查 C3P0/Hibernate JAR;若事務不生效確認 @Transactional 与 tx:annotation-driven;若 Action 注入為空確認 struts.objectFactory=spring 及 Bean ID 一致。本案例展示了 SSH 整合的標準姿勢:Spring 管理全局 Bean 與事務、Hibernate 負責 ORM、Struts2 處理 Web 流控。關鍵在於 applicationContext.xml 中 sessionFactory 注入 DAO、Service 注入 DAO、Action 注入 Service 的依賴鏈,以及 struts.xml 中 class 屬性引用 Spring Bean ID 實現 Action 托管。掌握此模板,可快速構建傳統 Java Web 專案骨架。