精华帖 (0) :: 良好帖 (1) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2009-06-05
迈向SpringMVC的旅程子曾经曰过,叫做“温故可以知新 ”,我想,如果我们简单回顾一下整个Java平台上的Web开发历程,将极大有助于我们理解当前各个Web开发框架存在的背景以及先进性, 最主要的,是有助于我们平滑过渡到SpringMVC的世界中去,所以,首先不妨让我们从最初的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可不只是烦人而已:
Tip请稍微关注以上的servlet代码,当然,这样说并不是因为它臻于完美,而是因为不管这之后的Web应用的开发如何演化,都将以该Servlet为基础进行,不信的话,继续往下看... 为了能够将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拥有比其他通用模板技术更大的能量:
<%@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的诞生: 在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应用程序的原型,但是,切记,不要以这种架构用于实际的生产环境! JSP的不良诱惑使得我们走上了歧路,本已经近在咫尺的良好架构在经历了一段尘封的岁月之后,又重现光芒。 让我们回到JSP时代的开始,在那里,我们让JSP做为视图模板而存在,不管它有多大的能耐,我们只让他负责视图的渲染工作,这样,对于JSP来说,只需要页面开发人员或者说表现层(presentation layer)开发人员来负责和管理即可; 而已经剥离了视图渲染逻辑给JSP的servlet,现在只负责请求处理流程的控制以及与业务层的交互,这部分工作则由Java开发人员来负责,至此,我们不仅将各个关注点清晰的分离出来,而且也同时分离了Java开发人员与前台开发人员之间的职责, 而后者对于一个复杂并且需要多人协作的团队来说,是至关重要的。 通过结合Servlet和JSP,并且引入封装业务层逻辑的JavaBean,我们得到了JSP Model 1的升级版架构,即JSP Model 2: 在Model 2中,就跟我们重构过程结果所展示的那样,由Servlet来管理处理流程,由JSP来负责视图的渲染,并由JavaBean封装业务逻辑并负责与数据层进行交互。 JSP Model 2的提出可以说是Java平台WEB应用开发过程中一个里程碑,它促进了Web开发领域至今一直沿用的MVC设计模式的广泛应用。 JSP Model 2虽然已经具备了使用MVC模式实现的web应用架构的雏形,但并非严格意义上的MVC,为了搞清楚其间的差别,我们先来简单回顾一下MVC模式以及模式中牵扯的几个组件: MVC在当今Java界尤其是WEB开发领域已经是耳熟能详的一个名词了,他的全称是Model-View-Controller,中文通常称为“模型-视图-控制器 ”模式, 如上图所示,最初意义上的MVC模式中,各个组件的作用是:
JSP Model 2实际上已经十分接近Web MVC架构了,但是,在真正步入Web MVC应用框架时代之前,我们还是来看一下JSP Model 2在具体的应用过程中存在哪些问题,我们又是如何来解决这些问题,并进而促成Web MVC应用程序框架的广泛应用的吧! 从JSP Model 2架构的示意图上,我们可以看到Servlet是作为控制器(Controller)的角色存在的,但是,该架构示意图并没有进一步规定具体应用中到底是 只需要一个控制器那还是使用多个控制器, 这就造成两种情况:
Web框架存在的意义在于它们为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)来处理,整个情形看起来类似这个样子: 当控制器servlet接收到web处理请求之后,它会对web请求的URL信息进行分析,然后根据分析结果参照外部可配置的流程规则,将当前web请求转发给次一级的控制器类进行处理,现在, 作为front controller的servlet和次级控制器类共同组成了整个应用程序的控制器(Controller)。 原先单一的控制器servlet通过将流程控制信息外部化,并分离具体的web请求处理逻辑给次级控制器类进行处理的方式,瘦身为灵活而可复用的担当front controller的servlet, 有了这样的关注点分离之后,我们就极大的提高了整个web应用中控制器逻辑的可复用性。 如果我们之前接触过Struts框架的话,对照一下以上的结构,将有助于你更深入的理解该框架中各个组件所扮演的角色:
实际上,SpringMVC可以说是集之前各个web框架的优点于一身,并且还有进一步的发展,虽然最初考虑到Struts的市场地位,笔者并不看好SpringMVC,但是,在深入的了解他之后, 我改变了之初的想法,相信当你更多的了解SpringMVC之后,你也会深深的迷恋于此,话不多说,还是让我们赶快开始我们的SpringMVC之旅吧! 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2009-06-05
数什么时候出版啊?
|
|
返回顶楼 | |
发表时间:2009-06-05
引用 turingbooks 2009-05-21 大概还需要两个月的时间。 请再耐心等待些时日吧 http://turingbook.group.iteye.com/group/topic/10279 |
|
返回顶楼 | |
浏览 2892 次