从无到有写一个后台注册模块(基于SSH2)

此次项目基于SSH2框架(物业报修系统),为了避免忘记框架使用的步骤,我稍微记录一下注册模块的编写过程。

框架分层

核心是Spring框架,它有IOC容器,可以方便地管理Struts2和Hibernate;Struts2因其MVC的架构,因而负责显示;Hibernate则是持久层,实现DAO。

步骤

下面是详细的步骤,根据时间先后一步一步来。

Tomcat容器的配置(WebRoot/WEB-INF/web.xml)

该文件告诉Tomcat,主页是哪个、Action交给谁处理、网站的编码格式、过滤拦截哪些URL、托管给Spring等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

<!-- 所有请求都通过FilterDispatcher查找actionMapper的设置来决定请求对应的是哪个Action -->

<display-name>邱永臣配置</display-name>

<!-- 过滤器的配置 -->
<filter>
<!-- 名字 -->
<filter-name>filterDispatcher</filter-name>
<!-- 过滤器对应的名字 -->
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
</filter>

<filter-mapping>
<filter-name>filterDispatcher</filter-name>
<!-- 过滤匹配的URL -->
<url-pattern>/*</url-pattern>
</filter-mapping>

<!-- 解决中文乱码问题 -->
<filter>
<filter-name>struts-clean</filter-name>
<filter-class>org.apache.struts2.dispatcher.ActionContextCleanUp</filter-class>
</filter>
<filter-mapping>
<filter-name>struts-clean</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<!-- 进入WEB应用访问的默认文件 -->
<welcome-file-list>
<welcome-file>lookRemindRepairOrder.jsp</welcome-file>
</welcome-file-list>

<context-param>
<param-name>contextConfigLocation</param-name>
<!-- 如果将该xml文件放在WEB-INF下面的话,就需要下面配置 <param-value>WEB-INF/applicationContext.xml</param-value> -->
<param-value>WEB-INF/applicationContext.xml</param-value>
<!-- 下面是默认配置 -->
<!-- <param-value>classpath:com/lzq/config/applicationContext-*.xml</param-value> -->
</context-param>

<!-- 采用Listener来初始化Spring容器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>

编写用户实体(model层)

所谓用户实体,其实是java bean,它内部有用户数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/**
*
*/
package com.ilovecl.myproperty.model;

/**
* 学生实体
*
* @author 邱永臣
*
* 对应的数据库建表命令为 create table `Student` ( `userId` int not null primary
* key auto_increment, `userName` varchar(20) not null, `password`
* varchar(20) not null, `phoneNumber` long, `email` varchar(100),
* `sexual` varchar(10)) ENGINE=InnoDB DEFAULT CHARSET=utf8;
*/

public class Student {
private int userId;
private String userName;
private String password;
private long phoneNumber;
private String email;
private String sexual;

public Student() {
super();
// TODO Auto-generated constructor stub
}

public Student(String userName2, String password2, long phoneNumber2,
String email2, String sexual2) {
this.userName = userName2;
this.password = password2;
this.phoneNumber = phoneNumber2;
this.email = email2;
this.sexual = sexual2;
}

/**
* @return the userId
*/
public int getUserId() {
return userId;
}

/**
* @param userId
* the userId to set
*/
public void setUserId(int userId) {
this.userId = userId;
}

/**
* @return the userName
*/
public String getUserName() {
return userName;
}

/**
* @param userName
* the userName to set
*/
public void setUserName(String userName) {
this.userName = userName;
}

/**
* @return the password
*/
public String getPassword() {
return password;
}

/**
* @param password
* the password to set
*/
public void setPassword(String password) {
this.password = password;
}

/**
* @return the phoneNumber
*/
public long getPhoneNumber() {
return phoneNumber;
}

/**
* @param phoneNumber
* the phoneNumber to set
*/
public void setPhoneNumber(long phoneNumber) {
this.phoneNumber = phoneNumber;
}

/**
* @return the email
*/
public String getEmail() {
return email;
}

/**
* @param email
* the email to set
*/
public void setEmail(String email) {
this.email = email;
}

/**
* @return the sexual
*/
public String getSexual() {
return sexual;
}

/**
* @param sexual
* the sexual to set
*/
public void setSexual(String sexual) {
this.sexual = sexual;
}

}

编写Hibernate的映射文件(Student.hbm.xml)

