第22章 迈向Spring MVC的旅程

第22章 迈向Spring MVC的旅程

本章内容

  • Servlet独行天下的时代

  • 繁盛一时的JSP时代

  • Servlet与JSP的联盟

  • 数英雄人物,还看今朝

“子曰:温故而知新”,如果简单回顾一下整个Java平台上的Web开发历程,将极大有助于我们理解当前各个Web开发框架存在的背景和先进性,最主要的是有助于我们平滑过渡到SpringMVC的世界中去。所以,不妨让我们从最初的Servlet独行天下的时代说起。

22.1 Servlet独行天下的时代

话说Servlet是当年Java平台上第一个用于Web开发的技术。相对于CGI(Common Gateway Interface)时代来说,Servlet的提出是一个很大的进步,它运行于Web容器(Web Container)之内,提供了Session和对象生命周期管理等功能。最主要的,Servlet依然是Java类,从中直接访问Java平台的各种服务,并使用相应的API支持是很自然的事情,比如调用业务对象,使用JDBC进行数据访问等。

Servlet本身并非万能的,它的强项在于无缝地衔接业务对象与Web层对象之间的调用,以及二进制显示内容的输出等。 但在当时开发人员只有Servlet这一种技术可以选择的时候,或许是不得不,也或许是盲从,又或许根本就是图一时的省事儿,单一的Servlet被赋予了过多的使命,从而导致了开发过程中出现的一系列的弊病。最常见的,就是称为“神奇Servlet(Magic Servlet)”的普遍存在。在“神奇Servlet"中,开发人员会将各种逻辑混杂于一处,包括流程控制逻辑、视图显示逻辑、业务逻辑、数据访问逻辑等,这就造成了后期系统的难以维护等一系列问题。 我们不妨看一段模拟当年的Web应用中的Servlet实现代码,见下方代码清单。

public class MockMagicServlet extends javax.servlet.http.HttpServlet implements javax.servlet.Serv1et {
    
    
	private static final long serialVersionUID = 3122666952706765103L;
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,IOException {
    
    
		doPost(request,response);
  }
  
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
    
		String parameter1 = request.getParameter("paramName1");
		String parameter2 = request.getParameter("paramName2");
		// ...
    
		response.setContentType("text/html");
		PrintWriter out = response.getWriter();
		out.println("<HTML>");
		out.println("<HEAD><TITLE>PageTitle<TITLE><HEAD>");
		out.println("<BODY>");
		out.println("<table width=\"200\" border=\"1\">");
		out.println("<tr><td>Title1</td>td>Tit1e2</td><td>Tit1e3</td></tr>");
    
		String SQL = "select * from SomeTable where Column1=? and Column2=?";
		Connection con = getConnection();
		try {
    
    
			PreparedStatement ps = con.prepareStatement(SQL);
			ps.setString(1, parameter1);
			ps.setInt(2, Integer.parseInt(parameter2));
			// 其他可能的参数设置.....
			ResultSet rs = ps.executeQuery();
			while(rs.next()) {
    
    
				out.println("<tr>");
				out.println("<td>"+rs.getString(1)+"</td>");
				out.println("<td>"+rs.getString(2)+"</td>");
				// ...
				out.println("</tr>");
      }
			rs.close();
			ps.close();
    } catch (SQLException e) {
    
    
			e.printStackTrace(); // 不要这样做
    }
    closeConnection(con);
		out.println("</tab1e>");
		out.println("<BODY>");
		out.println("<HTML>");
		out.close();
  }
  ...
}   

代码的可读性极差,这一点先放一边不说,单就“数据访问逻辑和业务处理逻辑与对应的视图渲染逻辑相互混杂”这一点来说,就已经让今天的我们觉得该Servlet的实现是如此的不堪入目了。没有将相应的关注点进行明确的分离,直接导致相应的逻辑无法重用,进而造成后期系统难以维护。那么,我们有没有办法来重构这段代码,以使得它结构良好,易于维护呢?

实际上,我们只要将相应的逻辑以独立的形式剥离出来,避免这些逻辑之间的混杂,就应该能够得到一个结构清晰的应用。对于使用JDBC原始API进行数据访问的代码逻辑来说,有了之前Spring数据访问相关内容作为基础,对其进行重构应该是一件易事。假设你已经能够将这部分逻辑剥离到相应的数据访问对象,并提供了相应的MockServletService封装一系列的数据访问逻辑、事务管理逻辑等,那么,重构后的MockMagicServlet的代码看起来如下方代码清单所示。

