mvc试图 下拉框不重复_深入浅出MVC,SpringMVC源码分析 Struts2源码分析

第1章 深入浅出 MVC

本章课程介绍的是 MVC 模型,它包含了 Model(模型),View(视图)和 Controller(控制器)。其中 Model,通常指的就是 JavaBean。

View,通常指的是 JSP 或者 HTML(即用于展示数据的资源,包括静态资源和动态资源)。Controller,通常指的是 Servlet 或者 Filter,以及框架中封装的各类控制器。

首先,解释一下 JavaBean,请看下图:

4ff4bce725dad5bec072f90939a01802.png

其次,我们说的 MVC 模型,它是针对于表现层的设计模型。

最后,本课程内容会涉及常见框架 MVC 框架 SpringMVC 和 Struts2 的源码剖析,思路分析以及部分功能的代码实现。要求学习者有一定的前置知识,例如 SpringMVC 的使用,Struts2 的使用等等。

1.1表现层模型 MVC 的由来

1.1.1Model1 模型

Model1 模型是很早以前项目开发的一种常见模型,只有jsp 和 JavaBean 两部分组成。它的优点是:结构简单。开发小型项目时效率高。

它的缺点也同样明显:

第一:JSP 的职责兼顾于展示数据和处理数据(也就是干了控制器和视图的事) 第二:所有逻辑代码都是写在 JSP 中的,导致代码重用性很低。

第三:由于展示数据的代码和部分的业务代码交织在一起,维护非常不便。所以,结论是此种设计模型已经被淘汰没人使用了。

下图展示 Model1 模型的执行过程:

2f10fbf72782b3d387718b55ef7ea985.png

1.1.2Model2 模型

Model2 模型是在 Model1 的基础上进行改良,它是 MVC 的模型的一个经典应用。它把处理请求和展示数据进

行分离,让每个部分各司其职。此时的 JSP 已经就是纯粹的展示数据了,而处理请求的事情交由控制器来完成,使每个组件充分独立,提高了代码可重用性和易维护性。下图展示的就是 Model2 模型:

b9c8701515c6055cb905f09344494629.png

1.2MVC 模型的优略分析

1.2.1MVC 模型的优势

第一:清晰的职责划分。

第二:每个组件作用独立。有利于代码的重用。 第三:由于可重用性强,所以后期维护起来方便。

第四:任何项目都适用(现在没必要照本宣科的说什么适用于大型项目,其实中小型项目的表现层也可以采用

此种模型来开发,并且更易于后期的扩展,即二次开发)

1.2.2MVC 模型的弊端

任何事情都是有其两面性,MVC 模型也并不是全方位优秀的设计模型。它的弊端体现在:

第一:展示数据响应速度慢(这里讨论的是 JSP。因为 jsp 要经过翻译成 java,编译成 class,然后展示) 第二:对开发人员的要求高,需要合理的设计和严谨的架构。

第三:异步交互并不方便(因为响应回 ajax 引擎之后的数据处理,需要有 dom 编程的功底)

1.2.3基于异步请求的MVVM 模式

它全称是 Model View VielModel。是针对 mvc 模型的再次改良,不过只改良了展示数据的部分。

(Controller 的再次优化交给了框架,Model 部分已经无需再优化)

在上一小节,我们提出了异步交互不变的弊端,这主要是在异步展示数据时,javascript 的逻辑处理和数据显示交织在了一起,当我们想进行调整时,需要阅读大量的代码,给后期维护造成了影响。而 MVVM 它把 javascript 的逻辑处理和数据展示分开,可以让使用者在后期维护时,针对不同的需求进行调整。例如:如果是逻辑部分需要处理,则修改逻辑部分代码。如果是数据显示位置需要调整,则修改展示部分的代码,使前端展示更加灵活,也更加合理。

第2章 基于 MVC 模型框架之:SpringMVC

2.1SpringMVC 的源码分析

2.1.1SpringMVC 的执行过程分析

