我们都知道MVC有两个经典的模式,MVC1和MVC2:

View接受用户输入,并传递到Controller。 Controller统一进行处理命令,交由Model处理具体的业务。 经过处理Model更新后,通知View进行更新。

这种模式主要用于桌面程序,使用观察者模式实现,具体来说就是让View观察Model,而用户交互控制的地方用匿名类的方式统一放在Controller中。像MFC的frame-document-view架构,如果document改变了,他会主动通知View进行update。但是在WEB应用程序中,Model(在Java中通常是JavaBean)的数据更新后,无法通知View进行更新(View在Java中通常是非常多JSP页面,需要选择一个),因此,在WEB应用程序中应该使用下面这种模式:MVC2模式

View接受用户输入,并传递到Controller。 Controller统一进行处理命令,交由model处理具体的业务。 经过处理的Model更新后,Controller会选一个View并把Model内容传递(request,session)给它(forward)。然后View进行显示。

两者区别: Model是否能主动通知View就是MVC1和MVC2模式的主要差别。桌面程序可以做到,所以使用MVC1;Web程序由于Http协议的限制做不到,所以使用MVC2

本例简单使用 JSP+Servlet+JavaBean 实现一个简单的MVC模式,因为MVC1模式适合桌面程序,web应用程序适合使用MVC2模式,所以我们要实现的是MVC2模式。

1.首先创建Maven的web工程,配置pom.xml依赖如下:

<dependencies>
    <dependency>
      <groupId>org.json</groupId>
      <artifactId>json</artifactId>
      <version>20160810</version>
    </dependency>
    <dependency>
      <groupId>javax</groupId>
      <artifactId>javaee-api</artifactId>
      <version>7.0</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>
  </dependencies>

2.WEB-INF目录下的web.xml文件配置内容如下,不然使用不了EL表达式:

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee     http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
</web-app>

3.在resource目录下创建一个json文件,这个json文件是用于配置model层信息以及响应页面的,它相当于一个池子,配置信息内容如下:

{
  "mvc":[
    {
      "actionName":"/login",
      "className":"org.zero01.javabean.LoginBean",
      "ok":"success.jsp",
      "error":"index.jsp"
    }
  ]
}

4.编写主页面index.jsp,做一个简单的表单提交模拟登录,内容如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Title</title>
</head>
<body>
<p style="color: red">${error}</p>
<form action="login.action" method="post">
    <input type="text" name="username" placeholder="用户名"/><br>
    <input type="password" name="password" placeholder="密码"/><br>
    <input type="submit"/>
</form>
</body>
</html>

5.编写success.jsp,该页面是登录成功后响应的页面,内容如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<script>
    alert("登录成功!");
</script>
<body>

</body>
</html>

6.完成以上view层的编写后,先编写model层。声明一个抽象接口,简单应用面向接口编程,Login.java内容如下:

package org.zero01.javabean;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public interface Login {

    public String excute(HttpServletRequest req, HttpServletResponse resp);

}

7.实现以上声明的接口,LoginBean.java内容如下:

package org.zero01.javabean;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class LoginBean implements Login {

    public String excute(HttpServletRequest req, HttpServletResponse resp) {
		
		// 接收表单数据
        String userName = req.getParameter("username");
        String password = req.getParameter("password");

		// 验证表单数据,返回相应的结果
        if (userName == null || userName.trim().equals("") || password == null || password.trim().equals("")) {
            req.setAttribute("error","用户名或密码不能为空!");
            return "error";
        } else if (userName.equals("lisi") && password.equals("123456")) {
            return "ok";
        }
        req.setAttribute("error","用户名或密码错误!");
        return "error";
    }
}

8.最后是完成Controller控制器的编写,ActionServlet.java内容如下:

package org.zero01.servlet;

import org.json.JSONArray;
import org.json.JSONObject;
import org.zero01.javabean.Login;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;

@WebServlet("*.action")
public class ActionServlet extends HttpServlet {

    private JSONObject config = null;

    // 初始化时加载配置信息
    public void init() throws ServletException {
        try {

            // 读取json文件的内容
            File file = new File(this.getServletContext().getRealPath("WEB-INF"), "classes/mvc2.json");
            FileReader fileReader = new FileReader(file);
            BufferedReader bufferedReader = new BufferedReader(fileReader);

            StringBuffer stringBuffer = new StringBuffer();
            String line = null;

            // 将读取的内容追加到字符串里
            while ((line = bufferedReader.readLine()) != null) {
                stringBuffer.append(line);
            }

            bufferedReader.close();
            // 把字符串内容转换成json对象
            config = new JSONObject(stringBuffer.toString());
            System.out.println("配置信息加载完成!");
        } catch (Exception e) {
            e.printStackTrace();
            System.err.println("配置信息加载失败!");
        }
    }

    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        // 如果请求资源的后缀不是.action就报404
        String uri = req.getRequestURI();
        if (!uri.endsWith(".action")) {
            resp.sendError(404, "The Page Not Found!");
            return;
        }

        // 截取出请求资源的前缀
        String actionName = uri.substring(0, uri.indexOf("."));
        System.out.println(actionName);

        try {
            // 取出mvc数组中的json对象
            JSONArray jsonArray = config.getJSONArray("mvc");
            for (int i = 0; i < jsonArray.length(); i++) {
                JSONObject object = jsonArray.getJSONObject(i);

                // 调用相应的model
                if (object.getString("actionName").equals(actionName)) {
                    Login login = (Login) Class.forName(object.getString("className")).newInstance();

                    String name = login.excute(req, resp);
                    // 将结果返回view
                    req.getRequestDispatcher(object.getString(name)).forward(req, resp);

                    return;
                }
            }
            resp.sendError(404, "The Page Not Found!");
        } catch (Exception e) {
            e.printStackTrace();
            resp.sendError(500, "The config file error!");
        }

    }
}

运行结果: