引言:在进行程序开发的时候,为了更加利于程序的管理我们引入了新的开发模式MVC分层模式,即按功能将程序代码分别分为M(Model模型)、V(View视图)、C(Controller控制器)三个组成部分。
一、 MVC各层的详解:
- 1. M(Model)模型层:
提供一些基础数据给控制器进行调用,完成服务器端数据的管理,其中又分成如下几个部分:
1) Entity实体:实体中的属性与数据库的字段对应,通过实体的属性就可以携带数据与数据表建立联系。
package com.oop.entity; import java.io.Serializable; /** * 用户实体 * */ public class User implements Serializable { private static final long serialVersionUID = 1L; private Integer userId; //用户编号 private String userName; //用户名 private String mobile; //电话号码 private String email; //电子邮箱 private String passWord; //密码 public Integer getUserId() { return userId; } public void setUserId(Integer userId) { this.userId = userId; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getMobile() { return mobile; } public void setMobile(String mobile) { this.mobile = mobile; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getPassWord() { return passWord; } public void setPassWord(String password) { this.passWord = password; } public User() { } public User(Integer userId) { this.userId = userId; } public User(Integer userId, String userName, String mobile, String email, String password) { this.userId = userId; this.userName = userName; this.mobile = mobile; this.email = email; this.passWord = password; } public User(String userName, String mobile, String email, String password) { this.userName = userName; this.mobile = mobile; this.email = email; this.passWord = password; } /** * toString()方法 */ @Override public String toString() { return "User [userId=" + userId + ", userName=" + userName + ", mobile=" + mobile + ", email=" + email + ", password=" + passWord + "]"; } }
2) DAO(data access object)数据访问对象:提供一些CRUD的方法(将被service调用),实现操作数据的目的。
package com.oop.dao.impl; import com.oop.dao.UserDao; import com.oop.entity.User; import com.oop.util.BaseDao; /** * 管理“用户”Dao的实现类 */ public class UserDaoImpl extends BaseDao implements UserDao { User userr = null; public User selectUserByUP(User user) { try { ct = getConnection(); String sql = "SELECT * FROM tb_user WHERE username = ? AND password = ?"; pst = ct.prepareStatement(sql); pst.setString(1, user.getUserName()); pst.setString(2, user.getPassWord()); rs = pst.executeQuery(); while (rs.next()) { userr = new User(); userr.setUserId(rs.getInt("user_id")); userr.setUserName(rs.getString("username")); userr.setMobile(rs.getString("mobile")); userr.setPassWord(rs.getString("password")); userr.setEmail(rs.getString("email")); } } catch (Exception e) { e.printStackTrace(); } finally { closeAll(); } return userr; } }
3) Service业务层:完成一些DAO、Controller不能或不方便处理的任务,他也会提供一些方法给Controller调用。
package com.oop.service.impl; import com.oop.dao.UserDao; import com.oop.dao.impl.UserDaoImpl; import com.oop.entity.User; import com.oop.service.UserService; /** * 管理“用户”的service实现类 * @author zhangzimu * */ public class UserServiceImpl implements UserService { UserDao userDao = new UserDaoImpl(); /** * 通过用户名、密码查询用户对象的service层方法 * 当前的登录功能对应service层只需要调用DAO层的方法,并将结果返回给Controller即可,不需要处理额外的数据 * @param user:是user类型的对象,其中携带username和password,最终传到DAO层作为查询的条件。 * @return:返回通过username和password查询到的user对象。 */ @Override public User findUserByUP(User user) { return userDao.selectUserByUP(user); } }
- 2. C(Controller)控制器:
其作用主要是实现“准备数据”、“控制跳转”。准备数据通常有两种途径获取需要准备的数据(从请求中获取、从查询数据库获取);控制跳转通常有两种方式(转发、重定向)。
package com.oop.controller; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import com.oop.entity.User; import com.oop.service.UserService; import com.oop.service.impl.UserServiceImpl; /** * 控制器:用于接收“登录”请求的控制器 * 当在login.jsp页面中发送/loginServlet请求时是以post方式提交时就会自动调用doPost()方法; */ public class LoginServlet extends HttpServlet { private static final long serialVersionUID = 1L; // 实例化UserServiceImpl的对象 UserService userService = new UserServiceImpl(); public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置字符编码 request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=UTF-8"); /* * 1)从请求中获取“用户名”、“密码”。 request.getParameter("username"): * 获取input的name为username这个输入框中的value值 (用户输入的“用户名称”) value = "admin" */ String username = request.getParameter("username"); String password = request.getParameter("password"); // 创建一个User类型的对象,将以上获取到的数据通过调用setXXX()方法为属性赋值。通过对象就可以传递属性值 User user = new User(); user.setUserName(username); user.setPassWord(password); /* 2)调用service层的方法,service又调用Dao层的方法,查询数据库获取User类型的对象。 */ User loginUser = userService.findUserByUP(user); // 3)根据以上的查询结果判断用户是否合法,若合法跳转到成功页面(/success.jsp); // 若不合法跳转到登录页面(/login.jsp),同时提供错误消息。 if (loginUser != null) { // 合法跳转到成功页面(/success.jsp),同时显示成功消息 // 键值对,通过键值对就可以在目标jsp页面(success.jsp)中使用EL表达式调用( 欢迎您:${loginUser.getUserName()}),在网页中显示数据,达到不会显示Java代码的目的 request.setAttribute("loginUser", loginUser); //将请求转发到目标页面, request.getRequestDispatcher("/success.jsp").forward(request, response); } else { // 不合法跳转到登录页面(/login.jsp),同时提供错误消息。 /** * 第一种跳转:request.getRequestDispatcher().forward(request, response); * 1、属于服务器跳转,相当于方法调用,在执行当前文件的过程中转向执行的目标文件,两个文件(当前文件和目标文件)属于同一次请求,前后页共用一个request, * 可以通过此来传递一些数据或者会话(session)信息。request.setAttribute()和request.getAttribute()。 * 2、在前后两次执行后,地址栏不变,仍是当前文件地址。 * 3、不能转向本web应用之外的页面和网站,所以转向的速度要快。 * 4、URL中所包含的“/”表示应用程序(项目webroot)的路径 */ request.setAttribute("errorInfo", "用户名或密码错误"); request.getRequestDispatcher("/login.jsp").forward(request, response); /** * 第二种跳转:response.sendRedirect()。 * 1、属于重定向,也是客户端跳转,相当于客户端向服务端发送请求之后,服务器返回一个响应,客户端接收到响应之后又向服务端发送一次请求,一共是2次请求, * 前后页不共用一个request,不能读取转向前通过request.setAttribute()设置的属性值。 * 2、在前后两次执行后,地址栏发生改变,是目标文件的地址。 * 3、可以转向到本web应用之外的页面和网站,所以转向的速度相对要慢。 * 4、URL种所包含的"/"表示根目录的路径 * */ /** * 特殊的应用:对数据进行修改、删除、添加操作的时候,应该用response.sendRedirect()。 * 如果是采用了request.getRequestDispatcher().forward(request,response), * 那么操作前后的地址栏都不会发生改变,仍然是修改的控制器,如果此时再对当前页面刷新的话, * 就会重新发送一次请求对数据进行修改,这也就是有的人在刷新一次页面就增加一条数据的原因。 * 如何采用第二种方式传递数据: 1、可以选择session,但要在第二个文件中删除; 2、可以在请求的url中带上参数,如"add.htm?id=122" */ } } }
- 3. V(view)视图:
给用户展示数据的平台,通常是由JSP(Java sever page)作为视图。
JSP可以完全兼容HTML的内容,又增加了一些动态处理数据的功能。
login.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'login.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> <!--action="/loginServlet":指定请求提交的路径。 method = "post":指定提交的方式 --> <p>${pageContext.request.contextPath}表示JSP取得绝对路径的方法,也就是取出部署的应用程序名或者是当前的项目名称<p/> <p>会在web xml文件中去找<p/> <form action="${pageContext.request.contextPath}/loginServlet" method = "post"> 用户名称:<input type = "text" name = "username" /><br/><br/> 用户密码:<input type = "password" name = "password"/><br/><br/> ${errorInfo}<br/> <input type = "submit" value = "提交"/> <input type = "reset" value = "重置"/><br/><br/> </form> </body> </html>
success.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'success.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> 欢迎您:${loginUser.getUserName()} </body> </html>
二、 MVC各层之间调用的注意事项:
1、 上一层调用下一层:如Controller调用Service,Service调用Dao,不允许反向调用。
2、 同一层之间:尽量避免同层之间调用。
三、 MVC分层模式下的案例:
案例描述:实现登录功能------------------------------------------------------------案例代码在上面
1、 创建一个web project:MVC
2、 加入MySQL的jar包:
3、 创建项目需要的包:按照MVC分层模式来创建包。
4、 编写登录页面:/login.jsp
表单中提供“用户名输入框”、“密码输入框”、“登录按钮”
当点击“登录按钮”时就会向服务器发送登录请求
请求路径: /loginServlet
请求参数:参数名为username、password。
5、 编写Controller:
Com.oop.controller.LoginServlet
1) 从请求中获取“用户名”、“密码”。
2) 调用service层的方法,service又调用Dao层的方法,查询数据库获取User类型的对象。
3) 根据以上的查询结果判断用户是否合法,若合法跳转到成功页面(/success.jsp);若不合法跳转到登录页面(/login.jsp),同时提供错误消息。
6、 处理视图:/login.jsp、/success.jsp
/login.jsp:显示错误消息
/success.jsp:显示成功消息