| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> Java知识库 -> SpringMVC理论总结 -> 正文阅读 |
|
[Java知识库]SpringMVC理论总结 |
目录 @RestController vs @Controller @RestController 返回JSON 或 XML 形式数据 @Controller +@ResponseBody 返回JSON 或 XML 形式数据 注意:本文参考?怎么理解Spring MVC Controller线程安全性问题_弗兰-随风小欢的博客-CSDN博客_controller线程安全 说说自己对于 Spring MVC 了解?MVC 是模型(Model)、视图(View)、控制器(Controller)的简写,其核心思想是通过将业务逻辑、数据、显示分离来组织代码。 网上有很多人说 MVC 不是设计模式,只是软件设计规范,我个人更倾向于 MVC 同样是众多设计模式中的一种。java-design-patterns?项目中就有关于 MVC 的相关介绍。 ? 想要真正理解 Spring MVC,我们先来看看 Model 1 和 Model 2 这两个没有 Spring MVC 的时代。 Model 1 时代 很多学 Java 后端比较晚的朋友可能并没有接触过 Model 1 时代下的 JavaWeb 应用开发。在 Model1 模式下,整个 Web 应用几乎全部用 JSP 页面组成,只用少量的 JavaBean 来处理数据库连接、访问等操作。 这个模式下 JSP 即是控制层(Controller)又是表现层(View)。显而易见,这种模式存在很多问题。比如控制逻辑和表现逻辑混杂在一起,导致代码重用率极低;再比如前端和后端相互依赖,难以进行测试维护并且开发效率极低。 ? Model 2 时代 学过 Servlet 并做过相关 Demo 的朋友应该了解“Java Bean(Model)+ JSP(View)+Servlet(Controller) ”这种开发模式,这就是早期的 JavaWeb MVC 开发模式。 Model:系统涉及的数据,也就是 dao 和 bean。 View:展示模型中的数据,只是用来展示。 Controller:处理用户请求都发送给 ,返回数据给 JSP 并展示给用户。 ? Model2 模式下还存在很多问题,Model2 的抽象和封装程度还远远不够,使用 Model2 进行开发时不可避免地会重复造轮子,这就大大降低了程序的可维护性和复用性。 于是,很多 JavaWeb 开发相关的 MVC 框架应运而生比如 Struts2,但是 Struts2 比较笨重。 Spring MVC 时代 随着 Spring 轻量级开发框架的流行,Spring 生态圈出现了 Spring MVC 框架, Spring MVC 是当前最优秀的 MVC 框架。相比于 Struts2 , Spring MVC 使用更加简单和方便,开发效率更高,并且 Spring MVC 运行速度更快。 MVC 是一种设计模式,Spring MVC 是一款很优秀的 MVC 框架。Spring MVC 可以帮助我们进行更简洁的 Web 层的开发,并且它天生与 Spring 框架集成。Spring MVC 下我们一般把后端项目分为 Service 层(处理业务)、Dao 层(数据库操作)、Entity 层(实体类)、Controller 层(控制层,返回数据给前台页面)。 Spring MVC的几个区别Spring MVC与Struts1. 机制:spring mvc的入口是servlet,而struts2是filter(这里要指出,filter和servlet是不同的。以前认为filter是servlet的一种特殊),这样就导致了二者的机制不同,这里就牵涉到servlet和filter的区别了。 2. 性能:spring会稍微比struts快。spring mvc是基于方法的设计,而sturts是基于类,每次发一次请求都会实例一个action,每个action都会被注入属性. 而spring基于方法,粒度更细,但要小心把握像在servlet控制数据一样。spring mvc是方法级别的拦截,拦截到方法后根据参数上的注解,把request数据注入进去,在spring mvc中,一个方法对应一个request上下文。 而struts2框架是类级别的拦截,每次来了请求就创建一个Action,然后调用setter getter方法把request中的数据注入;struts2实际上是通过setter getter方法与request打交道的;struts2中,一个Action对象对应一个request上下文。 3、springmvc可以进行单例开发,并且建议使用单例开发,struts2通过类的成员变量接收参数,无法使用单例,只能使用多例。 4、经过实际测试,struts2速度慢,在于使用struts标签,如果使用struts建议使用jstl。 Spring拦截器和过滤器?过滤器(Filter): 依赖于servlet容器,是JavaEE标准,是在请求进入容器之后,还未进入Servlet之前进行预处理,并且在请求结束返回给前端这之间进行后期处理。在实现上基于函数回调,可以对几乎所有请求进行过滤,但是缺点是一个过滤器实例只能在容器初始化时调用一次。 使用过滤器的目的是用来做一些过滤操作。过滤器可以简单理解为“取你所想取”,忽视掉那些你不想要的东西,比如:在过滤器中修改字符编码;在过滤器中修改HttpServletRequest的一些参数,包括:过滤低俗文字、危险字符等。 过滤器底层实现方式是基于函数回调的,自定义过滤器实现一个 doFilter()方法(init()和destroy()方法可以不实现,有默认实现),这个方法有一个FilterChain 参数,而实际上它是一个回调接口,基于函数回调实现。 ApplicationFilterChain是它的实现类, 这个实现类内部也有一个 doFilter() 方法就是回调方法。过滤器Filter触发时机是在请求进入容器后,但在进入servlet(StandWrapper类)之前进行预处理,请求结束是在servlet(StandWrapper类)处理完以后。也可以通过@WebFilter注解实现对特定url拦截。 关于过滤器的一些用法可以参考我写过的这些文章: 继承HttpServletRequestWrapper以实现在Filter中修改HttpServletRequest的参数:https://www.zifangsky.cn/677.html 在SpringMVC中使用过滤器(Filter)过滤容易引发XSS的危险字符:https://www.zifangsky.cn/683.html 拦截器: 拦截器不依赖于servlet容器,依赖于web框架。一个Spring组件,并由Spring容器管理,并不依赖Tomcat等容器,是可以单独使用的。不仅能应用在web程序中,也可以用于Application、Swing等程序中。在SpringMVC中就是依赖于SpringMVC框架,在SSH框架中,就是依赖于Struts框架。 在实现上基于Java的反射机制,属于面向切面编程(AOP)的一种运用。由于拦截器是基于web框架的调用,因此可以使用spring的依赖注入(DI)获取IOC容器中的各个bean,进行一些业务操作,同时一个拦截器实例在一个controller生命周期之内可以多次调用。 但是缺点是只能对controller请求进行拦截,即⑴请求还没有到controller层时进行拦截,⑵请求走出controller层次,还没有到渲染时图层时进行拦截,⑶结束视图渲染,但是还没有到servlet的结束时进行拦截。对其他的一些比如直接访问静态资源的请求则没办法进行拦截处理,拦截器功在对请求权限鉴定方面确实很有用处。它可以简单理解为“拒你所想拒”。 拦截器底层实现方式是基于Java的反射机制(动态代理)实现的。拦截器Interceptor触发时机是在请求进入servlet(StandWrapper类)后,在进入Controller之前进行预处理的,Controller 中渲染了对应的视图之后请求结束。拦截器通过实现HandlerInterceptor接口来实现的,有三个方法preHandle(),postHandle(),afterCompletion()。拦截器是基于Java的反射机制实现,也就是动态代理。 SpringMVC 工作原理Spring MVC 原理如下图所示: SpringMVC 工作原理的图解我没有自己画,直接图省事在网上找了一个非常清晰直观的,原出处不明。 ? ?流程说明(重要): 1 客户端(浏览器)发送请求,直接请求到?
DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用处理器映射器HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象的形式返回; 3 解析到对应的? DispatcherServlet 根据获得的Handler,选择一个合适的处理器适配器HandlerAdapter。(附注:如果成功获得HandlerAdapter后,此时将开始执行拦截器的preHandler(...)方法)
提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作: HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息 数据转换:对请求消息进行数据转换。如String转换成Integer、Double等 数据根式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中 5?Handler执行完成后,向HandlerAdapter返回ModelAndView,HandlerAdapter向DispatcherServlet 返回一个ModelAndView对象;
8 把? 在图中包含 4 个 Spring MVC 接口,即 DispatcherServlet、HandlerMapping、Controller 和 ViewResolver。 HandlerMapping这个组件,它负责的是定位请求处理器Handler。 在HandlerMapping返回处理请求的Controller实例后,需要一个帮助定位具体请求方法的处理类,这个类就是HandlerAdapter,HandlerAdapter是处理器适配器,Spring MVC通过HandlerAdapter来实际调用处理函数。 Spring MVC实现在启动过程中,spring会使用一个默认的WebApplicationContext实现作为IoC容器。这个默认使用的IoC容器就是XMLWebApplicationContext。对于spring承载的web应用而言,可以指定在web应用程序启动时载入IoC容器(或者称为WebApplicationContext)。这个功能是由ContextLoaderListener这样的类来完成的,它是在web容器中配置的监听器(配置在web.xml中)。这个ContextLoader就像spring应用程序在web容器的启动器。该IoC容器会被存储到SevletContext中。 在完成对ContextLoaderListener初始化后,web容器(tomcat)开始初始化DispatcherServlet,DispatcherServlet会建立自己的上下文来持有Spring MVC的Bean对象,在建立这个自己持有的IoC容器时,会从ServletContext中得到根上下文(WebApplicationContext)作为DispatcherServlet持有上下文的双亲上下文。有了这个根上下文,再对自己持有的上下文进行初始化,最后把自己持有的这个上下文保存到ServletContext中,供以后检索和使用。 作为servlet,DispatcherServlet的启动与servlet启动过程是相联系的,servlet的init方法会被调用,以进行初始化,接着会初始化DispatcherServlet持有的IoC容器。 Spring MVC的实现大致由以下几个步骤完成: 1、根据controller和HTTP请求之间的映射关系,将url和handle(controller)作为键值对放到HandlerMapping中的handlerMap(HashMap)中。 2、DispatcherServlet调用doDispatch方法,分发请求。 DispatcherServlet持有IoC容器,里面装有Controller、HandlerMapping、HandlerAdapter、ViewResolver等这些特殊的bean HandlerAdapter:HandlerAdapter将会把处理器包装为适配器,从而支持多种类型的处理器,即适配器设计模式的应用,从而很容易支持很多类型的处理器;如SimpleControllerHandlerAdapter将对实现了Controller接口的Bean进行适配,并且掉处理器的handleRequest方法进行功能处理; ViewResolver:ViewResolver将把逻辑视图名解析为具体的View,通过这种策略模式,很容易更换其他视图技术;如InternalResourceViewResolver将逻辑视图名映射为jsp视图; Spring在web容器中的启动过程spring在web容器中,启动过程是Servlet 容器对spring环境的构造,初始化,装配的过程。 1.通过ContextLoaderListener监听作为启动spring的入口 启动必要条件:在web.xml中配置 ContextLoaderListener(spring中的类)继承ContextLoader(spring中的类),并实现ServletContextListener(servlet中的接口),ServletContextListener监听ServletContext,当容器启动时,会触发ServletContextEvent事件,该事件由ServletContextListener来处理,启动初始化ServletContext时,调用contextInitialized方法。而ContextLoaderListener实现了ServletContextListener,所以,当容器启动时,触发ServletContextEvent事件,让ContextLoaderListener执行实现方法contextInitialized(ServletContextEvent sce);
2.通过initWebApplicationContext方法来初始化WebApplicationContext WebApplicationContext是spring中的上下文。它的作用等同于Servlet中的ServletContext。
initWebApplicationContext(ServletContext servletContext)方法是ContextLoader中的方法。它的作用是制作一个WebApplicationContext上下文,并将这个上下文保存在servletContext中,并保存在当前ContextLoader实例中。 3.如何初始化WebApplicationContext 上面源码中的
通过determineContextClass(ServletContext servletContext)方法获取需要实例化的context类的class,通过BeanUtils.instantiateClass(contextClass)将这个class用反射的手段实例化WebApplicationContext 。 那么determineContextClass怎样来确定实例化那个context类那?(spring有很多的context类实现了WebApplicationContext ,当然这个context类也可以是我们自己写的,具体实例化那个类,在web.xml中配置)
从上面的代码可以看出,先从servletContext中找我们在web.xml中有没有配置要实例化那个上下文context,如果配置了
那么将实例化StaticWebApplicationContext这个上下文。注意:这个地方的param-name必须是contextClass(约定成俗的,其实就是是程序写死的)。如果没有这个配置,那么程序将找到一个叫ContextLoader.properties的配置文件,这个配置文件注明了一个默认的上下文:XmlWebApplicationContext。这个XmlWebApplicationContext实例化的过程是制造一个ResourcePatternResolver的实例,这个实例将会在后面的spring启动过程中起到关键作用。 最后流程图: SpringMVC参数绑定1 SpringMVC初始化时,RequestMappingHandlerAdapter类会把一些默认的参数解析器添加到argumentResolvers中。当SpringMVC接收到请求后首先根据url查找对应的HandlerMethod。 2 遍历HandlerMethod的MethodParameter数组 3 根据MethodParameter的类型来查找确认使用哪个HandlerMethodArgumentResolver,遍历所有的argumentResolvers的supportsParameter(MethodParameter parameter)方法。。如果返回true,则表示查找成功,当前MethodParameter,使用该HandlerMethodArgumentResolver。这里确认大多都是根据参数的注解已经参数的Type来确认。 4 解析参数,从request中解析出MethodParameter对应的参数,这里解析出来的结果都是String类型。 5 转换参数,把对应String转换成具体方法所需要的类型,这里就包括了基本类型、对象、List、Set、Map。 具体源码参考? ?SpringMVC源码之参数解析绑定原理 - 卧颜沉默 - 博客园 @RestController vs @ControllerController 返回一个页面单独使用? @RestController 返回JSON 或 XML 形式数据但 ? @Controller +@ResponseBody 返回JSON 或 XML 形式数据如果你需要在Spring4之前开发 RESTful Web服务的话,你需要使用
? Spring MVC Controller线程安全性问题spring生成对象默认是单例(也就是一个对象)的。通过scope属性可以更改为多例。 对于使用过SpringMVC和Struts2的人来说,大家都知道SpringMVC是基于方法的拦截,而Struts2是基于类的拦截。 对于Struts2来说,因为每次处理一个请求,struts就会实例化一个对象;这样就不会有线程安全的问题了; 而Spring的controller默认是Singleton的,这意味着每一个request过来,系统都会用原有的instance去处理,这样导致两个结果: 一是我们不用每次创建Controller,二是减少了对象创建和垃圾收集的时间;由于只有一个Controller的instance,当多个线程调用它的时候,它里面的instance变量就不是线程安全的了,会发生窜数据的问题。 当然大多数情况下,我们根本不需要考虑线程安全的问题,比如dao,service等,除非在bean中声明了实例变量。因此,我们在使用spring mvc 的contrller时,应避免在controller中定义实例变量。? 解决方案: 有几种解决方法: 1、在Controller中使用ThreadLocal变量 2、在spring配置文件Controller中声明 scope="prototype",每次都创建新的controller 所在在使用spring开发web 时要注意,默认Controller、Dao、Service都是单例的。 |
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/24 11:48:24- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |