mvc开发模式简介
M: Model模型 JavaBean
V:view视图 JSP
C:Controller控制器 Servlet
其实就是JSP + Servlet + JavaBean
上面的JavaBean就是一个普通类(实体bean),包含三部分:构造方法、私有成员变量、公共的getter和setter方法。
上图中是一个简单的MVC模式的流程图,其中各层的主要作用如下:
M:封装结果数据集
V:将最终的结果展示给用户
C:处理业务流程,将数据集发送给JSP
MVC模式的优点:降低各个模块之间的耦合,能够增强程序的可维护性和可扩展性,提高了模型的复用性。
MVC模式的缺点:增加了程序源码的复杂性。
在实际web开发中,通常会使用MVC的开发模式进行编码。
分层的开发思想
在实际开发中往往采用分层的开发思想,该思想是基于MVC模式的。
分层的思想将不同的模块内容分开,可以方便软件开发人员分工协作,提高开发效率,实现了软件开发的高内聚低耦合。
业务需求分析
在实际开发中,通常会有专门的人去跟客户进行沟通从而了解客户需要什么样的系统,之后由专业的美工将要做的系统以图片的形式表现出来,客户确认后作出一些静态的html demo页面,然后由软件开发人员创建相关数据库,编写代码将该静态页面做成动态页面,由测试人员通过测试后将其交付给客户使用。
这里主要以学习为目的,所以简化一些流程,通常一般的注册和登录都由下面几个页面组成:
- 注册页面:没有用户名时,首先需要在该页面注册成功之后才可进行登录操作,用户所提交的数据要持久化到数据库中。
- 登录页面:用户输入用户名和密码提交给后台处理。
- 登录成功页面:根据输入的用户名和密码去数据库中查找匹配的数据,如果存在则跳转登录成功页面,否则提示用户登录失败。
开发前的准备
数据库的设计,该功能比较简单,使用一张表就能完成业务逻辑,创建一个名为t_user的表,语句如下:
CREATE TABLE 't_user' (
'id' INT NOT NULL AUTO_INCREMENT,
'name' VARCHAR(45) NOT NULL,
'password' VARCHAR(45) NULL,
'email' VARCHAR(45) NULL,
'birthday' DATE NULL,
PRIMARY KEY ('id'));
搭建web开发环境:
在eclipse中创建一个web项目,因为要使用jdbc,因此将数据库驱动相关的jar包加入到项目中。
根据分层的开发思想,创建以下包名:
- com.monkey1024.bean
- com.monkey1024.servlet
- com.monkey1024.service
- com.monkey1024.service.impl
- com.monkey1024.dao
- com.monkey1024.dao.impl
- com.monkey1024.util
在src下创建db.properties
driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/m-login
username=root
password=monkey1024
在com.monkey1024.util包下创建数据库工具类DBUtil:
package com.monkey1024.util; import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ResourceBundle; public class DBUtil { private static String driverClass;
private static String url;
private static String username;
private static String password; static{
ResourceBundle rb = ResourceBundle.getBundle("db");
driverClass = rb.getString("driverClass");
url = rb.getString("url");
username = rb.getString("username");
password = rb.getString("password");
try {
//注册驱动
Class.forName(driverClass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} public static Connection getConnection() throws SQLException{
return DriverManager.getConnection(url, username, password);
}
}
在com.monkey1024.bean包下创建实体类User:
package com.monkey1024.bean; import java.util.Date; /**
* 用户表(t_user)
*
*/
public class User { private int id;
private String name;
private String password;
private String email;
private Date birthday; public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
实现注册功能
注册功能需要向数据库中添加数据,首先在com.monkey1024.dao包下创建接口UserDao:
package com.monkey1024.dao; import com.monkey1024.bean.User; /**
* 用户dao
*
*/
public interface UserDao { /**
* 添加用户信息
* @param user
* @throws Exception
*/
public void addUser(User user) throws Exception;
}
在com.monkey1024.dao.impl包下创建接口UserDao的实现类UserDaoImpl,该类中的addUser方法主要实现向数据库插入的功能:
package com.monkey1024.dao.impl; import java.sql.Connection;
import java.sql.PreparedStatement;
import java.text.SimpleDateFormat; import com.monkey1024.bean.User;
import com.monkey1024.dao.UserDao;
import com.monkey1024.util.DBUtil; public class UserDaoImpl implements UserDao { @Override
public void addUser(User user) throws Exception {
Connection conn = null;
PreparedStatement ps = null;
try {
conn = DBUtil.getConnection();
ps = conn.prepareStatement("INSERT INTO t_user(name,password,email,birthday) VALUES(?,?,?,?)");
ps.setString(1, user.getName());
ps.setString(2, user.getPassword());
ps.setString(3, user.getEmail());
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String birthday = sdf.format(user.getBirthday());
ps.setString(4, birthday);
ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("添加失败!");
}
} }
在com.monkey1024.service包下创建UserService接口:
package com.monkey1024.service; import com.monkey1024.bean.User; public interface UserService { /**
* 添加用户信息
* @param user
* @throws Exception
*/
public void addUser(User user) throws Exception; }
在com.monkey1024.service.impl包下创建UserServiceImpl实现类:
package com.monkey1024.service.impl; import com.monkey1024.bean.User;
import com.monkey1024.dao.UserDao;
import com.monkey1024.dao.impl.UserDaoImpl;
import com.monkey1024.service.UserService; public class UserServiceImpl implements UserService { UserDao userDao = new UserDaoImpl(); @Override
public void addUser(User user) throws Exception {
userDao.addUser(user);
} }
在com.monkey1024.servlet包下创建RegistServlet处理请求数据:
package com.monkey1024.servlet; import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date; import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import com.monkey1024.bean.User;
import com.monkey1024.service.UserService;
import com.monkey1024.service.impl.UserServiceImpl; /**
* 用户注册
*/
public class RegistServlet extends HttpServlet {
private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8"); //将表单提交的数据放在User类中
User u = new User();
u.setName(request.getParameter("name"));
u.setPassword(request.getParameter("password"));
u.setEmail(request.getParameter("email"));
String birthday = request.getParameter("birthday");
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date = sdf.parse(birthday);
u.setBirthday(date);
} catch (ParseException e) {
e.printStackTrace();
} //调用业务逻辑
UserService us = new UserServiceImpl();
try {
us.addUser(u);
// 分发转向
response.getWriter().write("注册成功!1秒跳转到主页");
response.setHeader("refresh", "1;url=" + request.getContextPath()
+ "/login.jsp");
} catch (Exception e) {
e.printStackTrace();
} } protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
} }
登录功能
在UserDao接口中添加下面方法:
/**
* 根据用户姓名和密码查找用户
* @param user
* @return
* @throws Exception
*/
public User findUserByNameAndPassword(User user) throws Exception;
在UserDaoImpl实现类中添加下面方法,根据用户名和密码去数据库中查找响应的记录
@Override
public User findUserByNameAndPassword(User user) throws Exception {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
User u = null;
try {
conn = DBUtil.getConnection();
ps = conn.prepareStatement("select * from t_user where name=? and password=?");
ps.setString(1, user.getName());
ps.setString(2, user.getPassword()); rs = ps.executeQuery();
if(rs.next()){
u = new User();
u.setId(rs.getInt(1));
u.setName(rs.getString(2));
u.setPassword(rs.getString(3));
u.setEmail(rs.getString(4));
u.setBirthday(rs.getDate(5));
}
} catch (Exception e) {
e.printStackTrace();
}
return u;
}
在UserService中添加下面方法:
/**
* 根据用户姓名和密码查找用户
* @param user
* @return
* @throws Exception
*/
public User findUserByNameAndPassword(User user) throws Exception;
在UserServiceImpl实现类中添加下面方法:
@Override
public User findUserByNameAndPassword(User user) throws Exception {
return userDao.findUserByNameAndPassword(user);
}
创建LoginServlet用来接收提交的用户名和密码,如果根据该用户名和密码可以从数据库中查询出相应的数据,则可以登录成功,否则登录失败。
package com.monkey1024.servlet; import java.io.IOException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import com.monkey1024.bean.User;
import com.monkey1024.service.UserService;
import com.monkey1024.service.impl.UserServiceImpl; /**
* 用户登录
*/
public class LoginServlet extends HttpServlet {
private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
User user = new User();
user.setName(request.getParameter("name"));
user.setPassword(request.getParameter("password")); UserService us = new UserServiceImpl(); try {
User u = us.findUserByNameAndPassword(user); //分发转向
if(u!=null){
//如果登录成功,就把user对象放到session对象中
request.getSession().setAttribute("user", u);
request.getRequestDispatcher("/login_success.jsp").forward(request, response);
}else{
request.setAttribute("msg", "用户名或密码不正确!");
request.getRequestDispatcher("/login.jsp").forward(request, response);
}
} catch (Exception e) {
e.printStackTrace();
}
} protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
} }
注销功能
创建LogoutServlet,在里面将session销毁:
package com.monkey1024.servlet; import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; /**
* 用户注销
*/
public class LogoutServlet extends HttpServlet {
private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//使用sessions销毁
request.getSession().invalidate();
//重定向到登录页面
response.sendRedirect(request.getContextPath()+"/login.jsp");
} protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
} }
表单赋值的问题
当请求request中携带了用户提交的数据时,需要将这些数据封装到JavaBean中,像之前写法需要一一赋值,倘若request携带了非常多的表单数据,此时的赋值操作就显得比较繁琐了,那有没有好的解决方法呢?这里可以使用apache的commons-beanutils搞定这个问题。
使用commons-beanutils解决表单赋值的问题。
首先需要下载两个jar包分别是:
commons-beanutils.jar:http://pan.baidu.com/s/1slzAndb
commons-logging.jar: http://pan.baidu.com/s/1eSNDiQA
下载完成后将其拷贝到项目的lib目录下。
修改RegistServlet中赋值操作如下:
//获取用户提交的表单数据,并封装到User中
User u = new User();
//使用commons-beanutils将表单数据封装到User对象中
try {
//因为User对象中的brithday是Date类型,所以先注册一个日期转换器
ConvertUtils.register(new DateLocaleConverter(), Date.class);
//将表单数据封装到User对象中
BeanUtils.populate(u, request.getParameterMap());
} catch (IllegalAccessException e1) {
e1.printStackTrace();
} catch (InvocationTargetException e1) {
e1.printStackTrace();
}
修改LoginServlet中赋值操作如下:
//获取用户提交的表单数据
User user = new User();
try {
BeanUtils.populate(user, request.getParameterMap());
} catch (IllegalAccessException e1) {
// TODO Auto-generated catch block
} catch (InvocationTargetException e1) {
e1.printStackTrace();
}
上面的BeanUtils.populate(user, request.getParameterMap())方法会遍历request.getParameterMap()的key,key与user中的属性一致的话,会将该属性赋值,所以要使用该方法的前提就是表单中的name值和JavaBean中的属性值名称要一致。
用户名不能重复的问题
在实际应用当中,用户名是不能重复的,即要保证用户名在数据库中的唯一性,要解决这个问题,需要在用户注册时先根据填写的用户名去数据库中查询,如果查询出结果的话,就说明该用户名已经被注册了。
主要代码如下,修改RegistServlet
//使用apache commons-beanutil解决赋值操作
try {
//因为User中的birthday是Date类型,所以需要先注册一个日期转换器
ConvertUtils.register(new DateLocaleConverter(), Date.class);
//User类中的属性名需要跟jsp表单中的name保持一致
BeanUtils.populate(u, request.getParameterMap());
} catch (IllegalAccessException e1) {
e1.printStackTrace();
} catch (InvocationTargetException e1) {
e1.printStackTrace();
} //调用业务逻辑
UserService us = new UserServiceImpl();
try {
//判断用户名是否重复
User result = us.findUserByName(u); //如果不等于null则说明用户名重复
if(result != null){
request.setAttribute("msg", "用户名重复");
request.getRequestDispatcher("/regist.jsp").forward(request, response);
}else{
//用户名不重复时,执行添加操作
us.addUser(u);
//分发转向
response.getWriter().write("注册成功!1秒后跳转到主页");
response.setHeader("refresh", "1;url=" + request.getContextPath() + "/login.jsp");
} } catch (Exception e) { e.printStackTrace();
}
login.jsp
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
${msg }
<form action="${pageContext.request.contextPath }/login" method="post">
<table>
<tr>
<td>用户名:</td><td><input type="text" name="name"/></td>
</tr>
<tr>
<td>密 码:</td><td><input type="password" name="password"/></td>
</tr>
</table>
<input type="submit" value="登录"/><br/>
</form>
没有用户名?点此<a href="${pageContext.request.contextPath }/regist.jsp">注册</a>
</body>
</html>
regist.jsp
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>注册</title>
</head>
<body>
<form action="${pageContext.request.contextPath }/regist" method="post">
${msg }
<table>
<tr>
<td>用 户 名 :</td><td><input type="text" name="name" value=""/></td>
</tr>
<tr>
<td>密 码 :</td><td><input type="password" name="password" /></td>
</tr>
<tr>
<td>确认密码:</td><td><input type="password" name="repassword"/></td>
</tr>
<tr>
<td>邮 箱 :</td><td><input type="text" name="email" /></td>
</tr>
<tr>
<!--
<td>生 日 :</td><td><input type="text" name="birthday" /></td>
-->
<td>生 日 :</td><td><input type="date" name="birthday" /></td>
</tr>
</table>
<input type="submit" value="注册"/><br/>
</form>
</body>
</html>
login_success.jsp
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>登录成功</title>
</head>
<body>
欢迎你:${user.name } <br>
<a href="${pageContext.request.contextPath }/logout">注销</a>
</body>
</html>