SpringMVC知识总结

Lou.Chen
大约 28 分钟

一、SpringMVC执行流程

https://www.cnblogs.com/myitnews/p/11565941.htmlopen in new window

  • 第一步:发起请求到前端控制器(DispatcherServlet)
  • 第二步:前端控制器请求HandlerMapping查找 Handler (可以根据xml配置、注解进行查找)
  • 第三步:处理器映射器HandlerMapping向前端控制器返回Handler,HandlerMapping会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象,多个HandlerInterceptor拦截器对象),通过这种策略模式,很容易添加新的映射策略
  • 第四步:前端控制器调用处理器适配器去执行Handler
  • 第五步:处理器适配器HandlerAdapter将会根据适配的结果去执行Handler
    • 第六步:Handler执行完成给适配器返回ModelAndView
  • 第七步:处理器适配器向前端控制器返回ModelAndView (ModelAndView是springmvc框架的一个底层对象,包括 Model和view)
  • 第八步:前端控制器请求视图解析器去进行视图解析 (根据逻辑视图名解析成真正的视图(jsp)),通过这种策略很容易更换其他视图技术,只需要更改视图解析器即可
  • 第九步:视图解析器向前端控制器返回View
  • 第十步:前端控制器进行视图渲染 (视图渲染将模型数据(在ModelAndView对象中)填充到request域)
  • 第十一步:前端控制器向用户响应结果

下面我们对出现的一些组件进行详细的介绍:

(1) 前端控制器DispatcherServlet(不需要程序员开发)。   作用:接收请求,响应结果,相当于转发器,中央处理器。有了DispatcherServlet减少了其它组件之间的耦合度。 (2) 处理器映射器HandlerMapping(不需要程序员开发)。   作用:根据请求的url查找Handler。 (3) 处理器适配器HandlerAdapter(不需要程序员开发)。   作用:按照特定规则(HandlerAdapter要求的规则)去执行Handler。 (4) 处理器Handler(需要程序员开发)。   注意:编写Handler时按照HandlerAdapter的要求去做,这样适配器才可以去正确执行Handler (5) 视图解析器ViewResolver(不需要程序员开发)。   作用:进行视图解析,根据逻辑视图名解析成真正的视图(view) (6) 视图View(需要程序员开发jsp)。   注意:View是一个接口,实现类支持不同的View类型(jsp、freemarker、pdf…)

二、前端控制器中的 / 和 /*的区别

tomcat配置的web.xml

DefaultServlet默认的对所有的请求的拦截,有JspServlet对*.jsp结尾的请求的拦截。

先走JspServlet路径下的映射规则,最后匹配DefaultServlet下的默认映射规则,匹配找不到资源则404

  • DefaultServlet是tomcat中处理静态资源的
    • 当请求index.html时,属于静态资源,tomcat就会在服务器下找到这个请求资源并返回
    <servlet>
        <servlet-name>default</servlet-name>
        <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
        <init-param>
            <param-name>debug</param-name>
            <param-value>0</param-value>
        </init-param>
        <init-param>
            <param-name>listings</param-name>
            <param-value>false</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet>
        <servlet-name>jsp</servlet-name>
        <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
        <init-param>
            <param-name>fork</param-name>
            <param-value>false</param-value>
        </init-param>
        <init-param>
            <param-name>xpoweredBy</param-name>
            <param-value>false</param-value>
        </init-param>
        <load-on-startup>3</load-on-startup>
    </servlet>

<!-- The mapping for the default servlet -->
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!-- 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>

项目中编写的web.xml相当于继承了tomcat中的web.xml

  <!--配置前端控制器 拦截的路径-->
  <servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <!--拦截所有-->
    <!-- /和/* 都是拦截所有请求;
     /会拦截所有请求,但是不会拦截 *.jsp 。保证jsp的访问正常
     /*  拦截的范围更大,会拦截到 *.jsp的请求,导致jsp页面不能正常显示
     -->
    <url-pattern>/</url-pattern>
  </servlet-mapping>

<url-pattern>/</url-pattern> 拦截所有请求 不会拦截到 *.jsp的地址(tomcat默认帮我们配置),但会拦截包括不在web.xml配置的其他请求,诸如:index.html,index.js 等静态资源

<url-pattern>/*</url-pattern> 拦截任意路径资源

当我们在项目中的web.xml映射路径为 <url-pattern>/</url-pattern>时,会覆盖tomcat中默认的拦截规则

  • 当请求index.html页面时,当做静态资源处理,请求来到tomcat时,前端控制器会从@RequestMapping下找到指定为这个请求的路径。找到则返回,找不到则404

而我们没有覆盖*.jsp的配置,在tomcat中的web.xml已经帮我们 *.jsp请求的拦截与处理。

若我们在项目中的路径为 <url-pattern>*.jsp</url-pattern>则会覆盖tomcat中的配置,当我们请求index.jsp时,也会通过前端控制器去找@RequestMapping指定请求,找到则返回,找不到则404.

三、常用注解和其他配置详解

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
    String name() default "";
    //请求地址
    @AliasFor("path")
    String[] value() default {};
	//请求地址
    @AliasFor("value")
    String[] path() default {};
	//请求方式:【GET】【DELTE】【PUT】【POST】
    RequestMethod[] method() default {};

    String[] params() default {};

    String[] headers() default {};

    String[] consumes() default {};

    String[] produces() default {};
}

1、@RequestMapping属性详解

  • path/value

    • 代表请求路径
    • @RequestMapping(value = "/hello" )@RequestMapping(path= "/hello" )
  • method

    • 请求类型【GET】【DELTE】【PUT】【POST】
    • @RequestMapping(value = "/hello",method = RequestMethod.POST)
  • params

    • 规定请求参数
    • @RequestMapping(value = "/hello",params = {"username"}) 请求必须携带username参数,没带404
    • @RequestMapping(value = "/hello",params = {"!username"} 请求不能携带username参数,带了404
    • @RequestMapping(value = "/hello",params = {"username=123"} 请求携带的参数的uername值必须为123,否则404
    • @RequestMapping(value = "/hello",params = {"username!=123"} 请求携带的参数username(传空值或者不传该参数都可以)值不是123,否则404
    • ``@RequestMapping(value = "/hello",params = {"username!=123","pwd","!age"} 请求携带的参数username不能为123,必须携带pwd参数,不能携带age`参数。必须同时满足请求才能成功。
  • headers

    • 规定请求头

    • //指定该请求只能是 Chrom刘浏览器才能访问
      @RequestMapping(value = "/hello",headers = {"User-Agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"})
      
  • consumes

    • 只接受内容类型是那种的请求,规定请求头中的Content-Type
  • produces

    • 告诉浏览器返回的内容类型是什么,给响应头中加上Content-Type:text/html;charset=utf-8

2、ant风格的url

若配匹配到多个地址,则优先选择精确的匹配路径 精确>?>*>**

  • ? : 只能代替一个字符
    • @RequestMapping(value = "/hello?") 合法的请求 /hello1 /hello2 /hello3
  • * : 能够代表任意多个字符,和一层路径
    • @RequestMapping(value = "/hello*") 合法请求 /hello123 /hello1
    • @RequestMapping(value = "/hello/*/aa") 合法请求 /hello/bb/aa
  • **: 代表任意多层路径
    • ``@RequestMapping(value = "/hello/**/cc") ` 合法请求 /hello/aa/bb/cc

3、@PathVariable

RESTful风格的路径

@PathVariable("路径上的变量名")

    @RequestMapping(value = "/hello/{id}")
    public String hello(@PathVariable("id")String i) {
        System.out.println(i);
        return "success";
    }

请求路径 /hello/11, 则我们通过url地址的id变量赋值到被@PathVariable注解的参数上。

我们的RESTful的一般设计规则

  • /book/1 【GET】请求, 查询图书

  • /book/1 【POST】请求 ,添加图书

  • /book/1 【PUT】请求,修改图书

  • /book/1 【DELETE】请求,删除图书

4、@GetMapping/@PostMapping

  • @DeleteMapping("/hello")
    • 等价于@RequestMapping(value = "/hello",method = RequestMethod.DELETE)
  • @PostMapping("/hello")
    • 等价于@RequestMapping(value = "/hello",method = RequestMethod.POST)
  • @GetMapping("/hello")
    • 等价于@RequestMapping(value = "/hello",method = RequestMethod.GET)
  • @PutMapping("/hello")
    • 等价于@RequestMapping(value = "/hello",method = RequestMethod.PUT)

5、使form表单能够发送DELET和PUT请求

Spring为我们提供一个过滤器,将请求中的方法类型转换我们给定的请求类型。

web.xml

  <!--使form表单支持 delet和put请求-->
  <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>

1)设置form表单的请求类型为POST

2)表单项携带一个 name为_method的参数,value为请求类型

<!--发送delete请求-->
<form action="/hello/1" method="post">
    <input name="_method" value="delete">
    <input type="submit" value="提交">
</form>
    @DeleteMapping(value = "/hello/{id}")
    public String hello(@PathVariable("id")String i) {
        //1
        System.out.println(i);
        System.out.println("收到请求");
        return "success";
    }

6、@RequestParam

获取请求的参数,默认的参数为必须,若没传入指定的参数则报错

@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {
    @AliasFor("name")
    String value() default "";
    @AliasFor("value")
    String name() default "";
    boolean required() default true;
    String defaultValue() default "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n";
}
  • value/name
    • 指定参数的名称
  • required
    • 指定参数是否必须
  • defaultValue
    • 若参数为空或者没传入该参数,指定的默认值
    @GetMapping("/hello")
    public String hello1(@RequestParam(required = false,defaultValue = "123") String username) {
        System.out.println("username:"+username);
        System.out.println("收到请求");
        return "success";
    }

7、@ReuestHeader

获取请求头的内容,默认的请求头内容为必须

@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestHeader {
    @AliasFor("name")
    String value() default "";

    @AliasFor("value")
    String name() default "";

    boolean required() default true;

    String defaultValue() default "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n";
}
    @GetMapping("/hello")
    public String hello1(@RequestHeader(value = "User-Agent",required = false) String userAgent) {
        //以谷歌为例
        //userAgent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36
收到请求
        System.out.println("userAgent:"+userAgent);
        System.out.println("收到请求");
        return "success";
    }

8、@CookieValue

获取cookie的sessionid,默认为必须

    @GetMapping("/hello")
    public String hello1(@CookieValue(value = "JSESSIONID",required = false) String sessionid) {
        //sessionid:3910DFECD2374747C93B3F42B73495A5
        System.out.println("sessionid:"+sessionid);
        System.out.println("收到请求");
        return "success";
    }

9、POJO属性自动封装

@Getter
@Setter
@ToString
public class User {
    private String id;
    private String username;
    private int age;
}
    @PostMapping("/user")
    public String user(User user) {
        //user:User(id=1001, username=zs, age=20)
        System.out.println("user:"+user);
        System.out.println("收到请求");
        return "success";
    }
<form action="user" method="post">
    <input type="text" name="id"/>
    <input type="text" name="username"/>
    <input type="text" name="age"/>
    <input type="submit" value="提交">
</form>

10、传入原生API(request,response,session)

    @PostMapping("/handler")
    public String user1(HttpSession httpSession, HttpServletRequest request, HttpServletResponse response) throws IOException {
        ServletInputStream inputStream = request.getInputStream();
        ServletOutputStream outputStream = response.getOutputStream();
        //得到字符流
        BufferedReader reader = request.getReader();
        //打印流
        PrintWriter writer = response.getWriter();
        System.out.println("收到请求");
        return "success";
    }

11、处理请求乱码

  <!--解决中文乱码 注意过滤器的配置顺序 我们解决中文乱码的过滤器一般放在所有过滤器之前-->
  <filter>
    <filter-name>characterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
      <!--设置编码为utf-8-->
      <param-name>encoding</param-name>
      <param-value>utf-8</param-value>
    </init-param>
    <init-param>
      <!--设置强制解决请求乱码-->
      <param-name>forceRequestEncoding</param-name>
      <param-value>true</param-value>
    </init-param>
    <init-param>
      <!--设置强制解决响应乱码-->
      <param-name>forceResponseEncoding</param-name>
      <param-value>true</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>characterEncodingFilter</filter-name>
    <!--指定所有请求-->
    <url-pattern>/*</url-pattern>
  </filter-mapping>

12、设置响应参数和返回指定视图

1)通过Map,Model,ModelMap设置的参数都被封装到请求对象参数中,前端通过request.getAttribute("")即可获取

  •     @GetMapping("/handler1")
        public String hander1(Map<String,Object> map) throws IOException {
            map.put("username", "张三");
            return "success";
        }
    
  •     @GetMapping("/handler1")
        public String hander1(Model model) throws IOException {
            model.addAttribute("username","李四");
            return "success";
        }
    
  •     @GetMapping("/handler1")
        public String hander1(ModelMap model) throws IOException {
            model.addAttribute("username","李四");
            return "success";
        }
    
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h1>成功!</h1>
request:<%=request.getAttribute("username")%>
</body>
</html>

2)设置返回视图,并指定返回参数

    @GetMapping("/handler1")
    public ModelAndView hander1() throws IOException {
        //设置视图名称
        ModelAndView modelAndView = new ModelAndView("success");
        //设置响应参数
        modelAndView.addObject("username", "王五");
        return modelAndView;
    }
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h1>成功!</h1>
request:<%=request.getAttribute("username")%>
</body>
</html>

13、使用forward转发

    @GetMapping("/hello1")
    public String hello1() {
        System.out.println("收到请求");
        //转发到 根路径下的test.jsp页面
        return "forward:/test.jsp";
    }
  • 通过forward转发的页面不会通过视图解析器进行加前缀和后缀的处理,可以直接写 / 代表项目路径(http://localhost:8080/项目名/)
  • 也可以通过forward转发到指定请求

14、使用redirect重定向

    @GetMapping("/hello1")
    public String hello1() {
        System.out.println("收到请求");
        return "redirect:/test.jsp";
    }
  • 通过redirect转发的页面不会通过视图解析器进行加前缀和后缀的处理,可以直接写 / 代表项目路径(http://localhost:8080/项目名/)
  • 也可以通过redirect重定向到其他请求

15、使用mvc直接通过请求映射一个页面

当我们要访问一个WEB/INF下的页面时,不能在前端直接通过url地址进行访问,需要在控制器进行请求处理转发到WEB/INF下的位置。我们可以使用在xml配置完成WEB/INF下页面的访问

    <!--配置视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--配置视图返回时的前缀-->
        <property name="prefix" value="/WEB-INF/page/"></property>
        <!--视图返回的后缀-->
        <property name="suffix" value=".jsp"></property>
    </bean>

    <!--path: 指定通过前端直接访问的请求-->
    <!--view-name:指定视图名称-->
    <mvc:view-controller path="/toSuccess" view-name="success"></mvc:view-controller>
    <!--若配置上面的mvc视图映射,则其他请求会失效
    需要开启基于注解的驱动-->
    <mvc:annotation-driven/>
  • 通过<mvc:view-controller path="/toSuccess" view-name="success"></mvc:view-controller> 配置的视图映射处理器,中的view-name的视图名称必须是视图解析器后的 前缀地址下的文件 ,即指定的视图名为 /WEB-INF/page/ 下的文件名。视图解析器会自动帮我们加前缀和后缀处理
  • 当我们<mvc:view-controller path="/toSuccess" view-name="forward:/index.jsp"></mvc:view-controller> 进行forwar转发时,转发请求并不会 通过视图解析器进行加前缀和后缀。这里访问的为项目根目录下的 index.jsp文件

16、自定义视图解析器

自定义请求返回的解析类型

@Controller
public class MyViewResovlerController {

    @RequestMapping("/handleplus")
    public String handler(Model model) {
        List<String> image=new ArrayList<>();
        image.add("1.jpg");
        image.add("2.jpg");
        model.addAttribute("images", image);
        return "down:/downloadImg";
    }

}

自定义返回视图的对象

//自定义视图
public class MyView implements View {
    /**
     * 返回的内容类型
     * @return
     */
    @Override
    public String getContentType() {
        return "text/html";
    }

    @Override
    public void render(Map<String, ?> map, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        System.out.println("之前保存的数据:"+map);
        List<String> o = (List<String>)map.get("images");
        httpServletResponse.setContentType("text/html");
        httpServletResponse.getWriter().write("<h1>即将显示精彩内容...</h1>");
        httpServletResponse.getWriter().write("<h2>"+o.toString()+"</h2>");
    }
}

自定义视图解析器

//自定义视图解析器
public class DownloadResolver implements ViewResolver, Ordered {
	
    //通过xml设置视图解析器的优先级
    private int order;

    /**
     * 根据视图名返回视图对象
     * @param s
     * @param locale
     * @return
     * @throws Exception
     */
    @Override
    public View resolveViewName(String s, Locale locale) throws Exception {
        if (s.startsWith("down:")) {
            return new MyView();
        }
        return null;
    }

    @Override
    public int getOrder() {
        return order;
    }

    public void setOrder(int order) {
        this.order=order;
    }

}

将自定义的视图解析器加入到IOC容器

   <!--将自定义的视图解析器放在IOC容器中-->
    <bean  class="org.lc.view.DownloadResolver">
        <!--让我们自定义的视图解析器 优先于spring中默认的视图解析器优先执行-->
        <property name="order" value="1"></property>
    </bean>

17、静态资源文件访问

    <!--告诉springmvc 自己映射的请求自己处理,不能处理的请求交由tomcat处理-->
    <!--需要在后面配置基于注解的驱动 <mvc:annotation-driven/>-->
    <mvc:default-servlet-handler/>
    <mvc:annotation-driven/>

18、HttpEntity<String>

@ResponBody可以获取请求体的所有信息,但是只是获取一个字符串,里面包含所有

@RequestHeader 可以获取请求头中的某一个头信息

HttpEntity<String>可以获取请求头的所有信息**

 @GetMapping("/test")
    @ResponseBody
    public String getAjaxAllUser1(HttpEntity<String> httpEntity)  {
        System.out.println(httpEntity);
        return "success";
    }
<[host:"localhost:8080", connection:"keep-alive", upgrade-insecure-requests:"1", user-agent:"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36", sec-fetch-mode:"navigate", sec-fetch-user:"?1", accept:"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3", sec-fetch-site:"same-origin", referer:"http://localhost:8080/ssm/", accept-encoding:"gzip, deflate, br", accept-language:"zh-CN,zh;q=0.9,en;q=0.8", cookie:"JSESSIONID=50782912CF4054A36CA4AB8553E368E0"]>

19、ResponseEntity<String>

我们可以通过 @ResponseBody 将返回数据放在响应体中

这里我们可以通过ResponseEntity<String>定制响应头,响应体,状态码等信息

    @GetMapping("/test1")
    public ResponseEntity<String> test1( )  {
        //定制响应头
        MultiValueMap<String,String> headers=new HttpHeaders();
        headers.add("Set-Cookie","username=lc");
        //定制响应体
        String body = "<h1>success</h1>";
        ResponseEntity<String> responseEntity = new ResponseEntity<>(body, headers, HttpStatus.OK);
        return responseEntity;
    }

20、@RestController

加在类上 等同于: @Controller+@ResponseBody

该控制器下的所有请求方法 都以响应体的方式输出

三、自定义类型转换器(转换日期)

1、使用只带转换器的ConversionService(格式化的功能会失效)

有时候我们前端过来的日期格式类型的字符串想要自动映射为后端的请求参数,我们需要自定义类型转换器

1)实现一个 Converter<S, T> 接口

2)将我们自定义的类型转换器加入到ConversionServiceFactoryBean中

3)告诉我们的springmvc使用哪个conversion-service

自定义类型转换器

//实现自定义转换器
//将 字符串类型 自动转换为 日期类型
public class DateConverter implements Converter<String, Date> {
    private SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd");
    @Override
    public Date convert(String s) {
        try {
            return simpleDateFormat.parse(s);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return null;
    }
}

注册该转换器 使用ConversionServiceFactoryBean转换器

    <!--告诉springmvc 使用哪个类型转换器ConversionService -->
	<mvc:annotation-driven conversion-service="conversionService"/>

    <!--告诉springmvc别用默认的类型转换器ConversionService
      而用我们自定义的ConversionService,加入我们自定义的Converter转换器
     -->
    <bean class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <!--添加我们自定义的类型转换器-->
                <bean class="org.lc.converter.DateConverter"></bean>
            </set>
        </property>
    </bean>

控制器

    @PostMapping("/user")
    public String user(@RequestParam("birthday") Date birthday) {
        System.out.println("birthday:" + new SimpleDateFormat("yyyy-MM-dd").format(birthday));
        System.out.println("收到请求");
        return "success";
    }

前端

<form action="user" method="post">
   输入日期:<input type="text" name="birthday"/>
    <input type="submit" value="提交">
</form>

输入的日期格式必须为符合自定义转换器中的规则,否则会转换失败 满足 我们转换器中的定义的日期格式:yyyy-MM-dd

2、使用带格式化和转换器的ConversionService(一般我们使用这种)

使用 FormattingConversionServiceFactoryBean 转换器

    <mvc:annotation-driven conversion-service="conversionService"/>

    <!--告诉springmvc别用默认的类型转换器ConversionService
      而用我们自定义的ConversionService,加入我们自定义的Converter转换器
     -->
    <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <!--添加我们自定义的其他类型转换器-->
                <bean class="org.lc.converter.xxxx"></bean>
            </set>
        </property>
    </bean>

使用**@DateTimeFormat**注解 (将请求参数的字符串转换为日期类型)。可以无需上述配置

@Getter
@Setter
@ToString
public class User {
    private String id;
    private String username;
    private int age;
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date birthday;
}
    @PostMapping("/user")
    public String user(User user) {
        System.out.println("user:" + user);
        System.out.println("收到请求");
        return "success";
    }
<form action="user" method="post">
   输入日期:<input type="text" name="birthday"/>
    <input type="submit" value="提交">
</form>

只能输入和注解匹配的日期格式 pattern = "yyyy-MM-dd"

四、 <mvc:default-servlet-handler/>与<mvc:annotation-driven/>

<mvc:default-servlet-handler/>

告诉springmvc如果我们的请求处理静态资源,则交由tomcat处理。但是只配置 <mvc:default-servlet-handler/>注解,但是会把所以的请求交由tomcat处理,需要配置<mvc:annotation-driven/>,让@RequestMapping动态请求也能访问

  • 当我们没有加 <mvc:default-servlet-handler/>静态资源处理器的时候,默认只能访问通过@RequestMapping的请求信息和默认在tomcat中的web.xml中默认配置的*.jsp的请求。一切静态资源都不能访问,例如:html,css,js等

这两个配置一般同时加上

五、jackson的应用

1、各种类型转换

    <!--jackson依赖-->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.11.0</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.11.0</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-annotations</artifactId>
      <version>2.11.0</version>
    </dependency>
@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Integer id;
    private String username;
    @JsonIgnore
    private String password;
    private String email;
    private int age;
    @JsonFormat(pattern = "yyyy-MM-dd",timezone = "Asia/Shanghai")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date birthday;
}
①json字符串和对象转换
        User user = new User(1, "张三", "123456", "123@qq.com", 22, new Date());
        ObjectMapper objectMapper=new ObjectMapper();

        //将 对象 转换为json字符串
        String s = objectMapper.writeValueAsString(user);
        //{"id":1,"username":"张三","email":"123@qq.com","age":22,"birthday":"2020-07-16"}
        System.out.println(s);

        //将json字符串转换为 对象
        User user1 = objectMapper.readValue(s, User.class);
        //User(id=1, username=张三, password=null, email=123@qq.com, age=22, birthday=Thu Jul 16 00:00:00 GMT+08:00 2020)
        System.out.println(user1);
②list集合和json转换
        List<User> list=new ArrayList<>();
        list.add(new User(1, "张三", "123456", "123@qq.com", 22, new Date()));
        list.add(new User(2, "李四", "12356", "123@qq.com", 16, new Date()));
        ObjectMapper objectMapper=new ObjectMapper();

        //list集合转 json
        String writeValueAsString = objectMapper.writeValueAsString(list);
        //[{"id":1,"username":"张三","email":"123@qq.com","age":22,"birthday":"2020-07-16"},{"id":2,"username":"李四","email":"123@qq.com","age":16,"birthday":"2020-07-16"}]
        System.out.println(writeValueAsString);
        
        //json转 list集合
        JavaType javaType = objectMapper.getTypeFactory().constructParametricType(ArrayList.class, User.class);
        List<User> list1 = objectMapper.readValue(writeValueAsString, javaType);
        //[User(id=1, username=张三, password=null, email=123@qq.com, age=22, birthday=Thu Jul 16 00:00:00 GMT+08:00 2020), User(id=2, username=李四, password=null, email=123@qq.com, age=16, birthday=Thu Jul 16 00:00:00 GMT+08:00 2020)]
        System.out.println(list1);
③map集合和json转换
        HashMap<String,User> map=new HashMap<>();
        map.put("p1",new User(1, "张三", "123456", "123@qq.com", 22, new Date()));
        map.put("p2",new User(2, "李四", "12356", "123@qq.com", 16, new Date()));
        ObjectMapper objectMapper=new ObjectMapper();

        //map集合转json字符串
        String s = objectMapper.writeValueAsString(map);
        //{"p1":{"id":1,"username":"张三","email":"123@qq.com","age":22,"birthday":"2020-07-16"},"p2":{"id":2,"username":"李四","email":"123@qq.com","age":16,"birthday":"2020-07-16"}}
        System.out.println(s);

        //json字符串 转map集合
        JavaType javaType =objectMapper.getTypeFactory().constructParametricType(HashMap.class,String.class,User.class);
        HashMap<String, User> stringUserHashMap = (HashMap<String, User>) objectMapper.readValue(s, javaType);
        //{p1=User(id=1, username=张三, password=null, email=123@qq.com, age=22, birthday=Thu Jul 16 00:00:00 GMT+08:00 2020), p2=User(id=2, username=李四, password=null, email=123@qq.com, age=16, birthday=Thu Jul 16 00:00:00 GMT+08:00 2020)}
        System.out.println(stringUserHashMap);

2、@JsonFormat与@JsonIgnore

使用@ResponseBody时,jackson自动将我们的Map集合,List集合转换为json字符串返回给前端。

@JsonFormat(pattern = "yyyy-MM-dd",timezone = "Asia/Shanghai") 将日期类型格式化后再返回给前端

@JsonIgnore 忽略被该注解标识的字段

@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Integer id;
    private String username;
    @JsonIgnore
    private String password;
    private String email;
    private int age;
    @JsonFormat(pattern = "yyyy-MM-dd",timezone = "Asia/Shanghai")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date birthday;
}

   @GetMapping("/getAllUser")
    @ResponseBody
    public List<User> getAllUser() throws JsonProcessingException {
        List<User> list=new ArrayList<>();
        list.add(new User(1, "张三", "123456", "123@qq.com", 22, new Date()));
        list.add(new User(2, "李四", "12356", "123@qq.com", 16, new Date()));
        list.add(new User(3, "王五", "12456", "123@qq.com", 14, new Date()));
        list.add(new User(4, "赵六", "12356", "123@qq.com", 13, new Date()));
       return list;
    }

3、ajax请求

@GetMapping("/getAjaxUser")
    @ResponseBody
    public List<User> getAjaxAllUser() throws JsonProcessingException {
        List<User> list=new ArrayList<>();
        list.add(new User(1, "张三", "123456", "123@qq.com", 22, new Date()));
        list.add(new User(2, "李四", "12356", "123@qq.com", 16, new Date()));
        list.add(new User(3, "王五", "12456", "123@qq.com", 14, new Date()));
        list.add(new User(4, "赵六", "12356", "123@qq.com", 13, new Date()));
        return list;
    }
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <script type="text/javascript" src="js/jquery/3.4.1/jquery.js"></script>
</head>
<body>
<a href="#">获取所有用户</a>
<script>
    $(function () {
        $("a:first").click(function () {
                $.ajax({
                    url:"getAjaxUser",
                    type:"GET",
                    success:function (data) {
                        console.log(data);
                    }
                })
        })
    })
</script>
</body>
</html>

六、@ResponseBody和@RequestBody

@ResponseBody: 将返回的数据放在响应体中

@RequestBody:前端使用 application/json 类型的json字符串传给后端接收。

    @PostMapping("/putUser")
    @ResponseBody
    public String getAjaxAllUser1(@RequestBody User user)  {
        System.out.println(user);
        return "success";
    }
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<html>
<head>
    <title>Title</title>
    <script src="js/jquery/3.4.1/jquery.js"></script>
</head>
<body>
<a href="handleplus">发送get请求</a>

<form action="user" method="post">
    id: <input type="text" name="id"/><br/>
    用户名: <input type="text" name="username"/><br/>
   生日:<input type="text" name="birthday"/><br/>
   邮箱:<input type="text" name="email"/><br/>
   年龄:<input type="text" name="age"/><br/>
    <input type="button" onclick="sub()" value="提交">
</form>
<script>
    $(function () {

    })
    function sub() {
        //只有get无请求体,请求参数只能在 地址栏中传输
        //其他请求有请求体,可以指定将请求参数以json字符串的形式放在data请求体中
        
        //注意:当后端使用@RequestBody接收application/json的json字符串时,
        //data必须为字符串,且需要指定contentType:"application/json",
        let user='{"username":"张三","age":12}';
        $.ajax({
            url:"putUser",
            type:"POST",
            //默认为 application/x-www-form-urlencoded
            contentType:"application/json",
            data:user,
            success:function (data) {
                alert(data)
            }
        })
    }
</script>
</body>
</html>

七、SpringMVC文件下载

    @GetMapping("/download")
    public ResponseEntity<byte[]> test2(HttpServletRequest request) throws IOException {
        //通过HttpServletRequest获取我们要下载文件的绝对路径
        String realPath = request.getServletContext().getRealPath("/upload/1.jpg");
        FileInputStream fileInputStream = new FileInputStream(realPath);
        //将数组的大小 设置为文件的最大大小
        byte[] bytes = new byte[fileInputStream.available()];
        fileInputStream.read(bytes);
        fileInputStream.close();

        MultiValueMap<String,String> headers=new HttpHeaders();
        headers.add("Content-Disposition","attachment;filename="+"1.jpg");
        ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes, headers, HttpStatus.OK);
        return responseEntity;
    }

八、SpringMVC文件上传

	<!--文件上传-->
    <dependency>
      <groupId>commons-fileupload</groupId>
      <artifactId>commons-fileupload</artifactId>
      <version>1.4</version>
    </dependency>
	<!--注意 必须为commons-io下的包-->
    <dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>2.4</version>
    </dependency>

配置文件上传解析器

   <!--配置文件上传解析器-->
    <!--id必须为multipartResolver-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!--配置默认编码-->
        <property name="defaultEncoding" value="utf-8"></property>
        <!--最大文件的上传大小 单位为KB  这里我们指定20MB-->
        <property name="maxUploadSize" value="#{1024*1024*20}"></property>
    </bean>

1、单文件上传

@RequestMapping("/file")
@Controller
public class FileUploadController {

    @PostMapping("/upload")
    public String upload(Model model, @RequestParam(value = "username",required = false) String username, @RequestParam("headerImg")MultipartFile multipartFile) {
        //获取input标签中的name值
        System.out.println("文件的名称:"+multipartFile.getName());
        //获取文件的名称
        System.out.println("文件的名称:"+multipartFile.getOriginalFilename());

        //文件保存
        try {
            multipartFile.transferTo(new File("F:\\upload\\"+multipartFile.getOriginalFilename()));
            model.addAttribute("msg", "文件上传成功");
        } catch (IOException e) {
            e.printStackTrace();
            model.addAttribute("msg", "文件上传失败");
        }
        return "forward:/fileupload.jsp";
    }
}

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
上传状态:${msg}
<form action="file/upload" method="post" enctype="multipart/form-data">
   用户名: <input type="text" name="username"><br>
    头像:<input type="file" name="headerImg"><br>
    <input type="submit" value="提交">
</form>
</body>
</html>

2、多文件上传

    @PostMapping("/multipleUpload")
    public String upload1(Model model, @RequestParam(value = "username",required = false) String username, @RequestParam("headerImg")MultipartFile[] multipartFile) {
            for (MultipartFile multipartFile1 : multipartFile) {
                //判断是否选择了文件
                if (!multipartFile1.isEmpty()) {
                    //文件保存
                    try {
                        multipartFile1.transferTo(new File("F:\\upload\\"+multipartFile1.getOriginalFilename()));
                        model.addAttribute("msg", "文件上传成功");
                    } catch (IOException e) {
                        e.printStackTrace();
                        model.addAttribute("msg", "文件上传失败");
                    }
                }else{
                    model.addAttribute("msg", "请选择文件!!");
                }
            }
        return "forward:/fileupload.jsp";
    }
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
上传状态:${msg}
<form action="file/multipleUpload" method="post" enctype="multipart/form-data">
   用户名: <input type="text" name="username"><br>
    头像1:<input type="file" name="headerImg"><br>
    头像2:<input type="file" name="headerImg"><br>
    头像3:<input type="file" name="headerImg"><br>
    <input type="submit" value="提交">
</form>
</body>
</html>

九、拦截器

SpringMVC提供拦截器机制,允许允许的目标方法之前进行一些拦截工作,或者目标方法完成之后进行的一些处理

1、基本配置流程

public class MyInterceptor implements HandlerInterceptor {

    /**
     * 目标方法执行之前调用
     * @param request
     * @param response
     * @param handler
     * @return 返回true,放行该方法。返回false,不放行。
     * @throws Exception
     */
    @Override
    public boolean preHandle(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle....");
        return true;
    }

    /**
     * 在目标方法运行之后调用
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle.....");
    }

    /**
     * 在整个请求完成之后,即响应完成之后,调用该方法
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion.....");
    }
}

xml配置

    <!--配置拦截器-->
    <mvc:interceptors>
        <!--拦截所有请求-->
        <!--<bean class="org.lc.interceptor.MyInterceptor"></bean>-->

       <mvc:interceptor>
           <!--拦截所有请求-->
           <mvc:mapping path="/*"/>
           <bean class="org.lc.interceptor.MyInterceptor"></bean>
       </mvc:interceptor>
    </mvc:interceptors>
@Controller
public class InterceptorController {
    @GetMapping("/interceptor")
    public String interceptor() {
        System.out.println("目标方法.....");
        return "success";
    }
}

执行结果

preHandle....
目标方法.....
postHandle.....
来到success页面...
afterCompletion.....

2、单个拦截器执行流程

  • 当preHandle放行 (true):
    • preHandle --> 目标方法 --> postHandle --> 页面响应完成之后 --> afterCompletion
  • 当preHandle不放行 (false):
    • preHandle 执行完成之后,后续流程都不执行
  • 当preHandle放行之后,若执行目标方法出现异常
    • preHandle --> 目标方法异常(来到错误页面) --> afterCompletion
    • 即只要放行了,无论是否异常 afterCompletion 都会执行

3、多拦截器配置

拦截器1

public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle....");
        return true;
    }
    @Override
    public void postHandle(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle.....");
    }
    @Override
    public void afterCompletion(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion.....");
    }
}

拦截器2

public class MyInterceptro2 implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle2...");
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle2....");
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion2....");
    }
}

xml配置

    <!--配置拦截器-->
    <mvc:interceptors>

       <!-- 配置多拦截器 是按照配置的流程来执行-->
       <mvc:interceptor>
           <!--拦截所有请求-->
           <mvc:mapping path="/*"/>
           <bean class="org.lc.interceptor.MyInterceptor"></bean>
       </mvc:interceptor>

        <!--拦截所有请求-->
        <bean class="org.lc.interceptor.MyInterceptro2"></bean>

    </mvc:interceptors>
@Controller
public class InterceptorController {
    @GetMapping("/interceptor")
    public String interceptor() {
        System.out.println("目标方法.....");
        return "success";
    }
}
preHandle....
preHandle2...
目标方法.....
postHandle2....
postHandle.....
来到success页面...
afterCompletion2....
afterCompletion.....

4、多拦截器执行流程

和过滤器一致,先进行的先执行,但是最后出去,后执行的拦截器先出去

  • 拦截器的preHandle:按照顺序执行

  • 拦截器的postHandle:按照逆序执行

  • 拦截器afterCompletion:按照逆序执行

  • 只要拦截器都会执行对应拦截器的afterCompletion方法

  • 只要先进入的拦截器不放行,那么后续的其他拦截器也不会执行

十、拦截器与过滤器的区别

①:拦截器是基于java的反射机制的,而过滤器是基于函数的回调。 ②:拦截器不依赖于servlet容器,而过滤器依赖于servlet容器。 ③:拦截器只对action请求起作用,而过滤器则可以对几乎所有的请求起作用。 ④:拦截器可以访问action上下文、值、栈里面的对象,而过滤器不可以。 ⑤:在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。 ⑥:拦截器可以获取IOC容器中的各个bean,而过滤器不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。

https://blog.csdn.net/weixin_44502804/article/details/93139550open in new window

十一、异常处理

1、只处理本控制器类异常

@Controller
@RequestMapping("/")
public class ExceptionController {

    @ResponseBody
    @GetMapping("/handler1")
    public String handler1(@RequestParam("i") Integer i) {
        System.out.println("handler...");
        int a = 10 / i;
        return "success";
    }

    @GetMapping("/handler2")
    public String handler2(@RequestParam(value = "i",required = false) Integer i) {
        System.out.println("handler...");
        int a = 10 / i;
        return "success";
    }

    //放在该请求控制器下的只能处理 前控制器下的异常
    //以响应体的方式输出
    @ResponseBody
    //指定处理错误的类型
    @ExceptionHandler(ArithmeticException.class)
    public String hander1Exception(Exception e) {
        //获取错误消息
        String message = e.getMessage();
        return "error:"+ message;
    }


    //返回到指定页面
    //指定处理错误的类型
    // @ExceptionHandler(Exception.class)
    // public String hander2Exception(Exception e) {
    //     //由视图解析器拼串处理
    //     return "error";
    // }

    //返回指定视图并携带参数
    // 指定处理错误的类型
    @ExceptionHandler(Exception.class)
    public ModelAndView hander2Exception(Exception e) {
        ModelAndView modelAndView = new ModelAndView("error");
        modelAndView.addObject("msg",e);
        return modelAndView;
    }
    
}

2、处理全局异常

  • @RestControllerAdvice == @ControllerAdviced + @ResponseBody
    • 若响应的信息需要直接输为到响应体,则直接在全局异常控制器上加该注解,里面所有的 异常处理方法的返回值都以 响应体的形式输出

@ControllerAdviced 的使用

  • 若全局异常处理和本控制器异常都存在,则优先使用 本控制器的异常处理机制
  • 若多个异常处理器同时捕获到该异常,则优先使用精确的异常处理器

若@ResponseBody返回中文乱码:

   <mvc:annotation-driven>
        <!-- 消息转换器 -->
        <mvc:message-converters register-defaults="true">
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <property name="defaultCharset" value="UTF-8"/>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

全局异常处理:

@ControllerAdvice
public class GlobalExceptionController {

    //1、返回到指定视图
    @ExceptionHandler(Exception.class)
    public String handler1(Exception e) {
        return "error";
    }
    //2、返回到指定视图并携带异常参数
    // @ExceptionHandler(Exception.class)
    // public ModelAndView handler2(Exception e) {
    //     ModelAndView modelAndView = new ModelAndView("error");
    //     modelAndView.addObject("msg",e);
    //     return modelAndView;
    // }

    //3、返回为响应体
    @ResponseBody
    @ExceptionHandler(NullPointerException.class)
    public String handler3(Exception e) {
        return "{'error':'异常','statusCode':1010,'msg':"+e+"}";
    }

}

本控制器异常处理

@Controller
@RequestMapping("/")
public class ExceptionController {

    @GetMapping("/handler2")
    public String handler2(@RequestParam(value = "i",required = false) Integer i) {
        System.out.println("handler...");
        int a = 10 / i;
        return "success";
    }

    //放在该请求控制器下的只能处理 前控制器下的异常
    //以响应体的方式输出
    @ResponseBody
    //指定处理错误的类型
    @ExceptionHandler(ArithmeticException.class)
    public String hander1Exception(Exception e) {
        //获取错误消息
        String message = e.getMessage();
        return "error:"+ message;
    }
    
}

十二、SpringMVC+Spring整合

要使我们的Spring和SpringMVC各司其职

Spring管理业务逻辑组件-----IOC容器

SpringMVC管理控制器-----前端控制器

当我们有两个容器同时存在时,spring作为父容器,springmvc作为子容器。

  • 当我们的父容器的service要使用子容器的controller则不行
  • 当我们的子容器controller要使用父容器的service则可以。

web.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>

  <!--配置spring-->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring.xml</param-value>
  </context-param>
  <!--项目一启动就 创建IOC容器-->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

  <!--配置springmvc-->
  <!--配置前端控制器-->
  <servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <!--前端控制器的全路径名-->
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <!--指定springmvc配置文件位置-->
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springmvc.xml</param-value>
    </init-param>
    <!--服务器启动的时候就创建对象-->
    <load-on-startup>1</load-on-startup>
  </servlet>

  <!--配置前端控制器 拦截的路径-->
  <servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <!--拦截所有-->
    <!-- /和/* 都是拦截所有请求;
     /会拦截所有请求,但是不会拦截 *.jsp 。保证jsp的访问正常
     /*  拦截的范围更大,会拦截到 *.jsp的请求,导致jsp页面不能正常显示
     -->
    <url-pattern>/</url-pattern>
  </servlet-mapping>

  <!--解决中文乱码 注意过滤器的配置顺序 我们解决中文乱码的过滤器一般放在所有过滤器之前-->
  <filter>
    <filter-name>characterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
      <!--设置编码为utf-8-->
      <param-name>encoding</param-name>
      <param-value>utf-8</param-value>
    </init-param>
    <init-param>
      <!--设置强制解决请求乱码-->
      <param-name>forceRequestEncoding</param-name>
      <param-value>true</param-value>
    </init-param>
    <init-param>
      <!--设置强制解决响应乱码-->
      <param-name>forceResponseEncoding</param-name>
      <param-value>true</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>characterEncodingFilter</filter-name>
    <!--指定所有请求-->
    <url-pattern>/*</url-pattern>
  </filter-mapping>

  <!--使form表单支持 delet和put请求-->
  <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>
</web-app>

Spring容器

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd        http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd"

>
    <!--让spring只管理我们的业务组件-->
    <context:component-scan base-package="org.lc">
        <!--除了被controller标了注解不扫描外,其余的都扫描-->
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

</beans>

SpringMVC容器

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">

    <!--使springmvc只管理我们的控制器-->
    <!--禁用默认扫所有的规则-->
    <context:component-scan base-package="org.lc" use-default-filters="false">
        <!--只扫描包含controller注解的控制器-->
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
</beans>