2008年的SSH入门门槛——插入一个用户要配多少东西
Struts 1.1 + Spring 2.0 + Hibernate 3。这是2008年Java Web开发的"标准三件套"。那时候写一个"插入用户"功能,不是今天的三行代码,是在五层配置文件里找到了正确的标签、配对了正确的依赖链。这篇文章是一个当年的SSH学习项目,逐步拆解"插入一条用户记录"到底经历了多少层。
文章目录
一、背景:2008年的SSH三件套
2008年做Java Web,开口就是SSH——Struts做MVC,Spring做容器,Hibernate做ORM。不是选其中一个,是三个都要用,少一个就不叫"企业级"。
当时的入门门槛不是理解概念,是把这三个东西配通。因为它们不是一个爹生的——Struts是Apache的,Spring是SpringSource的,Hibernate是JBoss的。三个不同组织开发的框架要在一个项目里合作,全靠XML配置把它们的接触点手工接上。
一个最简单的"插入用户"功能,经历的层数是今天Spring Boot的十倍。
二、Struts 1——前端的请求入口
<!-- struts-config.xml -->
<form-beans>
<form-bean name="userform" type="com.myssh.form.UserForm"/>
</form-beans>
<action-mappings>
<action path="/user"
type="com.myssh.action.UserAction"
parameter="status">
<forward name="success" path="/success.jsp"/>
<forward name="fail" path="/fail.jsp"/>
</action>
</action-mappings>
前端JSP用 <html:form action="/user.do?status=insert"> 提交表单。Struts 1的 ActionServlet 拦截 *.do,读到 path="/user",根据 parameter="status" 的 insert 值找到 UserAction 里的 insert() 方法。
<!-- test.jsp -->
<html:form action="/user.do?status=insert">
用户名:<html:text property="username"/>
密码:<html:password property="password"/>
<html:submit>提交</html:submit>
</html:form>
Struts 1 的 ActionForm 要把表单字段一个一个人肉声明:
public class UserForm extends ActionForm {
private String username;
private String password;
// getter/setter... 每个字段都要写
}
今天一个 @RequestParam 或 @ModelAttribute 的事,那时要写一个完整的ActionForm类。
三、Spring 2——IOC容器把三层串起来
<!-- applicationContext.xml -->
<!-- 数据源 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:@127.0.0.1:1521:orcl"/>
<property name="username" value="YFSI"/>
<property name="password" value="YFSI"/>
</bean>
<!-- Hibernate Session工厂 -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mappingResources">
<list><value>com/myssh/po/User.hbm.xml</value></list>
</property>
</bean>
<!-- DAO -->
<bean id="iuserdao" class="com.myssh.dao.impl.UserDaoImpl">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<!-- Service -->
<bean id="iuserservice" class="com.myssh.service.impl.UserServiceImpl">
<property name="iuserdao" ref="iuserdao"/>
</bean>
<!-- Action(Struts与Spring的接触点) -->
<bean name="/user" class="com.myssh.action.UserAction">
<property name="iuserservice" ref="iuserservice"/>
</bean>
依赖注入链:UserAction → UserServiceImpl → UserDaoImpl → SessionFactory → DataSource。每个节点都是XML里一个 <bean> 标签。
注意Action的bean name是 /user——它必须和 struts-config.xml 里的 path="/user" 对得上。这是Struts和Spring之间唯一的连接点,配错了Struts找不到Action。
四、Struts与Spring的桥接——DelegatingRequestProcessor
<!-- struts-config.xml -->
<controller>
<set-property property="processorClass"
value="org.springframework.web.struts.DelegatingRequestProcessor"/>
</controller>
这一行是整个SSH集成的关键。Struts 1默认用 RequestProcessor 创建Action实例。DelegatingRequestProcessor 把它劫持了——创建Action时不自己 new,而是去Spring容器里按 path 名称查找bean。
所以Action的bean name必须和struts-config里的path一致——这就是SSH时代"Struts认path、Spring认beanName"的设计约定。不对上就是一串 NullPointerException。
五、Hibernate 3——对象到Oracle表的映射
<!-- User.hbm.xml -->
<hibernate-mapping>
<class name="com.myssh.po.User" table="user1" schema="YFSI">
<id name="id" column="id" type="integer">
<generator class="increment"/>
</id>
<property name="username" column="username" type="string"/>
<property name="password" column="password" type="string"/>
</class>
</hibernate-mapping>
POJO只有三个字段——id、username、password。但每个字段都要在XML里显式声明类型、列名、映射关系。今天 @Entity + @Column 的事,那时候是一份独立的XML文件。
六、DAO实现——HibernateDaoSupport
public class UserDaoImpl extends HibernateDaoSupport implements IUserDao {
public boolean insert(User user) {
try {
this.getHibernateTemplate().save(user);
return true;
} catch (Exception e) {
return false;
}
}
}
HibernateDaoSupport 是Spring对Hibernate的封装——通过 getHibernateTemplate() 拿到模板对象,调 save()、update()、delete()。这个模式的好处是:不用手动开Session、不用管事务、不用close连接。Spring帮你管了。
但代价也很明显——你要继承 HibernateDaoSupport,破坏了自己DAO的继承体系。如果你的DAO还需要继承别的基类,就没法做了。
七、完整调用链——从JSP到Oracle的五层传递
test.jsp
│ POST /user.do?status=insert
▼
ActionServlet (*.do路由)
│
▼
DelegatingRequestProcessor(从Spring容器取Action)
│
▼
UserAction.insert()
│ 从ActionForm取出username/password
│ 构造User PO
│ 调 iuserservice.insert(user)
▼
UserServiceImpl.insert()
│ 调 iuserdao.insert(user)
▼
UserDaoImpl.insert()
│ 调 getHibernateTemplate().save(user)
▼
Hibernate → INSERT INTO YFSI.user1 VALUES(1, 'admin', '123456')
│
▼
返回 success.jsp 或 fail.jsp
从点击提交到执行SQL,穿过六层。每一步都靠XML配置把接口和实现串起来。今天的Spring Boot让你感觉"写一个功能只要三行代码"——不是功能真的简化了,是这六层的XML配置全被Spring Boot自动配置替代了。
八、SSH时代的三座大山
| 山 | 当年的痛苦 | 今天的解法 |
|---|---|---|
| web.xml配置 | 手动配Servlet/Taglib/ContextLoader | Spring Boot自动注册 |
| Struts-Spring桥接 | DelegatingRequestProcessor配错就404 | Spring MVC原生集成 |
| Hibernate映射 | 每张表一个hbm.xml | JPA注解 + @Entity |
| 依赖注入 | 每个bean一个 <property> | @Autowired自动装配 |
| 事务管理 | HibernateTransactionManager手配 | @Transactional一个注解 |
一个插入用户的Demo,配置文件的篇幅是业务代码的三倍。这不是过度设计——是这三个框架设计之初就是独立的,合在一起全靠XML粘合剂。理解了这套粘合剂,就知道后来的Spring Boot消灭了什么。
九、结语
2008年的SSH学习成本不在Java本身——在配通三套框架的接触点。Struts认path,Spring认beanName,Hibernate认mappingResource,三者之间的对应关系全靠人脑记住。这套SSH三件套统治了Java Web开发整整五年,直到2013年Spring Boot出现,一键消灭了这堆XML。
现在回头看这个只有"插入用户"的项目,代码不到100行,配置超过200行。它不是复杂——是那个年代的技术选型就是这样的。理解了它,就理解了Spring Boot为什么能一统天下。
70

被折叠的 条评论
为什么被折叠?