...
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
    
	String parameter1 = request.getParameter("paramName1");
	String parameter2 = request.getParameter("paramName2");
	// ...
	List<InfoBean> infoList = MockServletService.query(parameter1, parameter2);
  
	response.setContentType("text/html");
	PrintWriter out = response.getWriter();
	out.println("<HTML>");
	out.println("<HEAD><TITLE>PageTitle<TITLE><HEAD>");
	out.println("<BODY>");
	out.println("<table width=\"200\" border=\"1\">");
	out.println("<tr><td>Title1</td><td>Tit1e2</td><td>Tit1e3</td></tr>");
	for(InfoBean infoBean : infoList) {
    
    
		out.println("<tr>");
		out.println("<td>"+infoBean.getColumn1()+"</td>");
		out.println("<td>"+infoBean.getColumn2()+"</td>");
		// ...
		out.println("</tr>");
  }
	out.println("</tab1e>");
	out.println("<BODY>");
	out.println("<HTML>");
  ...
	out.close();
}
...

哇噢,清爽多了,不是吗?不过,我们依然没有摆脱那些烦人的out.println,而且,对于还处于懵懂状态的Servlet开发时代来说,这些out.println可不只是烦人而已。

  • “神奇Servlet”的存在,并不意味着一个Web应用程序中只存在一个Servlet实现。实际上,Servlet时代之初,我们更多是使用“一个Servlet对应处理一个Web请求”的方式。对于简单的Web应用来说,这种用于生成视图的out.println语句,分散的程度看起来还不算夸张。可是,随着应用规模的扩展,试想一下,开发和维护这些out.println的工作量将是何等的恐怖?

  • 我们的代码示例只是给出了一个简单的视图渲染逻辑。可是,随着页面逻辑的膨胀,要维护这么一堆几乎无法“纵观全局”的out.println,我们这些开发人员该怎么办呢?暂且不谈开发人员大都不熟悉(X)HTML等页面标记语言,就算熟知,要在浩瀚的out.println中寻找要更改的位置,且保证更改过程中不会造成之前的显示逻辑的破坏,又是谈何容易啊!

  • 因为视图逻辑是以Java代码的形式写死到Servlet中的,所以如果视图逻辑需要变动的话,我们就得更改Servlet的代码并重新编译。开发人员或许会说视图逻辑不归我们管,那是美工和前台开发人员的工作,可是,写死到Java代码中的视图逻辑,你又能逃脱了干系不成?

令人遗憾的是,仅靠Servlet一人之力,我们无法解决视图逻辑与Servlet紧密耦合的问题。好消息是,这个时候有了JSP前来救驾。

提示:请稍微关注以上的Servlet代码。当然,这样说,并不是因为它臻于完美,而是因为不管这之后的Web应用的开发如何演化,都将以该Servlet为基础进行,不信的话,继续往下看。

22.2 繁盛一时的JSP时代

为了能够将Servlet中的视图渲染逻辑以独立的单元抽取出来,我们通常使用“模板化”(templating)的方法。JSP的提出,成为Java平台上开发Web应用程序事实上的模板化视图标准。

有了JSP的加入,我们就可以将原先不得不在Servlet中通过out.println语句输出的视图渲染逻辑,抽取到.jsp后缀名的模板文件中。这样,我们就有了如下方代码清单所示的视图模板定义。

<%@ page contentType="text/htm1" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type"/>
<title>PageTitle</title>
</head>
<body>
	<table border="1">
		<tr><td>Tit1e1</td><td>Tit1e2</td><td>Tit1e3</td></tr>
		<c:forEach items="$(infoList)" var="infoBean">
			<tr>
				<td>$(infoBean.column1)</td>
				<td>$(infoBean.column2)</td>
				<td>$(infoBean.column3)</td>
			</tr>
		</c:forEach>
	</tab1e>
</body>
</html>

现在,由JSP专职负责视图的渲染工作,我们的MockMagicServlet得以进一步的解脱。现在,MockMagicServlet的主要实现逻辑看起来如下方代码清单所示。

...
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,IOException {
    
    
	String parameter1 = request.getParameter("paramName1");
	String parameter2 = request.getParameter("paramName2");
	// ...
	List<InfoBean> infoList = MockServletService.query(parameter1, parameter2);
	request.setAttribute("infoList", infoList);
	forward(request, response, "view.jsp");
}
protected void forward(HttpServletRequest request, HttpServletResponse response, String viewPath) throws ServletException, IOException {
    
    
	RequestDispatcher.requestDispatcher = getServletContext().getRequestDispatcher(viewPath);
	requestDispatcher.forward(request, response);
}
...

啊哈,看起来我们离成功仅是一步之遥啦!但是,不好意思,在此之前,我们还需要经历一段曲折的日子。

实际上,如果当初我们的开发人员或者是“技术布道者”能够严格地界定JSP的基本使命的话,我们早就迈入了WebMVC的世界了。不过,如果我们更愿意“怨天尤人”,也可以把责任推卸给JSP本身。我们都知道,JSP与其他模板技术有一个主要的区别,那就是,它最终是编译为Servlet来运行的,这一层关系使得JSP拥有比其他通用模板技术更大的能量。

  • 我们可以直接在JSP中编写Java代码,通过Scriptlet,只要愿意,任何的应用程序逻辑几乎都能够融入JSP广阔的“胸襟”。我还清晰地记得当初自己写下的第一个Web应用程序是什么样子,整个应用几乎都是JSP文件组成,几乎什么逻辑全部通过Scriptlet编写到JSP文件中,甚至于数据库连接代码。我想,这只是一个缩影,实际上,那几年无论是技术社区还是技术书籍,倡导的也都是类似理念。比如介绍JSP的书籍,即使不应该放到JSP内的逻辑,也全都写入JSP的实例中,使得你不上当都难。可是现在回头看看,如果说我们之前创造了“神奇Servlet",那么,现在,我们创造的则是神奇JSP了。相应的弊病,就不多说了。

  • 使用Servlet处理Web请求,我们需要在web.xml文件中,注册相应的请求URL与具体的处理Servlet之间的一个映射关系。之前说过,最初阶段,我们是一个Web请求对应一个Servlet进行处理的,所以web.xml中就过多地充斥着与下方代码清单中类似的映射配置信息。

<!--Servlet定乂-->
<servlet>
	<servlet-name>MockMagicServlet</servlet-name>
	<servlet-class>package.name.MockMagicServlet</servlet-class>
</servlet>
<servlet>
	<servlet-name>OtherServlet</servlet-name>
	<servlet-class>package.name.OtherServlet</servlet-class>
</servlet>
...

<!--映射关系定义-->
<servlet-mapping>
	<servlet-name>MockMagicServlet</servlet-name>
	<url-pattern>/requestPath1</url-pattern>
</servlet-mapping>
<servlet-mapping>
	<servlet-name>OtherServlet</servlet-name>
	<url-pattern>/requestPath2</url-pattern>
</servlet-mapping>

使用JSP的话,则可以省却这些烦琐,直接通过链接就可以,无需任何配置。这也助长了“超频”使用JSP的“歪风”,直接使用JSP替代Servlet处理Web请求。对于简单的应用,或许几个页面的流程关系还理得清楚,一旦应用规模上来了,分散于各个JSP文件中的流程控制信息,无异于一张杂乱无章的网,令人理不清,道不明,更不用说易于管理和维护了。而且,将原本由Servlet处理的Web流程逻辑纳入JSP的职权,我们又进一步帮助了神奇JSP的尽快诞生。

本该行使简单的视图渲染功能的JSP,现在已经完全替代了Servlet而一统天下了,也就是说,我们现在不需要MockMagicServlet了,取而代之的,就是我们原先设想用于剥离试图渲染逻辑的JSP。而现在,它自身已经不只是一个视图模板了(见下方代码清单)。

包揽全部职能的JSP代码示例:

<%@page import="java.sq1.DriverManager"%>
<%@page import="java.sql.Connection"%>
<%@page import="java.sql.PrepareaStatement"%>
<%@page import="java.sq1.ResultSet"%>
<%@page import="java.sql.SQLException"%>
<htm1>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1">
<title>Inserttitlehere</title>
</head>
<body>
<table>
<tr>td>Title1</td><td>Tit1e2</td><td>Title3</td></tr>
<%
String parameter1 = request.getParameter("paramName1");
String parameter2 = request.getParameter("paramName2");
String SQL = "select * from SomeTable where Column1=? and Co1umn2=?";
Class.forName("your.driver.class.name");
Connection con = DriverManager.getConnection("serverAddress");
try {
	PreparedStatement ps = con.prepareStatement(SQL);
	ps.setString(1, parameter1);
	ps.setInt(2, Integer.parseInt(parameter2));
	// 其他可能的参数设置...
	ResultSet rs = ps.executeQuery();
	while(rs.next()) {
%>
<tr>
	<td><%=rs.getString(1)%></td>
	<td><%=rs.getString(2)%></td>
	<td><%=rs.getString(3)%></td>
</tr>
<%
  }
	rs.close();
	ps.close();
} catch (SQLException e) {
		e.printStackTrace(); // 不要这样做
)
con.close();
%>
</table></body>
</html>

可是,历史是相似的。当初为了解决“神奇Servlet”的问题,而被迫分离应用的各种关注点,我们是否也注意到了现在的JSP又以相似的步伐重蹈“神奇Servlet”的覆辙呢?答案是肯定的,不光我们注意到了,SUN公司也注意到了,所以,这也促使了JSP Model 1的诞生(见图22-1)。

image-20220627183154973

在JSP独大的世界中引入JavaBean,通过JavaBean对相关业务逻辑的封装,我们完成了初步的关注点分离。不过,JSP Model 1的提出并没有进一步地限定JSP的基本职责,本该一心关注视图渲染逻辑的JSP,现在依然紧攥着本该是Servlet强项的Web流程管理功能不放。看来,革命尚未成功,我们依然需要努力啊!

注意:单独使用JSP阶段的Web开发,还存在许多的弊端,你可以从Rod Johnson的ExpertOne-on-One J2EE Design and Development一书中了解更多详情。

另外,JSP Model 1也有其先进性,我们可以用它快速的构建Web应用程序的原型,但是,切记,不要以这种架构用于实际的生产环境。

22.3 Servlet与JSP的联盟

JSP的不良诱感使得我们走上了歧路,本已经近在咫尺的良好架构,在经历了一段尘封的岁月之后,又重现光芒。让我们回到JSP时代的开始,在那里,我们让JSP做为视图模板而存在。不管它有多大的能耐,我们只让它负责视图的渲染工作,这样,对于JSP来说,只需要页面开发人员或者说表现层(presentation layer)开发人员来负贵和管理即可。而已经剥离了视图渲染逻辑Servlet,现在只负责请求处理流程的控制以及与业务层的交互,这部分工作则由Java开发人员来负责。至此,我们不仅将各个关注点清晰地分离出来,而且也分离了Java开发人员与前台开发人员之间的职责。而后者对于一个复杂并且需要多人协作的团队来说,是至关重要的。

通过结合Servlet和JSP,并且引入封装业务层逻辑的JavaBean,我们得到了JSP Model 1的升级版架构,即JSP Model 2,见图22-2。

在Model 2中,与我们重构过程结果所展示的一样,由Servlet来管理处理流程,由JSP来负责视图的渲染,并由JavaBean封装业务逻辑并负贵与数据层进行交互。JSP Model 2的提出可以说是Java平台Web应用开发过程中一个里程碑,它促进了Web开发领域至今一直沿用的MVC设计模式的广泛应用。

虽然JSP Model 2已经具备了使用MVC模式实现的Web应用架构的雏形,但并非严格意义上的MVC。为了搞清楚其间的差别,我们先来简单回顾一下MVC模式以及模式中涉及的几个组件,见图22-3。

image-20220627195318728

MVC(Model-View-Controller,模型-视图控制器)在当今Java界尤其是Web开发领域已经是耳熟能详的一个名词了。如图22-3所示,最初意义上的MVC模式中,各个组件的作用如下所述。

  • 控制器负责接收视图发送的请求并进行处理,它会根据请求条件通知模型进行应用程序状态的更新,之后选择合适的视图显示给用户。
  • 模型通常封装了应用的逻辑以及数据状态。 当控制器通知模型进行状态更新的时候,模型封装的相应逻辑将被调用。执行完成后,模型通常会通过事件机制通知视图状态更新完毕,从而视图可以显示最新的数据状态。
  • 视图是面向用户的接口。 当用户通过视图发起某种请求的时候,视图将这些请求转发给控制器进行处理。处理流程流经控制器和模型之后,最终视图将接收到模型的状态更新通知,然后视图将结合模型数据,更新自身的显示。

