avatar

SpringMVC入门

Spring MVC工作流程

SpringMVC是Spring给我们提供的一个用于简化Web开发的框架,Spring MVC本质上可以认为是对Servlet的封装,简化了我们Serlvet的开发

开发过程

  • 配置DispatcherServlet前端控制器
  • 开发具体业务逻辑的Handler(@Controller,@RequestMapping)
  • xml配置文件配置cointrpller扫描,配置springMVC三大件(视图处理器(InternalResourceViewResolver),处理器映射器,处理器适配器)
  • 将xml文件路径告诉SpringMVC(DispatcherServlet)

Spring MVC请求处理流程

流程说明

  • 用户发送请求至前端控制器DispatcherServlet
  • DispatcherServlet收到请求调用HandlerMapping处理器映射器
  • 处理器映射器根据请求URL找到具体的Handler(后端控制器),生成处理对象及处理器拦截器(如果有则生成)一并返回DispatcherServlet
  • DispatcherServlet调用HandlerAdapter处理器适配器去调用handler
  • 处理器适配器执行Handler
  • Handler执行完成给处理器是配置返回ModelAndView
  • 处理器适配器向前端控制器返回ModelANdVide,ModelAndView是SpringMVC框架的一个底层对象,包括Model和View
  • 前端控制器请求视图解析器去进行视图解析,根据逻辑视图名来解析真正的视图
  • 视图解析器想前端控制器返回View
  • 前端控制器进行视图渲染,就是将模型数据(在ModelAndView对象中)填充到request域
  • 前端控制器向用户响应结果

Spring MVC九大组件

  • HanderMapping(处理器映射器)
    HanderMapping使用来查找Handler的,也就是处理器,具体表现形式可以是类(继承Controller接口),也可以是方法(标记Requetsmapping注解)。Handler负责具体实际的请求处理,在请求到达后,Handlermapping的作用便是找到请求响应的处理器Handler和Interceptor

  • HandlerAdapter(处理器适配器)
    HandlerAdapter是一个适配器。因为SpringMVC中Handler可以是任意形式的,只要能处理请求即可。但是把请求交给Servlet的时候,由于Servlet的方法结构都是doService(HttpServletRequest req,HttpServletResponse resp)形式的,要让固定的Servlet处理方法调用Handler来进行处理,便是HandlerAdapter的职责

  • HandlerExceptionResolver(处理器异常解析器)
    HandlerExceptionResolver用于处理Handler产生的异常情况。它的作用是根据异常设置ModelAndView,之后交给渲染方法进行渲染,渲染方法会将ModelandView渲染成页面

  • ViewResolver(视图解析器)
    ViewResolver即视图解析器,用于将String类型的视图名和Locale解析为View类型的视图,只有一个resolveViewName()方法。从方法的定义可以看出,Controller层返回的String类型视图名viewname最终会在这里被解析成为View。View是用来渲染页面的,也就是说,它会将程序返回的参数和数据填入模板中,生成html文件。ViewResolver在这个过程中主要完成两件事情:ViewResolver找到渲染是所用的模板(第一件大事)和所用的技术(第二件大事,其实也就是找到视图的类型,如JSP)并填入参数。默认情况下Spring MVC会自动为我们配置一个InternalResourceViewResolver,是针对JSP类型视图的。

  • RequestToViewnameTranslator
    RequestToViewnameTranslator组件的作用是从请求中获取Viewname。因为ViewResolver根据ViewName查找View,但有的Handler处理完成以后没有设置View,也没有设置ViewName,便要通过这个组件从请求中查找Viewname

  • LocaleResolver
    LocaleResolver组件的resolveViewName需要两个参数,一个是视图名,一个是Locale。LocaleResolver用于请求中解析出Locale,比如中国Locale是zh-CN,用于标识一个区域。这个组件也是i18n的基础

  • ThemeResolver
    ThemeResolver组件是用来解析主题的,主题是样式、图片以及它们所形成的显示效果的集合。Spring MVC中一套主题对应一个properties文件,里面存放着与当前主题相关的所有资源,如图片、css样式等。创建主题非常简单,只需准备好资源,然后新建一个”主题名.properties”并将资源设置进去,放在classpath下,之后便可以在页面中使用了。SpringMVC中与主题有关的类有ThemeResolver、ThemeSource和Theme。ThemeResolver负责从请求中解析出主题名,TnemeSource根据主题名找到具体的主题,其抽象也是Theme。可以通过Theme来获取主题和具体的资源。

  • MultipartResolver
    MultipartResolver用于上传请求,通过将普通的请求包装成MultipartHttpServletRequest来实现。MultipartHttpServletRequest可以通过getFile()方法直接获取到文件。如果上传多个文件,还可以调用getFilemap()方法得到Map<FileName,File>这样的结构,MultipartResolver的作用就是封装普通的请求,使其拥有文件上传的功能

  • FlashMapManager
    FlashMap用于重定向时的参数传递,比如在处理用户订单时候,为了避免重复提交,可以处理完post请求之后重定向到一个get请求,这个get请求可以用来显示订单详情之类的信息。这样做虽然可以避免用户有重新提交订单的问题,但是在这个页面上要显示订单的信息,这些数据从哪里来获得呢,因为重定向时没有传递参数这一功能,如果不想把参数写道URL(不推荐),那么就可以通过FlashMap来传递。只需要在重定向之前将要传递的数据写入请求(可以通过ServletRequestAttributes.getRequest()方法获取)的属性OUTPUT_F:ASH_MAP_ATTRIBUTE中,这样在重定向之后的Handler中Spring就会自动将其设置到Model中,在显示订单信息的页面上就可以直接从Model中获取数据。FlashMapManager就是用来管理FlashMap的。

