SSM框架之Spring MVC小结

Spring MVC总结

1.Spring MVC的基本配置

1)web.xml配置

    <!--前端控制器-->
    <servlet>
        <servlet-name>springDispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <!-- contextConfigLocation:指定SpringMVC配置文件位置 -->
            <!-- 如果不指定配置文件位置,则会默认找寻一个文件:
            /WEB-INF/xxx-servlet.xml-----xxx为在web中配置的前端控制器servlet名字 -->
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <!-- servlet启动加载,servlet原本是第一次访问创建对象;
        load-on-startup:服务器启动的时候创建对象;值越小优先级越高,越先创建对象;
         -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <!-- Map all requests to the DispatcherServlet for handling -->
    <servlet-mapping>
        <servlet-name>springDispatcherServlet</servlet-name>
        <!--
            /*和/都是拦截所有请求; /:会拦截所有请求,但是不会拦截*.jsp;能保证jsp访问正常;
            /*的范围更大;还会拦截到*.jsp这些请求;一但拦截jsp页面就不能显示了;
          -->
        <url-pattern>/</url-pattern>
    </servlet-mapping>
<!--	encoding设置请求编码,forceEncoding设置响应体编码-->
<!-- 一定注意:字符编码filter的位置一般都在其他Filter之前 -->
    <filter>
        <filter-name>CharacterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <!-- encoding:指定解决POST请求乱码,GET请求乱码在大 -->
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
         <!-- forceEncoding:解决响应乱码(默认不解决响应乱码):底层调用response.setCharacterEncoding(this.encoding); -->
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
<!--	支持rest风格编码方式-->
    <filter>
        <filter-name>hiddenHttpMethodFilter</filter-name>
        <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>hiddenHttpMethodFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

Tips:拦截路径/和/*区别
/:拦截所有请求(静态资源,servlet),不拦截jsp页面
/*:拦截所有请求,包括jsp页面
       所有项目的小web.xml都是继承于tomcat的大web.xml,在tomcat中DefaultServlet是处理静态资源的(除jsp和servlet外都是静态资源),而前端控制器的“/”禁用了tomcat服务器中的DefaultServlet
       但是没有覆盖服务器中的JspServlet的配置,大web中有一个jspservlet,设置的拦截路径是.jsp,所以写/能访问到jsp页面

2)springmvc配置文件

    <context:component-scan base-package="com"/>
<!--    视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
<!--    解析静态资源,如html,js等-->
    <mvc:default-servlet-handler/>
<!--    解析动态资源-->
    <mvc:annotation-driven></mvc:annotation-driven>

2.@RequestMapping详解

1)用法

@RequestMapping注解标注在方法上,是告诉SpringMVC这个方法处理的请求路径, /代表从当前项目下开始,可以省略
@RequestMapping注解标注在类上,是给类中所有的@RequestMapping标注的方法加一个基础路径

   @RequestMapping("/hello")
   public String myfirstRequest(){
      System.out.println("请求收到了....正在处理中");
      //视图解析器自动拼串;
//    <property name="prefix" value="/WEB-INF/pages/"></property>
//    <property name="suffix" value=".jsp"></property>
      //   (前缀)/WEB-INF/pages/+返回值(success)+后缀(.jsp)
      return "success";
   }

2)@RequestMapping属性

  • method:限定请求方式,默认是任何请求方式都接受
           HTTP协议中的所有请求方式:
                  【GET】,【POST】,HEAD, PUT, PATCH, DELETE, OPTIONS, TRACE
    GET、POST
           eg:method=RequestMethod.POST:只接受post类型的请求
  • params:规定请求参数
           params 和 headers支持简单的表达式:
                  param1: 表示请求必须包含名为 param1 的请求参数
           eg:params={“username”}:发送请求的时候必须带上一个名为username的参数;没带都会404
                  !param1: 表示请求不能包含名为 param1 的请求参数
           eg:params={"!username"}:发送请求的时候必须不携带上一个名为username的参数;带了都会404
                  param1 != value1: 表示请求包含名为 param1 的请求参数,但其值不能为 value1
            eg:params={“username!=123”}:发送请求的时候;携带的username值必须不是123(不带username或者username不是123)
  • headers:规定请求头;也和params一样能写简单的表达式
           User-Agent:浏览器信息;
                  eg:让火狐能访问,让谷歌不能访问
    谷歌User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36
    火狐User-Agent:Mozilla/5.0 (Windows NT 6.3; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0
@RequestMapping(value="/hello",headers={"User-Agent=Mozilla/5.0 (Windows NT 6.3; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0"})
   public String handle04(){
        System.out.println("hello....");
        return "success";
   }
  • consumes:只接受内容类型是哪种的请求,即规定请求头中的Content-Type
  • produces:告诉浏览器返回的内容类型是什么,相当于给响应头中加上Content-Type:text/html;charset=utf-8

3)@RequestMapping模糊匹配

优先级:?>*>**

  • ?:能替代任意一个字符,不能匹配空字符,路径符“/”也不能匹配
    @RequestMapping("/antTest0?")
    public String antTest02(){
        System.out.println("antTest02...");
        return "success";
    }
  • 能替代任意多个字符,或一层路径
    @RequestMapping("/a/*/antTest01")
    public String antTest04(){
        System.out.println("antTest04...");
        return "success";
    }
  • **:能替代多层路径
    @RequestMapping("/a/**/antTest01")
    public String antTest05(){
        System.out.println("antTest05...");
        return "success";
    }
  • 路径上可以有占位符,占位符可以在方法中获取到
    语法:在任意路径的地方写一个{变量名},可以写多个获取多级路径,egeg:/{haha}/{id}
    @RequestMapping("/user/{id}")
    public String pathVariableTest(@PathVariable("id")String id){
        System.out.println("路径上的占位符的值"+id);
        return "success";

    }

4)@RequestMapping获取请求参数

(1)获取请求参数:直接给方法入参上写一个和请求参数名相同的变量

请求参数中有值,没带则为null