可见,最初意义上的MVC模式,在视图与模型间的数据同步工作是采用从模型Push到视图的形式完成的。而对于Web应用来说,局限于所用的协议以及使用场景,无法实现从模型Push数据到视图这样的功能。所以,我们只能对MVC中的组件的最初作用定义做适当的调整,由控制器与模型进行交互,在原来通知模型更新应用程序状态的基础上,还要获取模型更新的结果数据,然后将更新的模型数据一并转发给视图。也就是说,我们现在改由控制器从模型中Pull数据给视图,这种意义上的MVC称为Web MVC,也就是现在大多说Web开发框架所使用的架构模式。

实际上,JSP Model 2已经十分接近WebMVC架构了,但是,在真正步入WebMVC应用框架时代之前,我们还是来看一下JSP Model 2在具体的应用过程中存在哪些问题,我们又是如何解决这些问题,并进而促成WebMVC应用程序框架的广泛应用的吧!

从JSP Model 2架构的示意图(图22-3)上,我们可以看到,Servlet是作为控制器的角色存在的,但是,该架构示意图并没有进一步规定,具体应用中到底是只需要一个控制器,还是使用多个控制器,这就造成如下两种情况。

  • Web应用程序中使用多个Servlet作为控制器。 这实际上也是从最初Servlet步入Java平台Web开发领域后使用最多的模式,即一个Servlet对应一个Web请求的处理。以这种方式进行的开发实践表明,我们需要为每个请求处理流程都定义一个Servlet,并借助Web容器的URL映射匹配能力来解决Web请求到具体的处理Servlet的映射。自然,我们就需要在web.xml配置文件中,为每个Servlet都提供定义并添加URL映射。随着应用规模的增加,web.xml的体积将愈加庞大。最主要的,系统中的所有Web请求的处理流程将各自分散管理,没有一种集中管理的方式。这将不利于整个系统的开发和维护工作。所以,随着开发理念的更新,这种方式逐步淡化出了我们的视野,并更多侧重于下面这种单一Servlet作为整个Web应用程序控制器的实践方式。
  • Web应用程序中使用单一Servlet作为集中控制器。 现在,所有的Web处理请求全部经由Web应用程序中定义的这个单一的Servlet控制器来进行。相对于原先的情况,请求的处理现在有了一个集中管理的位置,而且,也不用顾虑web.xml文件内容是否会因Web请求流程的增多而膨胀。不过,却遇到了新的问题,我们避免了web.xml文件的膨胀,却将这种膨胀变相地带到了Servlet控制器类中,如下所述。
    • 因为现在所有的Web请求都映射到了集中的Servlet控制器来处理,所以,控制器类需要自己来根据Web请求的URL信息进行分析,以判断处理流程的流向。 显然,无法再借助Web容器的URL映射匹配能力来完成这个工作了。早期来说,这些逻辑都是硬编码到Servlet控制器中的。这些逻辑往往不能重用,而且最主要的,一旦写死,要调整URL映射的处理,就得修改Servlet控制器的代码并重新编译,灵活性和可扩展性根本无从谈起。
    • 分析完URL映射关系之后,Servlet控制器就可以根据结果来选择执行哪些处理流程,硬编码的问题再次出现了。那个时候的控制器Servlet类,大都是将处理流程和处理逻辑硬编码到自身,无论是流程分支的调整,还是具体每个分支的处理逻辑的调整,都不可避免地要对Servlet控制器的实现代码进行一番“或大或小”的手术。当然,下一个应用开始之后,这些处理流程的转发逻辑以及其他通用逻辑,是无法复用到下一个应用程序中的。

不过,情况并没有我们所看到的那么坏,引入合适的设计模式,可以避免早期单一Servlet控制器所面临的问题。所以,这种方式依然是我们比较倾向使用的控制器实践方式。

如我们所看到的,制约JSP Model 2发展的,就是将流程控制等通用相关逻辑进行硬编码的实践方式,这直接导致了JSP Model 2架构的不可重用性。每次启动新的Web应用程序的开发工作,通常又得从头编写Servlet控制器的URL分析,以及流程控制等Web层通用逻辑。这自然就促使我们寻找途径,去除架构中控制逻辑的硬编码,并尽可能地复用Web应用程序开发过程中的一些通用逻辑。而在JSP Model 2架构基础,上发展起来的各种Web MVC应用框架,恰好顺应了历史的需求。现在,我们步入了各种Web应用程序框架盛行的时代。

22.4 数英雄人物,还看今朝

Web框架存在的意义在于,它们为Web应用程序的开发提供了一套可复用的基础设施,这样开发人员只需要关注特定于每个应用的逻辑开发工作,而不需要每次都重复那些可以统一处理的通用逻辑。当前的Web开发框架有如下两种类型。

  • 请求驱动的Web框架(request-driven framework)。 又称为request/response框架(request/response framework)。顾名思义,这种框架是基于Servlet的请求/响应(request/response)处理模型构建的。这种类型的开发框架大都以WebMVC模式为指导,在JSPModel2架构基础上“进化”而来。比如,几乎是整个Java平台Web开发框架事实标准的Struts框架,优雅轻便的WebWork等,以及我们稍后即将为你介绍的SpringMVC框架,都属于这种请求驱动的Web开发框架。

  • 事件驱动Web开发框架(event-drivenweb framework)。 或许它的另一个名字基于组件的Web开发框架(component-based framework)更好理解一些。这种框架采用与Swing等GUI开发框架类似的思想,将视图组件化,由视图中的相应组件触发事件,进而驱动整个处理流程。最初的Tapestry(http://tapestryapache.org/)框架以及现在的JSF(Java Server Faces)框架都属于这一类。

Spring MVC属于请求驱动的Web框架,所以,我们要对这种类型的框架给予更多的笔墨。至于事件驱动的Web开发框架,如果你感兴趣,不妨自己做进一步深入的了解。

对于请求驱动的Web开发框架来说,它们大多是在JSP Model 2的基础上发展而来的。我们一定有一个问题,那就是,这些Web开发框架是如何解决JSP Model 2在实践中所存在的那些问题的?

如前所述,在JSP Model 2中,我们更加倾向于使用单一Servlet作控制器的实践方式。实际上,现在的请求驱动的Web框架也大都如此,但是为了避免之前提到的一些问题,这些框架通常会结合Front Controller以及Page Controller模式,对单一Servlet控制器做进一步的改进,对原先过于耦合的各种控制器逻辑进行逐步的分离。

具体来说,就是由原来的单一Servlet作为整个应用程序的Front Controller。该Servlet接收到具体的Web处理请求之后,会参照预先可配置的映射信息,将待处理的Web处理请求转发给次一级的控制器(sub-controller)来处理,整个情形看起来如图22-4所示。

在控制器Servlet接收到Web处理请求之后,它会对Web请求的URL信息进行分析,然后根据分析结果,并参照外部可配置的流程规则,将当前Web请求转发给次一级的控制器类进行处理。现在,作为Front Controller的Servlet和次级控制器类共同组成了整个应用程序的控制器。原先单一的控制器Servlet通过将流程控制信息外部化,并分离具体的Web请求处理逻辑给次级控制器类进行处理的方式,瘦身为灵活而可复用的担当Front Contoller的Servlet。有了这样的关注点分离之后,我们就极大地提高了整个Web应用中控制器逻辑的可复用性。

如果我们之前接触过Struts框架,对照一下以上的结构,将有助于更深入地理解该框架中各个组件所扮演的角色:

  • ActionServlet是整个框架的Front Controller,负责分发具体的Web请求;

  • Action是次级控制器,ActionServlet分发的具体Web请求将由被选中的Action来处理;

  • 为了避免控制流程的硬编码,ActionServlet将从struts-config.xml中读取请求的URL与具体的Action之间的映射关系,进而正确地转发Web请求给相应的Action进行处理。

当然,各个Web框架中实现相应角色的具体实现类或许不同,但所要达到的目的是一样的。而且,有些框架还在以上实现结构的基础上,进一步细化了框架中可能复用的逻辑。在介绍完SpringMVC之后,你将发现一个设计更加完善的Web开发框架。

实际上,Spring MVC可以说是集之前各个Web框架的优点于一身,并且还有进一步的发展。相信当你更多地了解SpringMVC之后,也会深深地迷恋于此。话不多说,还是赶快开始我们的SpringMVC之旅吧!

22.5 小结

本章我们主要回顾了Java平台上以往的Web开发历程。我们从Servlet出现之初独当一面的时候开始,进而经历了JSP繁盛一时的时代,然后就是Servlet和JSP结盟,最终演化到现在Web开发框架盛行的年代。

有这些背景作为基础,接下来,我们将正式进入SpringMVC的领地,看一下SpringMVC框架到底有何独到之处。

猜你喜欢

转载自blog.csdn.net/qq_34626094/article/details/125710169
今日推荐