首先,我们先来看一下 springmvc 官方文档中提供的执行过程图。

f386016f966607c245453620f421ab82.png
* @return*/ @RequestMapping("hello2") public String sayHello2() {System.out.println("控制器方法执行了 2");return "success";}}示例代码的整个执行过程如下图所示:
9d23e38323c458a9c43e56259f06e653.png

我们关注的是 DispatcherServlet 是如何找到我们的控制器的,下图展示了代码跟踪,最终发现它是通过反射调用的。

2.1.2SpringMVC 中三大组件详解

2.1.2.1处理器映射器

它指的是:RequestMappingHandlerMapping

是在 Spring 的 3.1 版本之后加入的。它的出现,可以让使用者更加轻松的去配置 SpringMVC 的请求路径映射。去掉了早期繁琐的 xml 的配置(关于这个内容,请见 2.1.8 小节)。

它的配置有两种方式:都是在 springmvc.xml 中加入配置。第一种方式:

第二种方式:

mvc:annotation-driven

在这两种方式中,第二种方式更加的完善,它可以帮我们在容器中添加很多的 bean(更多说明请看 2.1.9 小节)。

它起的作用是为我们 建立起 @RequestMapping 注解和控制器方法的对应关系。 并且存在于MappingRegistry 对象中的 mappingLookup 的 Map 中,该 Map 是个 LinkedHashMap。对应关系的建立时机是在应用加载的时候,也就是当服务器启动完成后,这些对应关系已经建立完成了。从而做到在我们访问的时候,

只是从 Map 中获取对应的类和方法的信息,并调用执行。

6c18e1521e658e9ef73e2aa090443a4c.png

2.1.2.2处理器适配器

要清晰的认识 SpringMVC 的处理器适配器,就先必须知道适配器以及它的作用。我们先通过下图,直观的了解一下:

bf225aa069323a101f40dfbfb1f59344.png

通过上面三张图,我们可以直观的感受到,它是把不同的接口都转换成了 USB 接口。

带入到我们 SpringMVC 中,就是把不同的控制器,最终都可以看成是适配器类型,从而执行适配器中定义的方法。更深层次的是,我们可以把公共的功能都定义在适配器中,从而减少每种控制器中都有的重复性代码。就如上图中所显示的,像公共数据交换都可以在 USB 接口中定义,而无需在三种不同接口中重复定义。

我们通过 2.1.1 小节,学习了 SpringMVC 的执行过程,最终调用的是前端控制器 DispatcherServlet 的doDispatch 方法,而该方法中的 HandlerAdapter 的 handle 方法实际调用了我们自己写的控制器方法。而我们写的控制方法名称各不一样,它是通过 handle 方法反射调用的。但是我们不知道的是,其实 SpringMVC 中处理器适配器也有多个。

第一个:org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter 使用此适配器,适用的控制器写法:要求实现 Controller 接口

/***@author *@Company http://www.ithiema.com*/public class HelloController2 implements Controller {@Overridepublic ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {ModelAndView mv = new ModelAndView(); mv.setViewName("success");return mv;}}

同时要求我们在 springmvc.xml 中添加:

第二个:org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter 使用此适配器的控制器写法:要求实现 HttpRequestHandler 接口

/**

public class HelloController3 implements HttpRequestHandler {

@Override

public void handleRequest(HttpServletRequest request,

HttpServletResponse response) throws ServletException, IOException { request.getRequestDispatcher("/WEB-INF/pages/success.jsp")

.forward(request,response);

}

}

同时要求我们在 springmvc.xml 中添加:

第 三 个 : org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter 这种方式也是我们实际开发中采用最多的。它的要求是我们用注解@Controller 配置控制器

@Controllerpublic class HelloControler {@RequestMapping("hello")public String sayHello() { System.out.println("控制器方法执行了"); return "success";}}

同时要求我们在 springmvc.xml 中配置:

不过通常情况下我们都是直接配置:

2.1.2.3视图解析器

首先,我们得先了解一下 SpringMVC 中的视图。视图的作用是渲染模型数据,将模型里的数据以某种形式呈现给客户。

为了实现视图模型和具体实现技术的解耦,Spring 在 org.springframework.web.servlet 包中定义了一个高度抽象的 View 接口。

我们的视图是无状态的,所以他们不会有线程安全的问题。无状态是指对于每一个请求,都会创建一个 View

对象。

在 SpringMVC 中常用的视图类型

e5402ff80bce044a6cf1262becb08109.png

接下来就是了解视图解析器的作用。View Resolver 负责将处理结果生成 View 视图,View Resolver 首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View 视图对象,最后对View 进行渲染将处理结果通过页面展示给用户。视图对象是由视图解析器负责实例化。

视图解析器的作用是将逻辑视图转为物理视图,所有的视图解析器都必须实现 ViewResolver 接口。

SpringMVC 为逻辑视图名的解析提供了不同的策略,可以在 Spring WEB 上下文中配置一种或多种解析策略并指定他们之间的先后顺序。每一种映射策略对应一个具体的视图解析器实现类。程序员可以选择一种视图解析器或混用多种视图解析器。可以通过 order 属性指定解析器的优先顺序,order 越小优先级越高,SpringMVC 会按视图解析器顺序的优先顺序对逻辑视图名进行解析,直到解析成功并返回视图对象,否则抛出 ServletException

111d159b1fcd0d37d8819b27e635d6db.png

2.1.3不需要视图解析器的场景分析

在分析之前,我们先需要回顾下控制器方法的返回值,此处我们都是以注解@Controller 配置控制器为例, 控制器的方法返回值其实支持三种方式:

第一种:String 类型。借助视图解析器,可以在指定位置为我们找到指定扩展名的视图。视图可以是 JSP, HTML 或者其他的控制器方法上的 RequestMapping 映射地址。前往指定视图的方式,默认是请求转发,可以通过redirect:前缀控制其使用重定向。

第二种:void,即没有返回值。因为我们在控制器方法的参数中可以直接使用原始 SerlvetAPI 对象HttpServletRequest 和 HttpServletResponse 对象,所以无论是转发还是重定向都可以轻松实现,而无需使用返回值。

第三种:ModelAndView 类型。其实我们跟踪源码可以发现在 DispatcherServlet 中的 doDispatch 方法执行时,HandlerAdapter 的 handle 方法的返回值就是 ModelAndView,只有我们的控制器方法定义为 void 时,才不会返回此类型。当返回值是 String 的时候也会创建 ModelAndView 并返回。

通过上面三种控制器方法返回值,我们可以再深入的剖析一下我们请求之后接收响应的方式,其实无外乎就三

种。

第一种:请求转发第二种:重定向

第三种:直接使用 Response 对象获取流对象输入。可以是字节流也可以是字符流。接下来我们就分析,这三种方式的本质区别。

其中请求转发和重定向的区别相信大家已经很熟悉了。但是它们的共同点呢?就是都会引发页面的跳转。

在我们的实际开发中,如果我们不需要页面跳转,即基于 ajax 的异步请求,用 json 数据交互时,即可不配置任何视图解析器。前后端交互是通过 json 数据的,利用@RequestBody 和@ResponseBody 实现数据到 java

对象的绑定(当然还要借助 Jackson 开源框架)。

2.1.4请求参数封装的实现原理

在使用 SpringMVC 实现请求参数封装时,它支持基本类型,POJO 类型和集合类型。其封装原理其实就是使用我们原始的 ServletAPI 中的方法,并且配合反射实现的封装。

此处我们以最简单的 String 和 Integer 两个方法为例,带着大家把整个执行过程走一圈。先来看控制器的方法:

/***

Title: HelloControler

*

Company: http://www.itheima.com/

*/ @Controllerpublic class HelloControler {@RequestMapping("hello")public String sayHello(String name,Integer age) { System.out.println("控制器方法执行了"+name+
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值