<a href="handle01?user=lily">handle01</a><br/>
@RequestMapping("/hello")
public String handle01(String user) {
   System.out.println(user);//打印lily
}
(2)@RequestParam:获取请求参数

@RequestParam(“user”) 相当于String username =request.getParameter(“user”)
如果在方法参数中标记了该注解,默认在请求中参数是必须带该参数
value/name:请求参数名
required:参数是否必须要带(可写成http://…/hello?)
defaultValue:没带参数时的默认值

(3)@RequestHeader:获取请求头中某个key的值

@RequestHeader(“User-Agent”)相当于String userAgent userAgent =request.getHeader(“User-Agent”)
默认也是必须带
和上面一样有三个参数:value/required(是否要求请求头中可以没有value所对应的请求头)/defaultValue

(4)@CookieValue:获取某个cookie的值

@CookieValue(value=“JSESSIONID”,required=false)
相当于:Cookie[] cookies = request.getCookies(); (获取全部cookies)
(然后遍历)for(Cookie c:cookies){
if(c.getName().equals(“JSESSIONID”))
{String cv = c.getValue(); } }
也有三个参数:value/required(浏览器第一次访问网站就没有jsessionid)/defaultValue



    @RequestMapping("/handle01")
    public String handle02(
            @RequestParam(value = "user", required = false, defaultValue = "lily") String username,
            @RequestHeader(value = "AHAHA", required = false, defaultValue = "hhh") String userAgent,
            @CookieValue(value="JSESSIONID",required=false)String jid) {
        System.out.println("这个变量的值:" + username);
        System.out.println("请求头中浏览器的信息:" + userAgent);
        System.out.println("cookie中的jid的值"+jid);
        return "success";
    }
(5)POJO(plain old java object):请求参数是一个POJO,SpringMVC会自动的为这个POJO进行赋值

1)、将POJO中的每一个属性,从请求参数中尝试获取出来,并封装即可。要求请求参数的参数名和对象中的属性名要一一对应
2)、还可以级联封装,即属性的属性
POJO对象

//该类一定要有无参构造器
public class Book {
   
   private String bookName;
   private String author;
   private Double price;
   private Integer stock;
   private Integer sales;
   private Address address;
   
   /**
    * @return the bookName
    */
   public String getBookName() {
      return bookName;
   }
   /**
    * @return the address
    */
   public Address getAddress() {
      return address;
   }
   /**
    * @param address the address to set
    */
   public void setAddress(Address address) {
      this.address = address;
   }
   /**
    * @param bookName the bookName to set
    */
   public void setBookName(String bookName) {
      this.bookName = bookName;
   }
   /**
    * @return the author
    */
   public String getAuthor() {
      return author;
   }
   /**
    * @param author the author to set
    */
   public void setAuthor(String author) {
      this.author = author;
   }
   /**
    * @return the price
    */
   public Double getPrice() {
      return price;
   }
   /**
    * @param price the price to set
    */
   public void setPrice(Double price) {
      this.price = price;
   }
   /**
    * @return the stock
    */
   public Integer getStock() {
      return stock;
   }
   /**
    * @param stock the stock to set
    */
   public void setStock(Integer stock) {
      this.stock = stock;
   }
   /**
    * @return the sales
    */
   public Integer getSales() {
      return sales;
   }
   /**
    * @param sales the sales to set
    */
   public void setSales(Integer sales) {
      this.sales = sales;
   }
   /* (non-Javadoc)
    * @see java.lang.Object#toString()
    */
   /* (non-Javadoc)
    * @see java.lang.Object#toString()
    */
   @Override
   public String toString() {
      return "Book [bookName=" + bookName + ", author=" + author + ", price="
            + price + ", stock=" + stock + ", sales=" + sales
            + ", address=" + address + "]";
   }
}

JSP页面部分代码

   书名:<input type="text" name="bookName"/><br/>
   作者:<input type="text" name="author"/><br/>
   价格:<input type="text" name="price"/><br/>
   库存:<input type="text" name="stock"/><br/>
   销量:<input type="text" name="sales"/><br/>
   省:<input type="text" name="address.province"/>
   市:<input type="text" name="address.city"/>
   街道:<input type="text" name="address.street"/><br/>
@RequestMapping("/book")
public String addBook(Book book){
   System.out.println("我要保存的图书:"+book);
   return "success";
}
(6)直接在参数上写原生API

原生API能使用的对象:
HttpServletRequest
HttpServletResponse
HttpSession
java.security.Principal(不用管)
Locale:国际化有关的区域信息对象
InputStream:ServletInputStream inputStream = request.getInputStream();
OutputStream:ServletOutputStream outputStream = response.getOutputStream();
Reader:BufferedReader reader = request.getReader();
Writer:PrintWriter writer = response.getWriter();

@RequestMapping("/handle03")
    public String handle03(HttpSession session,HttpServletRequest request,HttpServletResponse response) throws IOException {
        request.setAttribute("reqParam", "我是请求域中的");
        session.setAttribute("sessionParam", "额我是Session域中的");
        return "success";
    }
}

5)@RequestMapping数据输出

(1)传入Map\Model\ModelMap

此三者是同一对象(BindingAwareModelMap类型),即地址一样,给这些参数里面保存的所有数据都会放在请求域(requestscope)中,可以在页面获取,eg:${requestscope.username}

(2)方法的返回值可以为ModelAndView

包含视图信息(页面地址,字符串)+模型数据(给页面带的数据)
而且数据是放在请求域中(request)(因为请求域方便)

    @RequestMapping("/handle04")
    public ModelAndView handle04(){
        //之前的返回值我们就叫视图名;视图名视图解析器是会帮我们最终拼串得到页面的真实地址;
        //ModelAndView mv = new ModelAndView("success");
        ModelAndView mv = new ModelAndView();
        mv.setViewName("success");
        mv.addObject("msg", "你好哦!");
        return mv;
    }
(3)@SessionAttributes(只能标在类上):给Session域中保存数据的方式,在请求域(model等)中也同时保存

此方法将会给BindingAwareModelMap中保存的数据,或者ModelAndView中的数据,同时给session中放一份,即session和request中都有

@SessionAttributes(value=“msg”): value指定保存数据时要给session中放的数据的key,如果有多个键,用大括号括起来写多个 value={“msg”,“haha”}

value=“msg”:只要保存的是这种key的数据,给Session中放一份,
types={String.class}:只要保存的是这种类型的数据,给Session中也放一份
两个同时用取并集
jsp页面取值:${Sessionscope.haha}

后来推荐@SessionAttributes就别用了,因为如果没有向其中保存值而又声明了该注解,在取值的时候会报错,如果要给session中放数据建议使用原生API

@SessionAttributes(value={"msg"},types={String.class})
@Controller
public class OutputController{...}
(4)@ModelAttribute:调用目标方法前会优先依次调用标注了该注解的方法

适用于并非全字段(即对象的所有字段)修改场景

例如要修改数据库中的图书信息,不应该是直接new一个Book对象,而是应该先从数据库中查询出该图书对象,再对其进行修改:

@Controller
public class ModelAttributeTestController { 
     /**
     * @ModelAttribute:可以放在方法参数和方法声明前两个地方
     *      方法:这个方法就会提前于目标方法先运行;
     *          1)我们可以在这里提前查出数据库中图书的信息
     *          2)将这个图书信息保存起来(方便下一个方法还能使用)
     *      参数:取出刚才保存的数据
     */
    @ModelAttribute
    public void hahaMyModelAttribute(Map<String, Object> map){
        Book book = BookService.getBookById(1);//先在数据库中查到的book数据
        map.put("book", book);
    }


    /**
     * 可以告诉SpringMVC不要new这个book了我刚才保存了一个book;
     * 哪个就是从数据库中查询出来的;用我这个book?@ModelAttribute("book")-
     *
     * model同都是BindingAwareModelMap并且和上面的map对象是同一个,所以在此处取出上面方法map放置的值是可以的
     */
    @RequestMapping("/updateBook")
    public String updateBook(@ModelAttribute("book")Book book,Map<String, Object> model){
        Book haha = (Book)model.get("book");
        //对图书进行修改
        ...
        return "success";
    }
}

3.视图解析器

视图解析器解析方法返回值得到视图对象
视图对象完成真正的转发(将模型数据全部放在请求域中)或者重定向到页面,即视图对象才能真正的渲染视图

1) 在有视图解析器的情况下转发路径写法

//方法一:用相对路径
   @RequestMapping("/hello")
   public String hello(){
      //  要想转发到web下的hello:默认视图解析器会拼串,即如果直接写一个hello会解析成web-inf/pages/hello.jsp
      //所以,要写相对路径,就要退出两层,即相对路径写法
      return "../../hello";
   }
//方法二:forward前缀
   /**
    *  forward:转发到一个页面
    *  /hello.jsp:转发当前项目下的hello,“/代表是当前项目下”
    *  
    *  一定加上/,如果不加/就是相对路径,容易出问题
    *  forward:/hello.jsp
    *  forward:前缀的转发,不会由我们配置的视图解析器拼串
    */
   @RequestMapping("/handle01")
   public String handle01(){
      System.out.println("handle01");
      return "forward:/hello.jsp";
   }


   /**
    * 转发到handle01,handle01在转发到hello
    */
   @RequestMapping("/handle02")
   public String handle02(){
      System.out.println("handle02");
      return "forward:/handle01";
   }

2) 有资源解析器的情况下,重定向路径写法

 /**
    * 重定向 redirect:重定向的路径
    * /hello.jsp:代表就是从当前项目下开始;SpringMVC会为路径自动的拼接上项目名,原生的Servlet重定向“/路径”需要加上项目名才能成功
    * 相当于response.sendRedirect("/hello.jsp")
    */
   @RequestMapping("/handle03")
   public String handle03(){
      System.out.println("handle03....");
      return "redirect:/hello.jsp";
   }
   
   @RequestMapping("/handle04")
   public String handle04(){
      System.out.println("handle04...");
      return "redirect:/handle03";
   }

有前缀的转发和重定向操作,配置的视图解析器就不会进行拼串

3)JstlView支持国际化

导包导入了jstl的时候会自动创建为一个jstlView,jstlView可以快速方便的支持国际化功能;
即如果导入jstl包后,写入下面的配置则解析器解析为InternalResourceView的子类jstlView

maven座标:

<dependency>
    <groupId>jstl</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
</dependency>
<dependency>
    <groupId>taglibs</groupId>
    <artifactId>standard</artifactId>
    <version>1.1.2</version>
</dependency>

springmvc配置:

<!--InternalResourceViewResolver:优先级是最低的-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
   <property name="prefix" value="/WEB-INF/pages/"></property>
   <property name="suffix" value=".jsp"></property>
   <!-- <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"></property>  指定视图类型,可以不写-->
</bean>

步骤:

  1. 让Spring管理国际化资源
    <!--配置一个资源文件管理器,id必须叫messageSource  -->
    <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
        <!--  basename指定文件基础名-->
        <property name="basename" value="i18n"></property>
    </bean>
  1. 直接去页面使用<fmt:message>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>
<fmt:message key="welcomeinfo"/>
</h1>
<form action="">
   <fmt:message key="username"/>:<input /><br/>
   <fmt:message key="password"/>:<input /><br/>
   <input type="submit" value='<fmt:message key="loginBtn"/>'/>
</form>
</body>
</html>

注意:因为是spring mvc的视图解析器创建的jstlView,所以转发的视图一定要经过视图解析器的解析,如果直接添加forward或redirect前缀,则国际化无效

4)mvc:view-controller:请求映射标签,直接去到某个页面

<!-- 发送一个请求("toLoginPage")将会直接来到web-inf下的login页面-->
<!-- path="":指定哪个请求
    view-name:指定映射给哪个视图;
    如果只配置mvc:view-controller,其他的注解的requestmapping就不好使-->
<mvc:view-controller path="/toLoginPage" view-name="login"/>

<!-- 所以要开启mvc注解驱动模式-->
<mvc:annotation-driven></mvc:annotation-driven>

5)自定义视图解析器/视图类:实现ViewResolver、View接口

  • 自定义视图解析器类:
//因为还配置有Internel的解析器,要再继承一个order接口改变其顺序,让其先于internel解析器完成解析,要不然进入internel中,internel就会拼串解析一个url然后报错
public class MyCustomViewResolver implements ViewResolver,Ordered{
   private Integer order = 0;
   @Override
   public View resolveViewName(String viewName, Locale locale)
         throws Exception {
      // TODO Auto-generated method stub
      //根据视图名返回视图对象
      if(viewName.startsWith("custom:")){
         return new MyView();
      }else{
         //如果不能处理返回null即可
         return null;
      }
   }
   /**
    *
    */
   @Override
   public int getOrder() {
      // TODO Auto-generated method stub
      return order;
   }
   //改变视图解析器的优先级
   public void setOrder(Integer order){
      this.order = order;
   }
}
  • 自定义视图类
public class MyView implements View{
   /**
    * 返回的数据的内容类型
    */
   @Override
   public String getContentType() {
      return "text/html";
   }

//渲染界面
  @Override
   public void render(Map<String, ?> model, HttpServletRequest request,
         HttpServletResponse response) throws Exception {
      response.setContentType("text/html");
      List<String> vn = (List<String>) model.get("video");
      response.getWriter().write("哈哈<h1>即将展现精彩内容</h1>");
      for (String string : vn) {
         response.getWriter().write("<a>下载"+string+".avi</a><br/>");
      }
   }
}
  • springmvc配置文件
<context:component-scan base-package="com.atguigu"></context:component-scan>


<!--InternalResourceViewResolver:优先级是最低的
  -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
   <property name="prefix" value="/WEB-INF/pages/"></property>
   <property name="suffix" value=".jsp"></property>
</bean>
<!--自定义的视图解析器    value="1"数字越小优先级越高-->
<bean class="com.atguigu.view.MyMeiNVViewResolver">
   <property name="order" value="1"></property>
</bean>

4.REST风格请求

  • REST(Representational State Transfer):(资源)表现层状态转化。HTTP协议是一个无状态协议,即所有的状态都保存在服务器端。因此,如果客户端资源想要操作服务器,必须通过某种手段,让服务器端发生“状态转化”(State Transfer)。

具体说,就是 HTTP 协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源,PUT 用来更新资源,DELETE用来删除资源。

  • 语法:
    GET、POST操作:直接发送请求即可
    DELETE、PUT操作:1、创建一个post类型的表单 2、表单项中携带一个_method的参数,3、这个_method的值就是DELETE、PUT<
<!-- 发送GET请求 -->
<a href="/hello">去hello</a>
<form method="get" action="${ctp}/query/1">
    <input type="submit" value="提交">
</form>
<!-- 发送POST请求 -->
<form method="post" action="${ctp }/add/4">
    <input type="submit" value="提交">
</form>
<!-- 发送DELETE请求 -->
<form method="post" action="${ctp }/delete/3">
    <input name="_method" value="delete"> <%--value(delete)不区分大小写 --%>
    <input type="submit" value="提交">
</form>
<!-- 发送PUT请求 -->
<form method="post" action="${ctp }/update/2">
    <input name="_method" value="put">
    <input type="submit" value="提交">
</form>

JAVA代码:

    @RequestMapping(value = "/hello")
    public String firstReq(){
        System.out.println("来到了hello");
        return "success";
    }

    @RequestMapping(value = "/query/{id}",method = RequestMethod.GET)
    public String queryTest(@PathVariable("id") int id){
        System.out.println("query"+id);
        return "success";
    }
    @RequestMapping(value = "/update/{id}",method = RequestMethod.PUT)
    public String updateTest(@PathVariable("id") int id){
        System.out.println("update"+id);
        return "success";
    }
    @RequestMapping(value = "/delete/{id}",method = RequestMethod.DELETE)
    public String deleteTest(@PathVariable("id") int id){
        System.out.println("delete"+id);
        return "success";
    }
    @RequestMapping(value = "/add/{id}",method = RequestMethod.POST)
    public String addTest(@PathVariable("id") int id){
        System.out.println("add"+id);
        return "success";
    }

注意:高版本Tomcat(8以上)Rest支持有点问题,转发页面success是还携带的是delete或put请求是不允许的,需要将转发页面设置成错误页面
isErrorPage

5.数据转换/格式化/校验

在这里插入图片描述

1)自定义数据转换器

(1)实现Converter接口,写一个自定义的类型转换器
public class MyStringToEmployeeConverter implements Converter<String, Employee> {
   @Autowired
   DepartmentDao departmentDao;
   /**
    * 自定义的转换规则
    */
   @Override
   public Employee convert(String source) {
      // TODO Auto-generated method stub
      // [email protected]
      System.out.println("页面提交的将要转换的字符串" + source);
      Employee employee = new Employee();
      if (source.contains("-")) {
         String[] split = source.split("-");
         employee.setLastName(split[0]);
         employee.setEmail(split[1]);
         employee.setGender(Integer.parseInt(split[2]));
         employee.setDepartment(departmentDao.getDepartment(Integer.parseInt(split[3])));
      }
      return employee;
   }
}
(2)将自定义的Converter放进ConversionService中

因为在spring中,ConversionService是ioc根据工厂方法ConversionServiceFactoryBean创建的,所以配置的时候也在工厂方法中配置

<!-- 告诉SpringMVC别用默认的ConversionService,
            而用我自定义的ConversionService、可能有我们自定义的Converter; -->
    <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <!--converters转换器中添加我们自定义的类型转换器  -->
        <property name="converters">
            <set>
                <bean class="com.atguigu.component.MyStringToEmployeeConverter"></bean>
            </set>
        </property>
    </bean>
(3)让SpringMVC用自定义ConversionService
<!-- conversion-service="conversionService":使用配置的类型转换组件 -->
    <mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>

使用:JSP部分代码

<%
   pageContext.setAttribute("ctp", request.getContextPath());
%>
<form action="${ctp}/quickadd">
   <!--将员工的所有信息都写成固定的  -->
   <input name="empinfo" value="[email protected]"/>
   <input type="submit" value="快速添加"/>
</form>

查看源码可以发现,源码上WebDataBinder上的ConversionService组件就替换了,新增了一个java.lang.String -> com.atguigu.bean.Employee

ConversionService converters =
    java.lang.Boolean -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@47d21368
    java.lang.Character -> java.lang.Number : org.springframework.core.convert.support.CharacterToNumberFactory@3cdad94b
    java.lang.Character -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@2a65d058
    java.lang.Enum -> java.lang.String : org.springframework.core.convert.support.EnumToStringConverter@5b63b99f
    java.lang.Number -> java.lang.Character : org.springframework.core.convert.support.NumberToCharacterConverter@1fee86c
    java.lang.Number -> java.lang.Number : org.springframework.core.convert.support.NumberToNumberConverterFactory@281e80cc
    java.lang.Number -> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@7cd83820
    //新增
    java.lang.String -> com.atguigu.bean.Employee :  com.atguigu.component.MyStringToEmployeeConverter@3dc22c7a
    java.lang.String -> java.lang.Boolean : org.springframework.core.convert.support.StringToBooleanConverter@4331ec46
    java.lang.String -> java.lang.Character : org.springframework.core.convert.support.StringToCharacterConverter@76a136a0
    ....

2)数据格式化

指定数据提交的格式,比如指定日期格式必须为2017-12-19,如果提交格式不是这个格式,就会报错(错误码:400)

如果设置自定义转换器ConversionServiceFactoryBean创建的ConversionService组件,是没有格式化器存在的;默认的转换器才有格式化器

解决方法:在springmvc的配置文件中,将ConversionServiceFactoryBean更改为FormattingConversionServiceFactoryBean,里面除了可以自定义转换器,还保留格式化器

<!-- conversion-service="conversionService":使用我们自己配置的类型转换组件 -->
<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>


<!-- 以后写自定义类型转换器的时候,就使用FormattingConversionServiceFactoryBean来注册;既具有类型转换还有格式化功能 -->
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
   <!--converters转换器中添加我们自定义的类型转换器  -->
   <property name="converters">
      <set>
         <bean class="com.atguigu.component.MyStringToEmployeeConverter"></bean>
      </set>
   </property>
</bean>

日期格式化:


@DateTimeFormat(pattern="yyyy-MM-dd")
private Date birth = new Date();

数字格式化:

//假设页面,为了显示方便提交的工资是  ¥10,000.98
@NumberFormat(pattern="#,###.##")//每个#表示一位,即每隔三位就以逗号分隔,小数点后面保留两位小数
private Double salary;

3)数据校验

对于在重要数据一定要加上后端验证,因为只做前端校验是不安全的:比如浏览器可以禁用js,也可以另外自己写一个页面提交到后端处理器,直接跳过自己写的前端页面

JSR303(Java Specification Requests第303号标准提案)是Java为Bean数据合法性校验提供的框架,通过在Bean的属性上添加注解指定校验规则,并通过指定的接口实现校验
实现之一:Hibernate Validator(第三方校验框架)

(1)注解种类:

在这里插入图片描述
此外,Hibernate Validator除支持所有标准的校验注解外,它还支持以下的扩展注解
在这里插入图片描述

(2)校验步骤

1.添加依赖jar包

hibernate-validator-5.0.0.CR2.jar
hibernate-validator-annotation-processor-5.0.0.CR2.jar
classmate-0.8.0.jar
jboss-logging-3.1.1.GA.jar
validation-api-1.1.0.CR1.jar

或maven依赖项

<dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>6.1.0.Final</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish</groupId>
        <artifactId>javax.el</artifactId>
        <version>3.0.1-b11</version>
    </dependency>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-validator-cdi</artifactId>
        <version>6.1.0.Final</version>
    </dependency>

2.给JavaBean对象属性添加注解

//直接用message属性指定发生错误时的提示消息,但是不能国际化
@NotEmpty(message="非空")
@Length(min=5,max=17,message="长度错误")
private String lastName;

@Email
private String email;

//规定页面提交的日期格式  
//@Past:必须是一个过去的时间
//@Future :必须是一个未来的时间
@DateTimeFormat(pattern="yyyy-MM-dd")
@Past
private Date birth;

3.给参数中的JavaBean对象添加注解@Valid,通知springMVC这个对象需要校验
获取校验结果:给需要校验的javaBean后面紧跟一个BindingResult(两者中间不能有任何其他的东西,必须是紧贴着的)。这个BindingResult就是封装前一个bean的校验结果

@RequestMapping(value = "/emp", method = RequestMethod.POST)
public String addEmp(@Valid Employee employee, BindingResult result)
{
   System.out.println("要添加的员工:" + employee);
   // 获取是否有校验错误
   boolean hasErrors = result.hasErrors();
   if (hasErrors) {
      System.out.println("有校验错误");
      return "add";
   } else {
      employeeDao.save(employee);
      // 返回列表页面;重定向到查询所有员工的请求
      return "redirect:/emps";
   }
}

4.在页面获取校验错误
使用原生标签时:则直接在隐含模型中存值,在页面中用EL表达式取出即可

  List<FieldError> errors = result.getFieldErrors();
      for (FieldError fieldError : errors) {
         errorsMap.put(fieldError.getField(),
               fieldError.getDefaultMessage());
      }
      model.addAttribute("errorInfo", errorsMap);

使用表单标签时:<form:errors path=“每个表单提交项的name值”/>

<form:input path="lastname"/><form:errors path="lastname"/>
<form:input path="email"/><form:errors path="email"/>

补充表单标签:
       对于请求域数据进行页面回显时,原来是在servelt中通过在域中添加属性,再在页面进行EL取值
       现在通过 SpringMVC的表单标签实现将模型数据中的属性和HTML表单元素相绑定,以实现表单数据更便捷编辑和表单值的回显

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!-- 表单标签写法 -->
<form:form action="${ctp }/emp" modelAttribute="employee" method="POST">
   <!-- path就是原来html-input的name项,且能自动回显隐含模型中某个对象对应的这个属性的值 -->
   lastName:<form:input path="lastName"/><br/>
   email:<form:input path="email"/><br/>
   gender:<br/>
      男:<form:radiobutton path="gender" value="1"/><br/>
      女:<form:radiobutton path="gender" value="0"/><br/>
   dept:
      <!--
      items="":指定要遍历的集合 ;遍历出的每一个元素是一个department对象
      itemLabel="属性名":指定遍历出的这个对象的哪个属性是作为option标签体的值
      itemValue="属性名":指定刚才遍历出来的这个对象的哪个属性是作为要提交 的value值
       -->
      <form:select path="department.id"
         items="${depts}"
         itemLabel="departmentName"
         itemValue="id"></form:select><br/>
   <input type="submit" value="保存"/>
</form:form>

Spring MVC规定:path指定属性是从隐含模型(请求域中)取出的某个对象中的属性并且
path指定的每一个属性,请求域中必须有一个对象,拥有这个属性,这个对象在请求域就的key为command
如果想要自定义key的名称,则在表单标签中使用modelAttribute来表明需要回显的对象名称

@RequestMapping("/toaddpage")
public String toAddPage(Model model) {
   // 1、先查出所有部门
   Collection<Department> departments = departmentDao.getDepartments();
   // 2、放在请求域中
   model.addAttribute("depts", departments);
   // 未使用modelAttribute属性:model.addAttribute("command", new Employee(){.....});
   model.addAttribute("employee", new Employee(){.....});
   // 3、去添加页面
   return "add";
}
(3)自定义国际化错误消息

       每个属性在数据绑定和数据校验发生错误时,都会生成一个对应的 FieldError 对象。当一个属性校验失败后,校验框架会为该属性生成 4 个消息代码,这些代码以校验注解类名为前缀,结合 modleAttribute、属性名及属性类型名生成多个对应的消息代码。
       例如:User 类中的 password 属性标注了一个 @Pattern 注解,当该属性值不满足 @Pattern 所定义的规则时, 就会产生以下 4 个错误代码:
Pattern.user.password
Pattern.password
Pattern.java.lang.String
Pattern
       当使用 Spring MVC 标签显示错误消息时, Spring MVC 会查看 WEB 上下文是否装配了对应的国际化消息,如果没有,则显示默认的错误消息,否则使用国际化消息。
       若数据类型转换或数据格式转换时发生错误,或该有的参数不存在,或调用处理方法时发生错误,都会在隐含模型中创建错误消息。其错误代码前缀说明如下:

  1. required:必要的参数不存在。如 @RequiredParam(“param1”) 标注了一个入参,但是该参数不存在
  2. typeMismatch:在数据绑定时,发生数据类型不匹配的问题
  3. methodInvocation:Spring MVC 在调用处理方法时发生了错误

如果是hibernate的自己的注解,是做好了国际化的,但JAVAEE的校验规则没有做国际化,如果需要实现国际化,则:

a.国际化文件中错误消息的key必须对应一个错误代码(codes):
      Email.employee.email,      校验规则.隐含模型中这个对象的key.对象的属性
      Email.email,               校验规则.属性名
      Email.java.lang.String,    校验规则.属性类型
      Email

解释:1、如果是隐含模型中employee对象的email属性字段发生了@Email校验错误,就会生成 Email.employee.email;
2、 Email.email:所有的email属性只要发生了@Email错误;
3、 Email.java.lang.String:只要是String类型属性发生了@Email错误
4、 Email:只要发生了@Email校验错误;
tips:如果同时用两个,会优先用精确的那一个错误消息。从上到下越来越模糊。

因此国际化文件编写:

Email=email error!
NotEmpty=must not empty!
Length.java.lang.String= length incorrect ,must between {2} and {1}!
Past=must a past time!
typeMismatch.birth=birth format error!
b.让SpringMVC管理国际化资源文件
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
    <property name="basename" value="errors"></property>
</bean>
c.高级国际化:动态传入消息参数

{0}:永远都是当前注解下的属性名
{1}、{2}:才是注解后面的参数,而且参数按照字符串顺序排序1,2…
即对于:

@Length(min=5,max=17,message="长度错误")
private String lastName;

提取参数时为,才会显示“5and17”

Length.java.lang.String= length incorrect ,must between {2} and {1}

6.SpringMVC ajax响应和封装json数据

       使用 HttpMessageConverter 将请求信息转化并绑定到处理方法的入参中或将响应结果转为对应类型的响应信息,Spring 提供了两种途径:
1.使用 @RequestBody / @ResponseBody 对处理方法进行标注
2.使用 HttpEntity / ResponseEntity 作为处理方法的入参或返回值
       当控制器处理方法使用到@RequestBody、 @ResponseBody 或HttpEntity、ResponseEntity 时, Spring 首先根据请求头或响应头的 Accept 属性选择匹配的HttpMessageConverter, 进而根据参数类型或泛型类型的过滤得到匹配的 HttpMessageConverter, 若找不到可用的HttpMessageConverter 将报错。

1)SpringMVC-ajax jar包:

jackson-annotations-2.1.5.jar
jackson-core-2.1.5.jar
jackson-databind-2.1.5.jar

maven依赖:

<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.0</version>
</dependency> 

2)ajax返回json数据:在@RequestMapping注解上加@ResponseBody,则直接返回json数据

部分jsp:

<%
   pageContext.setAttribute("ctp", request.getContextPath());
%>
<script type="text/javascript" src="scripts/jquery-1.9.1.min.js"></script>
</head>
<body>
<%=new Date() %>
<a href="${ctp }/getallajax">ajax获取所有员工</a><br/>
<div>
</div>
<script type="text/javascript">
   $("a:first").click(function(){
      //1、发送ajax获取所有员工上
      $.ajax({
         url:"${ctp}/getallajax",
         type:"GET",
         success:function(data){
            $.each(data,function(){
               var empInfo = this.lastName+"-->"+this.birth+"--->"+this.gender;
               $("div").append(empInfo+"<br/>");
            });
         }
      });
      return false;//要禁用掉超链接的默认行为,要不然会以超链接跳转而并非发送ajax
   });
</script>
</body>
/**
* @ResponseBody:将返回的数据()放在响应体中
* 如果是对象,jackson包自动将对象转为json格式
*/
@ResponseBody  
@RequestMapping("/getallajax")
public Collection<Employee> ajaxGetAll(){
   Collection<Employee> all = employeeDao.getAll();
   return all;
}

3)@RequestBody用法

(1)获取请求体

只有POST请求方式才会有请求体

<form action="${ctp}/testRequestBody" method="post">
   <input name="username" value="tomcat" /> 
<input name="password" value="123456">
<input type="submit" />
</form>

   @RequestMapping("/testRequestBody")
   public String testRequestBody(@RequestBody String str){
      System.out.println("请求体:"+str);
      return "success";
   }
(2)接受json数据并封装为对象
<%
   pageContext.setAttribute("ctp", request.getContextPath());
%>
</head>
<script type="text/javascript" src="scripts/jquery-1.9.1.min.js"></script>
<script type="text/javascript">
   $("a:first").click(function() {
      //点击发送ajax请求,请求带的数据是json
      var emp = {
         lastName : "张三",
         email : "[email protected]",
         gender : 0
      };
      //js对象
      var empStr = JSON.stringify(emp);
      $.ajax({
         url : '${ctp}/testRequestBody',
         type : "POST",
         data : empStr,
         contentType : "application/json",
         success : function(data) {
            alert(data);
         }
      });
      return false;
   });
</script>
@Controller
public class AjaxTestController {
   
   @Autowired
   EmployeeDao employeeDao;
   @RequestMapping("/testRequestBody")
   public String testRequestBody(@RequestBody Employee employee){ //@RequestBody标注下,json包直接将数据转为json格式
      System.out.println("请求体:"+employee);
      return "success";
   }
}

3)HttpEntity 获取请求体+请求头

/**
* 比@RequestBody更强,不仅可以拿到请求体,还可以拿到请求头
* 而@RequestHeader("")只能拿到某个请求头
*/
@RequestMapping("/test02")
public String test02(HttpEntity<String> str){
   System.out.println(str);
   return "success";
}

4)ResponseEntity自定义响应体内容

/**
* 返回string时,加@ResponseBody将返回数据放在响应体中,所以return时,写的内容会直接显示在页面上
* 返回ResponseEntity<String>:设置响应体和响应头内容,泛型指的是响应体中内容的类型
*/
//@ResponseBody
/**
* SpringMVC文件下载;
*/
@RequestMapping("/download")
public ResponseEntity<byte[]> download(HttpServletRequest request) throws Exception{
   //1、得到要下载的文件的流,找到要下载的文件的真实路径
   ServletContext context = request.getServletContext();
   String realPath = context.getRealPath("/scripts/jquery-1.9.1.min.js");
   FileInputStream is = new FileInputStream(realPath);
   byte[] tmp = new byte[is.available()];
   is.read(tmp);
   is.close();
   //2、将要下载的文件流返回
   HttpHeaders httpHeaders = new HttpHeaders();
   httpHeaders.set("Content-Disposition", "attachment;filename="+"jquery-1.9.1.min.js");
   return new ResponseEntity<byte[]>(tmp, httpHeaders, HttpStatus.OK);
}

7.拦截器

配置拦截器

SpringMVC的拦截器HandlerInterceptor允许运行目标方法之前进行一些拦截工作,或者目标方法运行之后进行一些其他处理,类似于javaWeb的Filte。

(1)实现HandlerInterceptor 接口
public class MyFirstInterceptor implements HandlerInterceptor {
   /**
    * 目标方法运行之前运行,返回true则放行执行目标方法,返回false则后面的都会被拦截
    */
   @Override
   public boolean preHandle(HttpServletRequest request,
         HttpServletResponse response, Object handler) throws Exception {
      System.out.println("MyFirstInterceptor...preHandle...");
      return true;
   }
   @Override
   public void postHandle(HttpServletRequest request,
         HttpServletResponse response, Object handler,
         ModelAndView modelAndView) throws Exception {
      System.out.println("MyFirstInterceptor...postHandle...");
   }
   @Override
   public void afterCompletion(HttpServletRequest request,
         HttpServletResponse response, Object handler, Exception ex)
         throws Exception {
      System.out.println("MyFirstInterceptor...afterCompletion");
   }
}
(2)springmvc配置拦截器
<!-- 测试拦截器 -->
<mvc:interceptors>
   <!--配置某个拦截器,默认是拦截所有请求的 -->
   <bean class="com.controller.MyFirstInterceptor"></bean>
   <!-- 配置某个拦截器更详细的信息,只来拦截test01请求 -->
   <mvc:interceptor>
      <mvc:mapping path="/test01"/>
      <bean class="com.controller.MySecondInterceptor"></bean>
   </mvc:interceptor>
</mvc:interceptors>

测试

@Controller
public class InterceptorTestController {
   @RequestMapping("/test01")
   public String test01(){
      System.out.println("test01....");
      //int i =10/0;
      return "success";
   }
}
(3)拦截器运行流程

正常情况下:
拦截器的preHandle------目标方法-----拦截器postHandle-----页面(资源)-------拦截器的afterCompletion

MyFirstInterceptor...preHandle...
test01....
MyFirstInterceptor...postHandle...
success.jsp....
MyFirstInterceptor...afterCompletion

只要preHandle不放行就没有以后的流程,但只要放行了,afterCompletion都会执行。即使目标方法报错了,postHandle不会执行,但afterCompletion会执行。
对于多个拦截器,对于前面已经放行了的拦截器的afterCompletion总会执行

(4)多个拦截器执行顺序:与过滤器相同

先进来的后出去:

MyFirstInterceptor...preHandle...
MySecondInterceptor...preHandle...
test01....
MySecondInterceptor...postHandle...
MyFirstInterceptor...postHandle...
success.jsp....
MySecondInterceptor...afterCompletion...
MyFirstInterceptor...afterCompletion

8.异常处理

SpringMVC默认异常处理器(HandlerExceptionResolver):
ExceptionHandlerExceptionResolver(配置了mvc:annotation-driven)、ResponseStatusExceptionResolver、DefaultHandlerExceptionResolver

1)ExceptionHandlerExceptionResolver

在方法上标注@ExceptionHandler,告诉SpringMVC这个方法专门处理的异常类类型
注意:1.要携带异常信息不能给参数位置写Model,只能写Exception,但是可以将异常信息写在ModelAndView返回
2.一个类中可以有多个方法标注@ExceptionHandler,如果有多个@ExceptionHandler都能处理这个异常,精确异常优先
3.可以写一个类,集中处理所有异常。全局异常处理与本类同时存在,本类优先,即使本类的异常类比全局的大,还是会执行本类而不执行异常类

(1)本类异常方法
//页面上获取异常信息:${exception}
@Controller
public class ExceptionTestController {
@ExceptionHandler(value = { Exception.class })
public String handleException01(Exception exception) {
   System.out.println("本类的:handleException01..." + exception);
   // 视图解析器拼串
   return "error";
}

//页面上获取异常信息:${ex}
@ExceptionHandler(value = { ArithmeticException.class})
public ModelAndView handleException01(Exception exception) {
   System.out.println("本类的:handleException01..." + exception);
   ModelAndView view = new ModelAndView("myerror");
   view.addObject("ex", exception);
   // 视图解析器拼串
   return view;
}
}

(2)异常处理类
/**
* 集中处理所有异常
* 1、集中处理所有异常的类(MyJiZhongException)需要加入到ioc容器中,用@ControllerAdvice标注专门处理异常的类
*/
@ControllerAdvice
public class MyJiZhongException {
   @ExceptionHandler(value={ArithmeticException.class})
   public ModelAndView handleException01(Exception exception){
      System.out.println("全局的:handleException01..."+exception);
      //
      ModelAndView view = new ModelAndView("myerror");
      view.addObject("ex", exception);
      //视图解析器拼串
      return view;
   }
   
}

2)自定义异常:ResponseStatusExceptionResolver—@ResponseStatus

自定义一个异常类,标注@ResponseStatus

@ResponseStatus(reason="用户被拒绝登陆",value=HttpStatus.NOT_ACCEPTABLE)
public class UserNameNotFoundException extends RuntimeException {
   private static final long serialVersionUID = 1L;
}

测试代码:

@RequestMapping("/handle02")
public String handle02(@RequestParam("username") String username) {
   if (!"admin".equals(username)) {
      System.out.println("登陆失败....");
      throw new UserNameNotFoundException();
   }
   System.out.println("登陆成功!。。。");
   return "success";
}

错误页面:
在这里插入图片描述

3)判断是否SpringMVC自带的异常DefaultHandlerExceptionResolver

前面两个异常都没有处理的话,就会到此异常来
Spring自己的异常:如:HttpRequestMethodNotSupportedException–规定必须以post请求方式访问

@RequestMapping(value="/handle03",method=RequestMethod.POST)
public String handle03(){
   return "success";
}

3)SimpleMappingExceptionResolver:通过配置的方式进行异常处理

<context:component-scan base-package="com"></context:component-scan>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
   <property name="prefix" value="/WEB-INF/pages/"></property>
   <property name="suffix" value=".jsp"></property>
</bean>
<mvc:default-servlet-handler/>
<mvc:annotation-driven></mvc:annotation-driven>

<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
   <!-- exceptionMappings:配置哪些异常去哪些页面 -->
   <property name="exceptionMappings">
      <props>
         <!-- key:异常全类名;value:要去的页面视图名 -->
         <prop key="java.lang.NullPointerException">myerror</prop>
      </props>
   </property>
   <!--指定错误信息取出时使用的key  -->
   <!-- 因为在错误页面用的是ex取错误信息,默认是exception,即如果不写这一行代码,则在页面中用${exception}也能取出 -->
   <property name="exceptionAttribute" value="ex"></property> 
</bean>

9.总结

1)SpringMVC运行流程

1、所有请求到前端控制器(DispatcherServlet),DispatcherServlet调用doDispatch进行处理
2、根据HandlerMapping中保存的请求映射信息找到,处理当前请求的,处理器执行链(包含拦截器+处理器方法)
3、根据当前处理器找到他的HandlerAdapter(适配器)
4、拦截器的preHandle先执行
5、适配器执行目标方法,并返回ModelAndView
1)、ModelAttribute注解标注的方法提前运行
2)、执行目标方法的时候(确定目标方法用的参数)
1)、有注解
2)、没注解:
1)、 看是否Model、Map以及其他的
2)、如果是自定义类型
1)、从隐含模型中看有没有,如果有就从隐含模型中拿
2)、如果没有,再看是否SessionAttributes标注的属性,如果是从Session中拿,如果拿不到会抛异常
3)、都不是,就利用反射创建对象
6、拦截器的postHandle执行
7、处理结果;(页面渲染流程)
1)、如果有异常使用异常解析器处理异常;处理完后还会返回ModelAndView
2)、调用render进行页面渲染
1)、视图解析器根据视图名得到视图对象
2)、视图对象调用render方法;
3)、执行拦截器的afterCompletion;

2)Spring和SpringMVC整合

SpringMVC的配置文件就来配置和网站转发逻辑以及网站功能有关的(视图解析器,文件上传解析器,支持ajax等等);
Spring的配置文件来配置和业务有关的(事务控制,数据源等等);
整合方法在springMVC配置文件中:可以合并配置文件;此时Spring为父容器,SpringMVC为子容器,父容器不能使用子容器组件,但子容器可以使用父容器组件
SpringMVC和Spring分容器;
Spring.xml管理业务逻辑组件;

<context:component-scan base-package="com">
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    <context:exclude-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
</context:component-scan>

SpringMVC.xml管理控制器组件;

<context:component-scan base-package="com" use-default-filters="false">
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    <context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
</context:component-scan>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章