Hibernate正是凭借它,才能将对象映射到数据库中去:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
<class name="com.ilovecl.myproperty.model.Student" table="Student">
<id name="userId" type="java.lang.Integer">
<column name="userId" />
<generator class="increment" />
</id>
<property name="userName" type="java.lang.String">
<column name="userName" length="20" />
</property>
<property name="password" type="java.lang.String">
<column name="password" length="20" />
</property>
<property name="phoneNumber" type="java.lang.Long">
<column name="phoneNumber" />
</property>
<property name="email" type="java.lang.String">
<column name="email" length="100" />
</property>
<property name="sexual" type="java.lang.String">
<column name="sexual" length="10" />
</property>
</class>
</hibernate-mapping>

将Hibernate托管给Spring

在WebRoot/WEB-INF/applicationContext.xml中增加下面的代码代码,告诉Hibernate数据库的信息,映射文件的位置,将Hibernate的sessionFactory托管给Spring:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<!-- 设置字符集,防止插入到数据库里时出现乱码 -->
<value>jdbc:mysql://139.129.118.90:3306/myproperty?useUnicode=true&amp;characterEncoding=UTF-8</value>
</property>
<property name="username">
<value>root</value>
</property>
<property name="password">
<value>test</value>
</property>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource" />
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</prop>
</props>
</property>
<property name="mappingResources">
<list>
<value>com/ilovecl/myproperty/model/Student.hbm.xml</value>
</list>
</property>
</bean>

<bean id="baseTransactionProxy"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
abstract="true">
<property name="transactionManager">
<ref bean="transactionManager" />
</property>
<property name="transactionAttributes">
<props>
<prop key="save*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="delete*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>

编写DAO层代码(接口和实现)

DAO层专门操作实体,比如增删改查一个学生等。
接口代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
*
*/
package com.ilovecl.myproperty.DAO;

import java.util.List;

import com.ilovecl.myproperty.model.Student;

/**
* DAO层:学生DAO的接口
*
* @author 邱永臣
*
*/
public interface IStudentDAO {
public abstract void save(Student transientInstance);

public abstract void delete(Student persistentInstance);

public abstract Student findById(java.lang.Integer id);

public abstract List<Student> findByUserName(java.lang.String userName);

boolean isStudentExits(String userName);

}

实现的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
/**
*
*/
package com.ilovecl.myproperty.DAO.impl;

import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import com.ilovecl.myproperty.DAO.IStudentDAO;
import com.ilovecl.myproperty.model.Student;

/**
* @author 邱永臣
*
*/
public class StudentDAO extends HibernateDaoSupport implements IStudentDAO {

private static final Log log = LogFactory.getLog(StudentDAO.class);

/*
* (non-Javadoc)
*
* @see
* com.ilovecl.myproperty.DAO.IStudentDAO#save(com.ilovecl.myproperty.model
* .Student)
*/
@Override
public void save(Student transientInstance) {
log.debug("save(Student transientInstance) in StudentDAO");
try {
this.getHibernateTemplate().saveOrUpdate(transientInstance);
log.debug("save(Student transientInstance) in StudentDAO successful");
} catch (RuntimeException re) {
log.error("save(Student transientInstance) in StudentDAO failed",
re);
throw re;
}

}

/*
* (non-Javadoc)
*
* @see
* com.ilovecl.myproperty.DAO.IStudentDAO#delete(com.ilovecl.myproperty.
* model.Student)
*/
@Override
public void delete(Student persistentInstance) {
log.debug("delete(Student persistentInstance)");
try {
getHibernateTemplate().delete(persistentInstance);
log.debug("delete(Student persistentInstance) successful");
} catch (RuntimeException re) {
log.error("delete(Student persistentInstance) failed", re);
throw re;
}

}

/*
* (non-Javadoc)
*
* @see com.ilovecl.myproperty.DAO.IStudentDAO#findById(java.lang.Integer)
*/
@Override
public Student findById(Integer id) {
log.debug("findById(Integer id) : " + id);
try {
Student instance = (Student) getHibernateTemplate().get(
"com.ilovecl.myproperty.model.Student", id);
return instance;
} catch (RuntimeException re) {
log.error("findById(Integer id) failed", re);
throw re;
}
}

/*
* (non-Javadoc)
*
* @see
* com.ilovecl.myproperty.DAO.IStudentDAO#findByUsername(java.lang.Object)
*/
@SuppressWarnings("unchecked")
@Override
public List<Student> findByUserName(String userName) {
List<Student> students;
try {
String queryString = "from Student as model where model."
+ "userName" + "= ?";
students = getHibernateTemplate().find(queryString, userName);
} catch (RuntimeException re) {
log.error("findByUserName failed", re);
throw re;
}

return students;
}

@Override
public boolean isStudentExits(String userName) {
return findByUserName(userName).size() == 1;
}
}

将DAO托管给Spring

在WebRoot/WEB-INF/applicationContext.xml中增加下面的代码代码:

1
2
3
4
5
6
<!-- 将学生DAO托给Spring -->
<bean id="studentDAO" class="com.ilovecl.myproperty.DAO.impl.StudentDAO">
<property name="sessionFactory">
<ref bean="sessionFactory" />
</property>
</bean>

编写逻辑层代码(接口与实现)

逻辑层原先放在Struts2控制层的Action中,但随着Action的日渐庞大,许多负责的逻辑代码需要分离出来单独作为逻辑层。

接口的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
*
*/
package com.ilovecl.myproperty.service;

import com.ilovecl.myproperty.DAO.IStudentDAO;
import com.ilovecl.myproperty.model.Student;

/**
* @author 邱永臣
*
*/
public interface IRegisterService {

public IStudentDAO getStudentDAO();

public void setStudentDAO(IStudentDAO studentDAO);

public boolean register(Student student);

boolean isStudentExits(String userName);
}

实现的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
/**
*
*/
package com.ilovecl.myproperty.service.impl;

import com.ilovecl.myproperty.DAO.IStudentDAO;
import com.ilovecl.myproperty.model.Student;
import com.ilovecl.myproperty.service.IRegisterService;

/**
* @author 邱永臣
*
*/
public class RegisterService implements IRegisterService {

private IStudentDAO studentDAO;

/**
* @return the studentDAO
*/
public IStudentDAO getStudentDAO() {
return studentDAO;
}

/**
* @param studentDAO
* the studentDAO to set
*/
public void setStudentDAO(IStudentDAO studentDAO) {
this.studentDAO = studentDAO;
}

/*
* (non-Javadoc)
*
* @see
* com.ilovecl.myproperty.service.IRegisterService#isStudentExits(java.lang
* .String)
*/
@Override
public boolean isStudentExits(String userName) {
return studentDAO.findByUserName(userName).size() == 1;
}

/*
* (non-Javadoc)
*
* @see
* com.ilovecl.myproperty.service.IRegisterService#register(java.lang.String
* , java.lang.String)
*/
@Override
public boolean register(Student student) {
if (isStudentExits(student.getUserName())) {
return false;
}

studentDAO.save(student);

return true;
}

}

将逻辑层托管给Spring

在WebRoot/WEB-INF/applicationContext.xml中增加下面的代码代码:

1
2
3
4
5
6
7
8
<!-- 注册业务逻辑 -->
<bean id="registerService" parent="baseTransactionProxy">
<property name="target">
<bean class="com.ilovecl.myproperty.service.impl.RegisterService">
<property name="studentDAO" ref="studentDAO" />
</bean>
</property>
</bean>

编写Struts2的Action

到这里,我们就进入了Struts2的MVC世界。

  1. 控制层:Action属于MVC中的C;
  2. 模型层:Struts1中用java bean作为模型层,也就是MVC中的M,但该方案在Struts2中被废除,模型层的内容直接放入Action;
  3. 视图层:jsp的内容即是视图层;

Action最主要的方法是execute,在用户点击注册按钮时,会执行该方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
/**
*
*/
package com.ilovecl.myproperty.struts.action;

import com.ilovecl.myproperty.model.Student;
import com.ilovecl.myproperty.service.IRegisterService;
import com.opensymphony.xwork2.ActionSupport;

/**
* 用户注册功能的Action类
*
* @author 邱永臣
*
*/
@SuppressWarnings("serial")
public class StudentRegisterAction extends ActionSupport {
private int userId;
private String userName;
private String password;
private String passwordConfirm;
private long phoneNumber;
private String email;
private String sexual;

String message;

private IRegisterService iRegisterService;

/**
*
*/
public StudentRegisterAction() {
phoneNumber = 0;
email = "";
sexual = "";
}

/*
* (non-Javadoc)
*
* @see com.opensymphony.xwork2.ActionSupport#execute()
*/
@Override
public String execute() throws Exception {
this.message = "";

if (this.userName.equals("")) {
this.message += "用户名为空!\n";
return INPUT;
} else if (this.password.equals("") || this.passwordConfirm.equals("")) {
this.message += "密码为空!\n";
return INPUT;
} else if (!this.password.equals(this.passwordConfirm)) {
this.message += "两次密码不相同!\n";
return INPUT;
}

if (iRegisterService.isStudentExits(userName)) {
this.message += "账户已存在!";
return INPUT;
}
if (iRegisterService.register(new Student(userName, password,
phoneNumber, email, sexual))) {
this.message = "注册成功";
return SUCCESS;
} else {
this.message = "注册失败";
return INPUT;
}
}

/**
* @return the userId
*/
public int getUserId() {
return userId;
}

/**
* @param userId
* the userId to set
*/
public void setUserId(int userId) {
this.userId = userId;
}

/**
* @return the userName
*/
public String getUserName() {
return userName;
}

/**
* @param userName
* the userName to set
*/
public void setUserName(String userName) {
this.userName = userName;
}

/**
* @return the password
*/
public String getPassword() {
return password;
}

/**
* @param password
* the password to set
*/
public void setPassword(String password) {
this.password = password;
}

/**
* @return the passwordConfirm
*/
public String getPasswordConfirm() {
return passwordConfirm;
}

/**
* @param passwordConfirm
* the passwordConfirm to set
*/
public void setPasswordConfirm(String passwordConfirm) {
this.passwordConfirm = passwordConfirm;
}

/**
* @return the phoneNumber
*/
public long getPhoneNumber() {
return phoneNumber;
}

/**
* @param phoneNumber
* the phoneNumber to set
*/
public void setPhoneNumber(long phoneNumber) {
this.phoneNumber = phoneNumber;
}

/**
* @return the email
*/
public String getEmail() {
return email;
}

/**
* @param email
* the email to set
*/
public void setEmail(String email) {
this.email = email;
}

/**
* @return the sexual
*/
public String getSexual() {
return sexual;
}

/**
* @param sexual
* the sexual to set
*/
public void setSexual(String sexual) {
this.sexual = sexual;
}

/**
* @return the iRegisterService
*/
public IRegisterService getiRegisterService() {
return iRegisterService;
}

/**
* @param iRegisterService
* the iRegisterService to set
*/
public void setiRegisterService(IRegisterService iRegisterService) {
this.iRegisterService = iRegisterService;
}

/**
* @return the message
*/
public String getMessage() {
return message;
}

/**
* @param message
* the message to set
*/
public void setMessage(String message) {
this.message = message;
}

}

将Action托管给Spring

在WebRoot/WEB-INF/applicationContext.xml中增加下面的代码代码:

1
2
3
4
5
<!-- 学生注册界面的Action -->
<bean id="studentRegisterAction"
class="com.ilovecl.myproperty.struts.action.StudentRegisterAction">
<property name="iRegisterService" ref="registerService" />
</bean>

让Struts2找到从Spring那里找到Action

由于Spring管理着Action,Struts2需要Action的时候,只能找Spring要。

在struts.xml中增加下面的代码代码:

1
2
3
4
<action name="register" class="studentRegisterAction">
<result name="success">/register.jsp</result>
<result name="input">/register.jsp</result>
</action>

编写视图层页面(jsp)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<%@ page contentType="text/html;charset=utf-8"%>
<%@ taglib prefix="s" uri="/struts-tags"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>

<title>用户注册</title>

</head>

<body><br><br><br><br>
<div align="center">
${requestScope.message}
<s:form action="register" method="POST">
<s:textfield name="userName" label="user name" />
<s:password name="password" size="21" label="password" />
<s:password name="passwordConfirm" size="21" label="passwordConfirm" />
<s:textfield name="phoneNumber" label="phoneNumber" />
<s:textfield name="email" label="email" />
<s:textfield name="sexual" label="sexual" />
<s:submit value="OK" />
</s:form>
</div>
</body>
</html>

其它

上面是就是一个完整的SSH2实现的注册模块,在运行之前必须先建立数据库,数据库的信息和建表命令都在上面。