<Web> springMVC翻译
- springMVC的特点
- 清晰的角色定位--每一个模块contorller,vaildator,command object,from object,model object,DispatcherServlet,handler mapping, view resolver等等都有指定的对象来完成。
- 基于框架和应用级别强大又简单的配置--这些配置能包涵跨上下文的引用,比如从controller到业务对象和过滤器。
- 适应性,非侵入式,灵活性--定义controller方法的时候,只要给定场景,就可以用比如@RequestParam(从请求中取值),@RequestHeader(从header中取值),@PathVariable(从url中取值)等。
- 可重用的业务代码,无需复制--将已经存在的业务对象作为命令或form对象,无需将它们映射成一个指定的框架基类。
- 可定制的绑定和验证机制--类型不符作为应用层错误,把错误的值、固定日期、号码绑定等,来取代字符串形式的form对象来手动解析和转换成业务对象。
- 可定制的处理匹配和视图方案--路由匹配和视图解析策略能从简单的基于url的配置,到复杂的专用的解析策略。Spring都比其他要求特定的技术的mvc框架更加灵活。
- 灵活的model转换器--键值对的转换支持和任何视图技术的集成。
- 可定制的本地化,时区和主题方案,Spring tag lib支持JSP,支持JSTL,支持Velocity且无需其他bridge。
- 简单但强大的Spring tag lib提供支持的功能,如数据绑定和主题--自定义标签允许最大的灵活性标记代码。
- JSP form lib让在JSP编写form更加容易。
- Beans的整个生命周期范围作用在当前http请求和http session--这不是springmvc特有的,这是WebApplicationContext容器的特性。
- springMVC的插拔性
- 非springMVC实现也可以是某些项目的优先选择,很多团队都希望利用他们现有的投资技巧和工具,比如用JSF。
- 如果你想用springMVC,你也可以很容易地用spring来集成其他web MVC框架,通过ContextLoadListener来启动spring的根应用上下文,可以在任何一个action中通过ServletContext属性来获取到这个上下文,没有类似于插件来干涉,所以无需专用集成。从整个web层面来看,spring是一个库,root application context是库的入口。
- 无需springMVC,你注册的bean和服务你也可以唾手可得,spring和其他框架没有任何冲突。它只是简单地去解决了很多其他纯MVC框架没有涉及到的领域,无论是bean的配置到数据的获取再到事务的处理等等,所以你可以通过利用spring的中间层或数据获取层来丰富你的应用,即使你只是想用到jdbc或hibernate的事务抽象。
- springMVC的核心
- springMVC和其他mvc框架一样,是请求驱动的,围绕一个中心Servlet来分发请求到contorller并提供其他功能和服务。然而spring的DispatcherServlet要做的事情更多,它完全和spring的ioc容器进行了集成,这样就可以享受到spring的所有特性。
- WebApplicationInitializer是springMVC提供的一个
- springMVC的请求处理流程图如下:
该图可以看出来DispatcherServlet是一个前端控制器(front controller)设计模式。DispatcherSerlvet是一个实体类(继承自HttpServlet),并且要在web.xml中声明,然后指定它要处理的url模式,这是JavaEE Servlet的标准配置,如下:
当然在spring3.0之后,就可以利用java来配置,如下:
WebApplicationInitializer是springMVC提供的一个接口,用来保证基于java的配置能被检测到并自动用于初始化Servlet 3 容器。它有一个抽象的实现类AbstractDispatcherServletInitializer,用它来实现java配置还更加简单,只要配置servlet mapping就足够了。
上面只是配置springMVC的第一步,下面要配置各种各样要用到的bean,DispatcherServlet本身也是一个bean。 - 除了WebApplicationContext之外,springMVC有它自己的WebApplicationContext,它默认继承了在WebApplicationContext中注册的所有bean,这些bean可以在指定serlvet范围内被覆盖,也可以新定义bean来指定它的范围。
在初始化完DispatcherServlet之后,springMVC会去WEB-INF目录下找一个[servlet-name]-serlvet.xml的文件来创建在那里面定义的bean,如果在那里面定义和全局一样id的bean,那么全局bean会被覆盖。
如果在web.xml中有如下所示:
那么在WEB-INF下就需要一个golfing-servlet.xml,在这里面需要定义所有mvc将要用到的组件或bean,当然你也可以通过servlet的init-parameter来改变这个文件存储位置,如下:
WebApplicationContext(根应用上下文)是ApplicationContext的一个扩展,它添加很多在web应用中必要的属性,它和一般的ApplicationContext的不同之处在于它能够解析主题,并且它知道它自己是被关联在哪个servlet上(通过持有一个ServletContext的连接),WebApplicationContext是和ServletContext结合在一起的,通过RequestContextUtils的静态方法可以查看WebApplicationContext的信息。 - WebApplicationContext特定的bean配置
- spring的DispatcherServlet使用特定bean来处理请求和渲染视图,这些bean是springmvc的一部分,你可以通过在WebApplicationContext中简单的配置来指定bean。当然你也可以不用指定,因为springMVC包涵一系列默认的beans。如下:
- 默认的DispatcherServlet配置,上面提到springMVC提供一系列的默认配置,在用org.springframework.web.servlet的DispatcherServlet.properties文件中,所有指定的bean都可以由我们自己来替换,比如最常用到的ViewResolver:
如果这样配置后,那么默认的就会被覆盖掉。 - DispatcherServlet处理顺序
- 当你启动好一个DispatcherServlet之后,过来一个请求,这时会发生:
- 找到WebApplicationContext,将它作为一个attribute绑定在request上,这样controller和其他元素就可以使用了,默认绑定的Key是DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE.
- 那些本地化解析器也将被绑定在request上,以便后面的用来处理请求(渲染视图,准备数据等等),如果你不想要本地解析,那么就不需要这步了。
- 主题解析器也会被绑定在request上,用来决定使用哪个主题,可以忽略这一步。
- 如果你指定了多媒体解析器,那么request会被检验是否是多媒体请求,如果通过,那么HttpRequest会被包装成MultipartHttpRequest,以便后面的进一步处理。
- 查找处理器(Handler),如果找到,execution chain关联上Handler(preprocessors, postprocessors,controllers)就被执行来准备数据模型或视图。
- 如果数据返回,则视图被渲染。如果没有数据(可能由于preprocessors, postprocessors或者安全原因)返回,则视图不会被渲染,因为请求已经完成了。
下面是整个渲染过程: - 异常处理Handler也是在WebApplcationContext声明的,如果在处理请求的过程中遇到问题会抛出这个异常。利用这个handler你也可以在抛出异常时自定义处理行为。
- 由于servlet api中指定的,DispatcherServlet也支持返回last-modification-date,确定处理指定请求的last modification date非常明确:DispatcherServlet查找适当的handler并测试它是否实现了LastModified接口,如果现实了,可以通过getLastModified(request)方法来返回。
- 通过在web.xml中添加servelt initialization parameters,你可以自定义DispatcherServlet实例
- 实现Controller
- controller提供一个平台来让那些定义在application中的service组件得到应用。它能将用户输出转化成一个视图呈现给用户,springmvc提供的controller高度抽象,最大程度保证了使用者创建各种controller时候的灵活性。
- 在spring2.5之后,就一直推荐使用基于注解的编程方式,比如@Controller,@RequestParam,@RequestMapping等等,这种形式省去了继承指定基类或实现指定接口的麻烦,如下:
这些注解让你完全可以自定义类名和方法名(注意struts)。 - @Controller注解表明该类是controller角色的类,当然要注意启用自动检测,如下:
- 通过@RequestMapping来分发请求,如果作用在类上,请求或被引导进入类中,在配合作用在方法上的注解,来确定唯一的请求方法,如下:
还可以如下获取url中的参数,如下:
还可以通过method和consumes(匹配request中的Content-Type)和produces(匹配request中的Accept)来缩小请求范围,如下:
甚至还可以将consumes设置为"!application/json"来匹配除该类型之外的请求。
再甚至可以在@RequestMapping中添加正则表达式来屏蔽不适合的url,如下:
还可以对request或head请求中的参数作要求,"myParsm"、"!myParam"这两个表示这个参数要不要存在,"myParam=myValue"表示该参数要等于指定值,如下:
spring3.2之后更有@MatrixVariable来匹配各式各样的url,可以说springmvc对分发url做了大量优化。 - 定义@RequestMapping处理方法
- 上面我们讲解很多分发url的方法,那么最后请求会进入方法中,下面介绍方法参数类型:
- Servlet API中的对象,HttpServletRequest,HttpServletResponse
- Session对象,比如HttpSession,这个参数要求相应的Session对象存在,不存在则创建,一旦创建之后会一直存在,并且在多请求环境下,修改session可能照成线程不安全问题,可以将RequestMappingHandlerAdapter的synchronizedOnSession设置成true即可。
- @RequestBody可以利用HttpMessageConverter将body转换成方法的参数,HttpMessageConverter还能将Http请求转换成对象,还可以将对象转成Http响应。RequestMappingHandlerAdapter通过如下默认的HttpMessageConverter来支持@RequestBody注解:
其他注解使用见spring注释学习笔记 ; - 异步请求处理
- springMVC 3.2就可以处理基于异步的请求,和同步请求不同,controller会返回一个Callable对象,然后主线程(Servlet Container thread)被释放(返回线程池用于处理其他请求),springMVC获取到Callable对象,然后启动另外一个线程池来执行这个对象,执行完之后,再去Servlet Container中继续找一个线程来处理Callable的返回值:
- 另外一种方式是controller返回一个DeferredResult,这种方式,其返回值也是通过启动另外一个线程来完成这个任务,但是springMVC是不知道的,比如在JMS中,或定时器任务中。
这里如果不了解Serlvet 3的异步处理特性是比较难理解的,下面来稍微讲解一下: - 一个ServletRequest可以通过调用request.startAsync()来启动异步模式,这样做的作用是让Servlet或者Filter可以在response没有被关闭的情况下退出(否则是不行的),返回到Serlvet Container中,response继续等待其他线程完成处理。
- 调用request.startAsync()之后,会返回AsyncContext,它可以被用来控制后面的异步处理,比如它提供了方法调度,应用线程可以调用它来将请求返回给Servlet Container,异步调度和和forword比较相似。
- ServletRequest提供了当前DispatcherType,可以用来区分是Servlet还是Filter正在处理初始化请求。
- 有了上面的讲解,再来看一下springmvc的异步请求:
- 返回Callable
- controller发回Callable
- springMVC开启异步处理,并将Callable提交到另外开启的TaskExecutor的线程中处理
- DispatcherServlet和所有Filter退出request处理线程,但是response还是打开状态
- Callable处理完成返回result,springMVC将request调度到Servlet Container
- DispatcherServlet再次被调用,来继续处理Callable返回的异步请求结果
- 返回DeferredResult
- 返回一个DeferredResult,保存在内存队列或数组中,等待被获取
- springMVC启动异步处理
- DispatcherServlet和所有Filter退出request处理线程,但是response还是打开状态
- Callable处理完成返回result,springMVC将request调度到Servlet Container
- DispatcherServlet再次被调用,来继续处理Callable返回的异步请求结果
- Handler匹配
- 通过HandlerInterceptor拦截请求
- spring的handler匹配机制包括handler拦截器,如果你需要在某些请求上加上一些指定的功能会非有用,比如验证权限。
- 拦截器必须实现HandlerInterceptor,这个接口定义了3个方法,分别是preHandle(处理请求之前)、postHandle(处理请求之后)、afterCompletion(request请求完成之后)。preHandle方法返回一个boolean值,你可以用它来继续或打断执行链的处理,当这个方法返回true,则继续handle,当它返回false,DispatcherServlet就认为拦截器本身已经搞定了请求(如:渲染好了视图),就不再执行其他拦截器以及handler
上面可以看到通过继承HandlerInterceptorAdapter来实现拦截器其实更加方便,因为可以只实现自己想要的方法。 - 视图解析
- 所有web MVC框架都提供一套视图解析机制,spring提供的是视图解析器,它能让数据模型渲染到视图上而不需要指定的视图技术,从大的方面来看,spring可以让你使用JSP,Velocity模版和XSLT视图。
- spring的视图解析有两个非常重要的接口,ViewResolver和View,如下:
ViewResolver提供从视图名称到视图对象的映射(就是根据到WEB-INF下找viewName.jsp文件),View则是完成request的准备工作并将request交给视图技术。 - ViewResolver是如何解析视图的
- 所有在controller里面的handler方法,都必须返回一个逻辑视图名称,不管是明确地通过String,view,modelandview,或不明确地通过转换器,在spring中,视图是通过名称来获取,通过解析器来解析,spring提供的解析器如下:
如果视图技术是JSP,那么可以用UrlBasedViewResolver,这个解析器会将视图名称解析成url,并将对应的request交给DispatcherServlet来渲染视图: - 如果指定的视图解析器没有解析出来结果,spring会查找是否存在其他解析器,如果存在就会用其他解析器继续解析,直到有结果为止,如果还是没有结果,则抛出ServletException。
- 重定向到视图
- 之前提到过,一个controller一般返回一个逻辑视图名称,然后通过特定的视图解析器解析,比如JSP视图技术,是通过Serlvet或JSP引擎来解析的,这部分是在InternalResourceViewResolver和InternalResourceView
- 基于代码的Servlet container初始化
- 在servlet 3.0+的环境下,你可以通过编程的方式来配置Servlet container,如下:
springMVC提供了WebApplicationInitializer接口来保证实现类被检测到,并自动用于初始化任何Servlet 3容器,还有一个抽象基类实现类这个接口,叫做AbstractDispatcherServletInitializer,继承它可以更加容易的注册DispatcherServlet,只需要覆盖方法来指定url匹配模式和DiapatcherServlet配置类(基于java配置)。
基于XML配置:
AbstractDiapatcherServletInitializer也提供了方便的添加Filter实例并将它们自动映射到DispatcherServlet: