写在前面
生活就像海洋,只有意志坚强的人才能到达彼岸。
已经很久没有发文章了呀,想必大家都挂念我了,哈哈。
温故而知新,今天一起来复习一下 spring mvc 的内容吧。
spring mvc 简介与运行原理
Spring 的模型 - 视图 - 控制器(MVC)框架是围绕一个 DispatcherServlet 来设计的,这个 Servlet 会把请求分发给各个处理器,并支持可配置的处理器映射、视图渲染、本地化、时区与主题渲染等,甚至还能支持文件上传。
主要注解
ContextLoaderListener
在讲 ContextLoaderListener 之前,首先来了解一下 web.xml 的作用。
而 spring mvc 启动过程大致分为两个过程:
其中 ContextLoaderListener 监听器它实现了 ServletContextListener 这个接口,在 web.xml 配置这个监听器,启动容器时,就会默认执行它实现的方法。在 ContextLoaderListener 中关联了 ContextLoader 这个类,所以整个加载配置过程由 ContextLoader 来完成。
- <!-- 配置contextConfigLocation初始化参数 -->
- <context-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>/WEB-INF/applicationContext.xml</param-value>
- </context-param>
- <!-- 配置ContextLoaderListerner -->
- <listener>
- <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
- </listener>
ServletContextListener 接口有两个方法: contextInitialized,contextDestroyed
DispatcherServlet
Spring MVC 框架,与其他很多 web 的 MVC 框架一样:请求驱动;所有设计都围绕着一个中央 Servlet 来展开,它负责把所有请求分发到控制器;同时提供其他 web 应用开发所需要的功能。不过 Spring 的中央处理器,DispatcherServlet,能做的比这更多。
下图展示了 Spring Web MVC 的 DispatcherServlet 处理请求的工作流。熟悉设计模式的朋友会发现,DispatcherServlet 应用的其实就是一个 "前端控制器" 的设计模式(其他很多优秀的 web 框架也都使用了这个设计模式)。
- <!-- servlet定义 -->
- <servlet>
- <servlet-name>dispatcher</servlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
- <load-on-startup>1</load-on-startup>
- </servlet>
- <servlet-mapping>
- <servlet-name>dispatcher</servlet-name>
- <url-pattern>/</url-pattern>
- </servlet-mapping>
其中
在 Spring MVC 中,每个 DispatcherServlet 都持有一个自己的上下文对象 WebApplicationContext,它又继承了根(root)WebApplicationContext 对象中已经定义的所有 bean。这些继承的 bean 可以在具体的 Servlet 实例中被重载,在每个 Servlet 实例中你也可以定义其 scope 下的新 bean。
WebApplicationContext 继承自 ApplicationContext,它提供了一些 web 应用经常需要用到的特性。它与普通的 ApplicationContext 不同的地方在于,它支持主题的解析,并且知道它关联到的是哪个 servlet(它持有一个该 ServletContext 的引用)
spring mvc 同时提供了很多特殊的注解,用于处理请求和渲染视图等。DispatcherServlet 初始化的过程中会默认使用这些特殊 bean 进行配置。如果你想指定使用哪个特定的 bean,你可以在 web 应用上下文 WebApplicationContext 中简单地配置它们。
其中,常用的 ViewResolver 的配置。以 jsp 作为视图为例
- <!-- 对模型视图名称的解析,即在模型视图名称添加前后缀 -->
- <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
- <property name="prefix" value="/WEB-INF/jsp/" />
- <property name="suffix" value=".jsp" />
- </bean>
配置上传文件限制 MultipartResolver
- <!-- 上传限制 -->
- <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
- <!-- 上传文件大小限制为31M,31*1024*1024 -->
- <property name="maxUploadSize" value="32505856" />
- </bean>
applicationContext.xml 中的标签
文件上传
前面说到 DispatcherServlet 中有个特殊的 Bean 叫 MultipartResolver,可用于限制文件的上传大小等。当解析器 MultipartResolver 完成处理时,请求便会像其他请求一样被正常流程处理。
- <form method="post" action="/form" enctype="multipart/form-data">
- <input type="text" name="name" />
- <input type="file" name="file" />
- <input type="submit" />
- </form>
- @RequestMapping(path = "/form", method = RequestMethod.POST)
- public String handleFormUpload(@RequestParam("name") String name,
- @RequestParam("file") MultipartFile file) {
- if (!file.isEmpty()) {
- byte[] bytes = file.getBytes();
- // store the bytes somewhere
- return "redirect:uploadSuccess";
- }
- return "redirect:uploadFailure";
- }
异常处理
先来说下常见的异常处理有几种方式,如下图:
Spring 的处理器异常解析器 HandlerExceptionResolver 接口的实现负责处理各类控制器执行过程中出现的异常。也是上面提到的,是 DispatcherServlet 中的特殊 bean,可以自定义配置处理。
某种程度上讲,HandlerExceptionResolver 与你在 web 应用描述符 web.xml 文件中能定义的异常映射(exception mapping)很相像,不过它比后者提供了更灵活的方式。比如它能提供异常被抛出时正在执行的是哪个处理器这样的信息。
- public interface HandlerExceptionResolver {
- ModelAndView resolveException(
- HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex);
- }
- @ExceptionHandler(Exception.class) public Object exceptionHandler(Exception ex, HttpServletResponse response, HttpServletRequest request) throws IOException {
- String url = "";
- String msg = ex.getMessage();
- Object resultModel = null;
- try {
- if (ex.getClass() == HttpRequestMethodNotSupportedException.class) {
- url = "admin/common/500";
- System.out.println("--------毛有找到对应方法---------");
- } else if (ex.getClass() == ParameterException.class) { //自定义的异常
- } else if (ex.getClass() == UnauthorizedException.class) {
- url = "admin/common/unauth";
- System.out.println("--------毛有权限---------");
- }
- String header = req.getHeader("X-Requested-With");
- boolean isAjax = "XMLHttpRequest".equalsIgnoreCase(header);
- String method = req.getMethod();
- boolean isPost = "POST".equalsIgnoreCase(method);
- if (isAjax || isPost) {
- return Message.error(msg);
- } else {
- ModelAndView view = new ModelAndView(url);
- view.addObject("error", msg);
- view.addObject("class", ex.getClass());
- view.addObject("method", request.getRequestURI());
- return view;
- }
- } catch(Exception exception) {
- logger.error(exception.getMessage(), exception);
- return resultModel;
- } finally {
- logger.error(msg, ex);
- ex.printStackTrace();
- }
- }
- <!-- 默认的错误处理页面 -->
- <error-page>
- <error-code>403</error-code>
- <location>/403.html</location>
- </error-page>
- <error-page>
- <error-code>404</error-code>
- <location>/404.html</location>
- </error-page>
- <!-- 仅仅在调试的时候注视掉,在正式部署的时候不能注释 -->
- <!-- 这样配置也是可以的,表示发生500错误的时候,转到500.jsp页面处理。 -->
- <error-page>
- <error-code>500</error-code>
- <location>/500.html</location>
- </error-page>
- <!-- 这样的配置表示如果jsp页面或者servlet发生java.lang.Exception类型(当然包含子类)的异常就会转到500.jsp页面处理。 -->
- <error-page>
- <exception-type>java.lang.Exception</exception-type>
- <location>/500.jsp</location>
- </error-page>
- <error-page>
- <exception-type>java.lang.Throwable</exception-type>
- <location>/500.jsp</location>
- </error-page>
- <!-- 当error-code和exception-type都配置时,exception-type配置的页面优先级高及出现500错误,发生异常Exception时会跳转到500.jsp-->
解答:如果 resolveException 返回了 ModelAndView,会优先根据返回值中的页面来显示。不过,resolveException 可以返回 null,此时则展示 web.xml 中的 error-page 的 500 状态码配置的页面。
当 web.xml 中有相应的 error-page 配置,则可以在实现 resolveException 方法时返回 null。
API 文档中对返回值的解释:
return a corresponding ModelAndView to forward to, or null for default processing.
写在最后
下篇文章将会写 Spring aop 的内容,同样以思维导图的方式编写。可视化学习,让 java 不再难懂。
来源: http://www.cnblogs.com/wlsblog/p/7354801.html