`
fujohnwang
  • 浏览: 156295 次
社区版块
存档分类
最新评论

Unveil Spring 连载 之 Spring MVC篇

阅读更多

迈向SpringMVC的旅程

6.1.1. Servlet独行天下的时代
6.1.2. 繁盛一时的JSP时代
6.1.3. Servlet与JSP的联盟
6.1.4. 数英雄人物,还看今朝



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

6.1.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.Servlet 
 {
	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>Page Title<TITLE><HEAD>");
		out.println("<BODY>");
		out.println("<table width=\"200\" border=\"1\">");
		out.println("<tr><td>Title1</td><td>Title2</td><td>Title3</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));
			// ... other parameters
			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(); // don't do this
		}
		closeConnection(con);
		out.println("</table>");
		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>Page Title<TITLE><HEAD>");
		out.println("<BODY>");
		out.println("<table width=\"200\" border=\"1\">");
		out.println("<tr><td>Title1</td><td>Title2</td><td>Title3</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("</table>");
		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中寻找要更改的位置,并且保证更改过程中不会造成之前的显示逻辑的破坏,又是谈何容易啊? 即使是使用ECS (Element Construction Set)这样的类库,我想也不会减去多少维护这些out.println的痛苦。

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

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

Tip

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

6.1.2. 繁盛一时的JSP时代

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

有了JSP的加入,我们就可以将原先不得不在servlet中通过out.println语句输出的视图渲染逻辑抽取到jsp后缀名的模板文件中:

<%@ page contentType="text/html" %>
<%@ 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>Page Title</title>
</head>
<body>
	<table border="1">
		<tr><td>Title1</td><td>Title2</td><td>Title3</td></tr>
		<c:forEach items="${infoList}" var="infoBean">
			<tr>
				<td>${infoBean.column1}</td>
				<td>${infoBean.column2}</td>
				<td>${infoBean.column3}</td>
			</tr>
		</c:forEach>
	</table>
</body>
</html>
				
现在,由JSP专职负责试图的渲染工作,而我们的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的基本使命的话,我们早就迈入了Web MVC的世界,不过,如果我们更愿意怨天尤人的话, 我们也可以把责任推卸给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 definitions-->
    	<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>
    	...
    	
    	<!-- mapping definitions -->
    	<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,而现在,它自身已经不仅仅是一个视图模板了:
<%@page import="java.sql.DriverManager"%>
<%@page import="java.sql.Connection"%>
<%@page import="java.sql.PreparedStatement"%>
<%@page import="java.sql.ResultSet"%>
<%@page import="java.sql.SQLException"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<table>
<tr><td>Title1</td><td>Title2</td><td>Title3</td></tr>
<%
String parameter1 = request.getParameter("paramName1");
String parameter2 = request.getParameter("paramName2");

String SQL = "select * from SomeTable where Column1=? and Column2=?";
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));
	// ... other parameters
	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(); // don't do this
}
con.close();
%>
</table></body>
</html>
				
可是,历史是相似的,当我们当初为了解决神奇servlet的问题而被迫分离应用的各种关注点的时候,我们是否也注意到了现在的JSP又以相似的步伐重蹈神奇servlet的覆辙那? 答案是肯定的,不光我们注意到了,SUN也注意到了,所以,这也促使了JSP Model 1的诞生:

Figure 6.1. JSP Model 1示意图

JSP Model 1示意图

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

Note

单独使用JSP阶段的web开发,还存在许多的弊端,读者可以从Rod Johnson的《expert one-on-one j2ee design and development》一书中了解更多详情。

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

6.1.3. Servlet与JSP的联盟

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

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

Figure 6.2. JSP Model 2示意图

JSP Model 2示意图

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

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

Figure 6.3. MVC模式示意图

MVC模式示意图

MVC在当今Java界尤其是WEB开发领域已经是耳熟能详的一个名词了,他的全称是Model-View-Controller,中文通常称为“模型-视图-控制器 ”模式, 如上图所示,最初意义上的MVC模式中,各个组件的作用是:
  • 控制器(Controller)负责接收视图(View)发送的请求并进行处理,它会根据请求条件通知模型(Model)进行应用程序状态的更新,之后选择合适的视图显示给用户;

  • 模型(Model)通常封装了应用的逻辑以及数据状态,当控制器(Controller)通知模型(Model)进行状态更新的时候,模型(Model)封装的相应逻辑将被调用, 执行完成后,模型(Model)通常会通过事件(Event)机制通知视图(View)状态更新完毕,可以根据最新的数据状态更新视图(View)显示;

  • 视图(View)是面向用户的接口,当用户通过视图(View)发起某种请求的时候,视图(View)将这些请求转发控制器(Controller)进行处理。 处理流程流经控制器(Controller)-模型(Model)之后,最终视图(View)将接收到模型(Model)的状态更新通知,然后视图(View)将结合模型数据更新自身的显示;

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

JSP Model 2实际上已经十分接近Web MVC架构了,但是,在真正步入Web MVC应用框架时代之前,我们还是来看一下JSP Model 2在具体的应用过程中存在哪些问题,我们又是如何来解决这些问题,并进而促成Web MVC应用程序框架的广泛应用的吧! 从JSP Model 2架构的示意图上,我们可以看到Servlet是作为控制器(Controller)的角色存在的,但是,该架构示意图并没有进一步规定具体应用中到底是 只需要一个控制器那还是使用多个控制器, 这就造成两种情况:

  • 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应用程序框架盛行的时代。

6.1.4. 数英雄人物,还看今朝

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

  1. request驱动的web框架(request-driven framework).  或者又称为“request/response框架(request/response framework) ”, 顾名思义,这种框架是基于servlet的请求/响应(request/response)处理模型进行构建的,这种类型的开发框架大都以Web MVC模式为指导,在JSP Model 2架构基础之上“进化 ”而来。 比如几乎是整个Java平台web开发框架事实标准的struts 框架,优雅轻便的webwork 等等,以及我们稍后即将为您介绍的SpringMVC框架,都属于这种request驱动的web开发框架;

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

SpringMVC属于request驱动的web框架,所以,我们要对这种类型的框架给予更多的笔墨,至于事件驱动的web开发框架,如果读者您感兴趣,不妨自己做进一步深入的了解。

对于request驱动的web开发框架来说,它们大多是在JSP Model 2的基础上发展而来,那我们一定有一个问题,那就是,这些Web开发框架是如何解决JSP Model 2在实践中所存在那些的问题? 如前所述,在JSP Model 2中,我们更加倾向于使用单一servlet作控制器的实践方式,实际上,现在的request驱动的web框架也大都如此, 但是为了避免之前提到的一些问题,这些框架通常会结合Front Controller以及Page Controller模式[52 ] 对单一servlet控制器做进一步的改进,对原先过于耦合的各种控制器逻辑进行逐步的分离。具体来说,就是由原来的单一servlet作为整个应用程序的Front Controller, 该servlet接收到具体的web处理请求之后,会参照预先可配置的映射信息,将待处理的web处理请求转发给次一级的控制器(sub-controllers)来处理,整个情形看起来类似这个样子:

Figure 6.4. 引入Front Controller模式后的控制器实现结构

引入Front Controller模式后的控制器实现结构

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

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

  • ActionServlet是整个框架的front controller,负责分发具体的web请求;

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

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

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

实际上,SpringMVC可以说是集之前各个web框架的优点于一身,并且还有进一步的发展,虽然最初考虑到Struts的市场地位,笔者并不看好SpringMVC,但是,在深入的了解他之后, 我改变了之初的想法,相信当你更多的了解SpringMVC之后,你也会深深的迷恋于此,话不多说,还是让我们赶快开始我们的SpringMVC之旅吧!

分享到:
评论
2 楼 fujohnwang 2009-06-05  
引用

turingbooks 2009-05-21
大概还需要两个月的时间。
请再耐心等待些时日吧 

http://turingbook.group.iteye.com/group/topic/10279
1 楼 auauau 2009-06-05  
数什么时候出版啊?

相关推荐

    unveil 最好的去混响

    Zynaptiq 宣布用于 Mac OS X 上 AudioUnits(AU)格式的 UNVEIL 和 PITCHMAP 插件的更新现已推出。 UNVEIL 是一款实时的去混音插件,便于用户移除或隔离包括单声道在内的任意通道数量混合信号的混响声,当前最新...

    query.unveil.js

    图片懒加载

    前端项目-unveil2.zip

    《前端项目:unveil2.zip——轻量级jQuery图片懒加载插件解析》 在当前互联网技术高速发展的时代,前端开发面临着优化网页性能、提高用户体验的重任。在这个背景下,"unveil2"作为一个非常轻量级的jQuery插件...

    Vec2Face Unveil Human Faces from their Blackbox Features inFace Recognition

    包括Vec2Face Unveil Human Faces from their Blackbox Features inFace Recognition论文和文献阅读报告

    unveiljs一个非常轻量级的图片延迟加载jQuery插件

    2. **初始化unveil**:在DOM加载完成后,使用`$("img").unveil();`初始化unveil.js,选择器可以针对页面上的所有图片或其他需要延迟加载的元素。 3. **设置占位符**:为防止图片未加载时出现空白,可以在图片标签中...

    滑动加载unveil

    在unveil-master这个项目中,很可能包含了一个实现滑动加载的示例代码或框架,包括JavaScript文件、CSS样式表和可能的HTML模板。通过研究这些文件,开发者可以学习如何在自己的项目中实现滑动加载功能,提升网页性能...

    unveil:响应式Javascript演示库

    "unveil:响应式Javascript演示库"是一个专注于创建响应式和交互式演示的JavaScript库。这个库的主要目标是帮助开发者和设计师制作出引人入胜、适应不同屏幕尺寸的在线内容展示。通过使用unveil,你可以创建出在桌面...

    unveil-rs:Unveil Rs是一种工具,可从受Reveal.js,mdbook和zola启发的markdown中创建演示文稿

    Unveil Rs是从markdown文件创建演示文稿的工具。 它的灵感来自 , 和 。 它是什么样子的 ? 观看。 安装 来自crates.io 目前,仅在提供。 首先,您需要安装rust,然后在终端中键入以下命令: cargo install ...

    unveil-rs:OpenBSD 的 Rust 绑定揭幕 (2)

    use unveil :: unveil; fn main () { let path = "public.txt" ; let contents = b"Hello world!" ; File :: create (path). unwrap (). write_all (contents). unwrap (); // Restrict filesystem view by only...

    react-unveil:跨浏览器的ShowMore组件,用于具有合理默认值的React

    React揭开面纱 轻巧且可自定义的React组件,可通过限制内容高度...演示版安装npm install --save react-unveil用法import Unveil from 'react-unveil' ;class Demo { render ( ) { return ( &lt; Unveil maxHeight = {

    unveil-rails:使用brew.js延迟加载Rails的图像

    Unveil.js for Rails 在轨道上延迟加载图像。 如果您想快速使用带有rails的 ,现在可以使用它。 通过加载显示在视口中而不是加载的图像,可以提高Rails应用程序的页面速度。 安装 将Gemfile -rails gem添加到您的...

    网站延时加载

    具体实现这一功能,通常会使用到一些JavaScript库,比如jQuery的一个插件——`jquery.unveil.js`或其压缩版本`jquery.unveil.min.js`。这个插件的工作原理是监听滚动事件,当图片进入视口时,触发加载。使用方法通常...

    jquery image lazy load

    `jquery.unveil.js` 是一个轻量级的 jQuery 图片懒加载插件,它的主要功能包括: 1. **自动绑定**:无需手动为每个图片元素添加事件监听器,插件会自动为所有带有`data-src`属性的图片元素进行绑定。 2. **阈值...

    【高考专题辅导】广东省2014版高考英语 专题检测卷(十二) 阅读理解

    2. **词汇理解**:文章中出现了如“blueprints”(蓝图)、“unveil”(公布)、“drama”(传奇)、“maiden voyage”(首航)等专业或高级词汇,学生需要理解这些词汇在上下文中的含义,以便准确理解文章内容。...

    Machine learning a Bayesian and optimization perspective

    in particular, our intent is to detect and unveil a possible hidden structure and regularity patterns associated with their generation mechanism. This information in turn helps our analysis and ...

    Pro Ajax and Java Frameworks

    Then they unveil a comprehensive Java/Ajax toolkit. Tools include JSEclipse for code editing, Venkman for JavaScript debugging, and Dojo Compressor for code compression. They also explain Log4js (and...

    Inside Microsoft SQL Server 2008 T-SQL Querying

    Delve into the internal architecture of T-SQL—and unveil the power of set-based querying—with comprehensive reference and advice from a highly regarded T-SQL expert and members of Microsoft's SQL ...

    Implementing a Language with LLVM in Objective Caml-llvm.org (2017).pdf

    The goal of this tutorial is to progressively unveil our language, describing how it is built up over time. This will let us cover a fairly broad range of language design and LLVM-specific usage ...

Global site tag (gtag.js) - Google Analytics