Spring MVC的url-pattern配置

在我们配置Tomcat中的SpringMVC时,我们除了需要定义DispatcherServletc以外,我们还需要处理Spring MVC能够拦截的请求,实现方式就是需要在web.xml中定义

 <!--设置Spring的Servlet拦截路径-->
<servlet-mapping>
<!--这里的是springmvc,就是上面定义DispatcherServletc起的名字-->
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

url-pattern 的编写规则

  • 带后缀,如.action,.do 这种方式比较精确和方便,在以前和现在企业中都有很大比例在使用
  • / 不会拦截.jsp 但是会拦截.html等静态资源(静态资源:除了servlet和jsp之外的js,css,png等)
  • /* 拦截所有请求,包括资源文件.jsp

question

  • 为什么配置/ 会拦截静态资源
    • 因为在tomcat中存在一个web.xml,在项目中也同时存在一个web.xml,当子xml中的属性与父标签中的属性重复时优先选用父标签中的属性,父标签中有一个DefaultServlet,url-pattern是一个 / 此时我们自己的web.xml中也配置了一个,复写了父web.xml的配置
      <servlet-mapping>
      <servlet-name>default</servlet-name>
      <url-pattern>/</url-pattern>
      </servlet-mapping>
  • 为什么不拦截.jsp呢
    • 父web.xml中有一个JspServlet,这个servlet拦截.jsp文件,而我们并没有覆写这个配置,suoyi springMVC此时不会拦截jsp,jsp的处理交给了tomcat
      <!-- The mappings for the JSP servlet -->
      <servlet-mapping>
      <servlet-name>jsp</servlet-name>
      <url-pattern>*.jsp</url-pattern>
      <url-pattern>*.jspx</url-pattern>
      </servlet-mapping>
  • 如何解决/会拦截静态资源这个问题
    • 在springMVC的配置中添加,添加这个标签之后,会在SpringMVC上下文中定义一个DefaultServletHttpRequestHandler对象,这个对象如同一个检察人员,对进入DispatcherServet的URL请求进行过滤筛查,如果发现是一个静态资源请求,那么会把请求转由web应用服务器(tomcat)默认的DefaultServlet来处理,如果不是静态资源请求,那么继续交由SpringMVC框架处理
      <!--解决/会拦截静态资源-->
      <mvc:default-servlet-handler/>
    • SpringMVC自己处理静态资源,这样让springMVC认为/resources/**的请求去classpath路径下找该静态资源。mapping:静态资源的url规则 location: 指定的静态资源存放位置
      <mvc:resources mapping="/resources/**" location="classpath:"/>

SpringMVC数据封装

//定义一个ModelAndView
@RequestMapping("/handler01")
public ModelAndView handle01(){
Date date = new Date();
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("date",date);
modelAndView.setViewName("success");
return modelAndView;
}
//传入一个ModelMap
@RequestMapping("/handler11")
public String handle11(ModelMap modelMap){
Date date = new Date();
modelMap.addAttribute("date",date);
return "success";
}
//传入一个Model
@RequestMapping("/handler12")
public String handle12(Model model){
Date date = new Date();
model.addAttribute("date",date);
return "success";
}
//传入一个Map
@RequestMapping("/handler13")
public String handle13(Map<String,Object> map){
Date date = new Date();
map.put("date",date);
return "success";
}

以上四种方式都可以进行数据的封装
那么为什么有这么多种方式可以对数据进行封装呢,因为其实传入的类都是一个BandingAwareModelMap,而BandingAwareModelMap继承了ExtendedModelMap
ExtendedModelMap这个类实现了Model接口,继承了ModelMap,而Model和ModelMap都实现了Map方法,所以使用Map、Model、ModelMap都可以进行数据的封装和传递

SpringMVC请求参数绑定

自定义类型转换器 Converter来处理特殊类型如日期格式

SpringMVC高级应用

监听器、过滤器、拦截器对比:

  • 过滤器(Filtre):对Request请求起到过滤的作用,作用在Servlet之前,如果配置为/* 可以对所有的资源访问(sevlet、js/css静态资源等)进行过滤处理
  • 监听器(Listener):实现了javax.servlet.ServletContextListener接口的服务器端组件,它随着web应用的启动而启动,只初始化一次,然后会一直运行监视,随着web应用的停止而销毁
    • 作用一:做一些初始化工作,web应用中Spring启动ContextLoaderListener
    • 作用二:监听web中的特定时间,比如HttpSession,ServletRequest的创建和销毁;变量的创建、销毁和修改等。可以在某些动作前后增加处理,实现监控,比如统计在线人数,利用httpSessionLisener等
  • 拦截器(Interceptor):是SpringMVC、Struts等表现层框架自己的。不会拦截jsp/html/css/image的访问等,只会拦截访问的控制器方法(Handler)。
  • 从配置的角度也能够总结发现:servlet、filter、listener是配置在web.xml中的,而interceptor是配置在表现层框架自己的配置文件中的
    • 在Handler业务逻辑执行之前拦截一次
    • 在Handler逻辑执行完毕但未跳转页面之前拦截一次
    • 在跳转页面之后拦截一次

SpringMVC拦截器

使用方式

  • 编写一个类,实现HandlerInterceptor接口

    public class MyInterceptor implements HandlerInterceptor {
    /**
    * 会在handler方法执行业务逻辑之前执行
    * 往往会完成权限校验
    * @param request
    * @param response
    * @param handler
    * @return 返回值boolean代表是否放行,true代表放行,false代表终止
    * @throws Exception
    */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    return HandlerInterceptor.super.preHandle(request, response, handler);
    }

    /**
    * 会在handler方法业务逻辑执行之后尚未跳转页面时
    * @param request
    * @param response
    * @param handler
    * @param modelAndView 封装了视图和数据,此时尚未跳转页面,可以在这里针对返回的数据和视图信息进行修改
    * @throws Exception
    */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    /**
    * 页面已经跳转渲染完毕之后执行
    * @param request
    * @param response
    * @param handler
    * @param ex 可以在这里捕获异常
    * @throws Exception
    */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
    }
  • 注册该拦截器,在SpringMVC中的配置文件中进行如下配置

    <mvc:interceptors>
    <!--拦截所有的handler-->
    <!-- <bean class="com.zenshin.springmvc.interceptor.MyInterceptor"/>-->
    <mvc:interceptor>
    <!--拦截根目录下得请求-->
    <mvc:mapping path="/**"/>
    <!--排除该类型的所有请求-->
    <mvc:exclude-mapping path="/demo/**"/>
    <bean class="com.zenshin.springmvc.interceptor.MyInterceptor"/>
    </mvc:interceptor>
    </mvc:interceptors>

    单个拦截器执行流程

  • 程序首先执行preHandle()方法,如果该方法的返回值为true,则程序会继续向下执行处理器中的方法,否则将不再向下执行

  • 在业务处理器(即控制器Controller类)处理完请求后,会执行postHandle()方法,然后会通过DispatcherServlet向客户端返回响应

  • 在DispatcherServlet处理完请求后,才会执行afterCompletion()方法

多个拦截器执行流程

当多个拦截器同时工作时,它们的preHandle()方法会按照配置文件中拦截器的配置顺序执行,而它们的postHandle()和afterCompletion()方法则会根据配置文件的顺序的反序执行,类似于栈的一个操作。

SpringMVC异常处理机制

  • 实现接口HandlerExceptionResolver,然后将实现该接口的类加入到SpringMVC中
  • 使用ExceptionHandler注解,标识在要处理的函数上,函数的返回值可以是String(前提方法加上@ResponseBody注解),也可以添加ModelAndView,进行页面跳转
    //在Controller中添加,只对当前的Controller生效
    @ExceptionHandler
    public void handleException(Exception e){
    //异常处理
    }
  • 全局异常捕获ControllerAdvice该注解标识全局处理类
    //可以让我们优雅的捕获所有Controller对象handler方法抛出的异常
    @ControllerAdvice
    public class GlobalExceptionResolver {

    @ExceptionHandler(ArithmeticException.class)
    public ModelAndView handleException(Exception e){
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.addObject("msg",e.getMessage());
    modelAndView.setViewName("error");
    return modelAndView;
    }
    }

重定向参数传递

转发:url不会变,参数也不会丢失,这是一个请求
重定向:url会变,参数会丢失需要重新携带参数,是两个请求

  • 重定向传参方式,使用RedirectAttributes
    @RequestMapping("/handleRedirect")
    public String handleRedirect(String name, RedirectAttributes redirectAttributes){
    //return "redirect:handle01?name="+name;//参数拼接安全性、参数长度都有限制
    redirectAttributes.addAttribute("name",name);//该属性会被暂存到session中,在跳转到页面之后该属性销毁
    return "redirect:handle01";
    }
文章作者: zenshin
文章链接: https://zlh.giserhub.com/2020/05/07/springmvc/helloworld/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 zenshin's blog
打赏
  • 微信
    微信
  • 支付宝
    支付宝

评论