SpringMVC入门一篇就够了!

SpringMVC

1. Spring简化IOC容器的获取

1.1 原理
  • 在spring学习中,我们要获取IOC容器通常都是new ClasspathXmlApplicationContext(applicationContest.xml)来获取,这种方式的弊端是每次通过IOC获取Bean时都要new一下,applicationContest.xml文件要加载多次。
  • 在web项目中,我们可以利用监听器监听web应用的启动,当web应用启动时,就自动加载Spring配置文件applicationContext.xml,创建容器,并把容器存入到Servlet中的最大作用域servletContext中。然后就可以在任一位置获取servletContext中存储的ioc容器。进而获得Bean。
  • 上述这一过程Spring帮我们简化了,我们直接调用相应API就行。
  • spring 提供了一个监听器ContextLoaderListener,该监听器内部加载配置文件**,创建ioc容器**,并存储到ServletContext域中,并提供一个客户端工具WebApplicationContextUtils供使用者来获取容器
1.2 如何利用spring的监听器来获取IOC
  1. 导入spring-web坐标(pom.xml)
  2. 在web.xml里配置ContextLoaderListener监听器。
  3. 使用WebApplicationContextUtils获取IOC容器。

示例:

  • 导入Spring集成web的坐标
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>5.0.5.RELEASE</version>
</dependency>
  • 在web.xml里配置ContextLoaderListener监听器
 <!-- spring的配置文件 指定为classpath下的applicationContext.xml-->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
  </context-param>

<!--Spring的监听器-->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  • 通过WebApplicationContextUtils工具来获得IOC容器。
ApplicationContext applicationContext =
WebApplicationContextUtils.getWebApplicationContext(servletContext);
Object obj = applicationContext.getBean("id");

2. SpringMVC的概述

  • SpringMVC是目前主流的实现MVC设计模式的企业级开发框架,它是spring框架的一个子模块,无需整合,开发起来更加便捷。
  • MVC设计模式:将应用程序分为Controller,Model,View三层,Controller接收客户端请求,调用Model生成业务数据,传递给View。
  • SpringMVC就是对这套流程的封装,屏蔽了很多底层代码,开放出接口,让开发者可以更加轻松便捷的完成基于MVC模式的web开发。

3. springmvc容器的创建及与spring容器的关系

web.xml(只有关于spring和springmvc的配置,其他的先省略)

  <!-- spring的配置文件 指定为classpath下的applicationContext.xml-->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
  </context-param>
<!--  spring的监听-->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

  <!-- spring mvc核心:分发servlet -->
  <servlet>
    <servlet-name>mvc-dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- spring mvc的配置文件 为classpath下的springMVC.xml-->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springMVC.xml</param-value>
    </init-param>
       <!--表示容器在启动时立即加载servlet -->
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
       <!-- 处理所有的url,即所有的请求都要经过DispatcherServlet-->
    <servlet-name>mvc-dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

  • Tomcat启动时会依次加载web.xml配置中的Listener,Filter和Servlet,当加载到Listener时,会根据ContextLoaderListener监听器来创建spring ioc容器,当加载到Servlet时,会依据上面的已经创建的spring ioc 容器在DispatcherServlet里创建springMVC容器。spring容器是父容器,springMVC是子容器。
  • spring中的bean指的是被实例的,组装的及被spring容器管理的java对象。
  • spring和springMVC都是管理bean对象的容器。springMVC是负责管理controller对象(bean)的容器,Spring是负责管理service和Dao对象的容器。因此我们在springMVC配置文件里配置的扫描路径是controller的路径,而spring的配置文件里配的是service和dao的路径
  • spring容器和springMVC容器是父子容器的关系。spring容器是父容器,springMVC是子容器。在子容器里可以访问父容器里的对象(bean),但在父容器里不可以访问子容器的对象(bean)。通俗的说就是在controller里可以访问service,dao对象,但在service,dao里不能访问controller对象。
  • 注意:在web.xml里也可以不配置ContextLoaderListener,这样的话会将所有的bean全都放在DispatcherServlet创建的springMVC容器里。

spring和springmvc的父子容器的关系1

4. SpringMVC入门

  • 通过一个小案例来快速了解一下:客户端发起请求,服务端接收请求,执行逻辑并进行视图跳转。
4.1 开发步骤
  1. 导入SpringMVC的坐标
  2. 配置SpringMVC核心控制器DispathcerServlet
  3. 创建Controller类和视图页面
  4. 使用注解配置Controller类中业务方法的映射地址
  5. 配置SpringMVC核心文件spring-mvc.xml
  6. 客户端发起请求测试
4.2 代码实现
  1. 在pom.xml里导入Spring和SpringMVC的坐标,导入Servlet和jsp的坐标

     <!--Spring坐标-->
     <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-context</artifactId>
         <version>5.0.5.RELEASE</version>
     </dependency>
     <!--SpringMVC坐标-->
     <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-webmvc</artifactId>
         <version>5.0.5.RELEASE</version>
     </dependency>
    <!--Servlet坐标-->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
        <version>2.5</version>
    </dependency>
    <!--Jsp坐标-->
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.0</version>
    </dependency>
    
  2. 在web.xml里配置SpringMVC的核心控制器

    //通过DispatcherServlet创建springMVC容器,并且加载spring-mvc.xml配置文件
    <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-mvc.xml</param-value>
        </init-param>
    	<load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>   
         <!-- 处理所有的url,即所有的请求都要经过DispatcherServlet-->
        <servlet-name>DispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    
  3. 创建Controller类

    public class QuickController {
    	public String quickMethod(){
    		System.out.println("quickMethod running.....");
    		return "index";
    	}
    }
    

    视图界面(/WEB-INF/jsp/index.jsp)

    <html>
    <body>
        <h2>Hello SpringMVC!</h2>
    </body>
    </html>
    
  4. 使用注解配置Controller类中业务方法的映射地址

    @Controller//将QuickController类对象加入SpringMVC容器中
    @RequestMapping("")//用于建立请求url和处理请求方法之间的对应关系
    public class QuickController {
        @RequestMapping("/quick")
    	public String quickMethod(){
    		System.out.println("quickMethod running.....");
    		return "index";
    	}
    }
    
  5. 配置SpringMVC核心文件spring-mvc.xml

    <!--配置注解扫描 ,让其扫描controller下的注解-->
        <context:component-scan base-package="com.itheima"/>
     <!-- 配置视图解析器,对Controller类中返回的字符串进行解析,在其前面加上/WEB-INF/jsp/,后面加上.jsp -->
     <!--这样才能使DispatchServlet找到index.jsp的具体位置,而不是一个代表文件名字的字符串 -->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
            <property name="prefix" value="/WEB-INF/jsp/" />
            <property name="suffix" value=".jsp" />
        </bean>
    
  6. 客户端发起请求测试

    http://localhost:8080/itheima_springmvc1/quick 
    

​ 页面显示:

4.3 流程分析
  • 当浏览器访问测试地址时,Tomcat会接收客户的请求,然后将请求传给前端控制器(DispatchServlet),前端控制器会根据url的内容找到对应的Controller处理器类,(这里注意url里的quick和requestMapping("/quick")里的quick要对应) 然后执行这个Controller类QuickController,在控制台输出quickMethod running…,然后返回一个字符串(代表要进行页面跳转),这个字符串会被返回到DispatchServlet前端控制器,前端控制器并不知道代表"index"什么意思,会把它交给视图解析器(就是在spring-mvc.xml里配置的),经过视图解析器解析后将结果返回,然后前端控制器再将结果index.jsp页面输出到浏览器。可以看出,这其中最重要的就是前端控制器DispatchServlet
  • 上面是对SpringMVC流程的简单分析,实际上要比这个复杂一点,下面我们会深入分析,同时只要理解了这个流程就行,实际开发中要我们写代码的地方很少,springmvc框架自动帮我们做了,但这个过程要了解!

5. SpringMVC的执行流程及分析

5.1 执行流程图

5.2 SpringMVC的核心组件的说明
  • DispatcherServlet:前端控制器,是整个springmvc流程控制的核心,控制其他的组件的执行,进行统一的调度,降低组件之间的耦合性,相当于总指挥。
  • HandlerMapping:处理器映射器,负责完成客户端请求与controller类的映射,根据请求找到具体的处理器(即controller类),通常通过注解方式查找,例如requestMapping("/xxx")。
  • HandlerExecutionChain:处理器执行链包括两部分内容:Handler和HandlerInterceptor(处理器拦截器,是一个接口,如果要完成一些拦截处理,可以实现该接口,系统有一个默认的,要额外拦截时才实现接口)。
  • HandlerAdaptor:处理器适配器,Handler要执行业务操作之前,需要进行一系列的操作,包括表单数据的验证,数据类型的转换,将表单数据封装到javaBean等,这些操作都是由HandlerAdaptor来完成,开发者只需要将注意力集中到业务逻辑的处理上,DispatcherServlet通过HandlerAdaptor执行不同的Handler。
  • Handler:处理器,也就是controller类,是我们开发中要具体编写的业务控制器,由DispatcherServlet把用户请求转发给Handler。Handler对具体的用户请求进行处理。处理完后返回ModelAndView
  • ViewResolver:视图解析器,负责将传进的ModelAndView解析,生成View视图,ViewResolver首先根据逻辑视图解析成物理视图名,即具体的页面地址,再生成View视图对象,将其返回给前端控制器。
  • 视图View:它的作用是渲染数据模型,将模型里的数据按照某种MIME类型呈现给客户端,常见的MIME类型有tetx/html,image/jpeg等。

MIME 类型:https://www.w3school.com.cn/media/media_mimeref.asp

5.3 执行流程文字描述
  1. 用户发送请求到前端控制器DispatcherServlet
  2. 前端控制器根据请求调用HandlerMapping处理器映射器
  3. 处理器映射器找具体的处理器Handler 生成处理器对象及处理器拦截器,然后将它们作为一个处理器执行链返回前端控制器。
  4. 前端控制器根据处理器执行链中的处理器 首先访问HandlerAdapter处理器适配器,
  5. HandlerAdapter处理器适配器经过适配调用具体的处理器(controller)
  6. controller执行完逻辑代码返回ModelAndView。
  7. HandlerAdapter处理器适配器将ModelAndView传给前端控制器。
  8. 前端控制器将ModelAndView传给ViewReslover视图解析器进行解析
  9. ViewReslover视图解析器解析后返回具体 的View给前端控制器
  10. 前端控制器根据返回的view进行渲染视图(将模型数据填充到视图中)。
  11. 前端控制器将结果返回给客户端浏览器。

6. SpringMVC的相关注解

@RequestMapping
  • SpringMVC通过@RequestMapping注解将url请求与业务方法controller进行映射,在Handler的类定义处以及方法处都可以添加@RequestMapping注解,在类定义处添加,相当于客户端多了一层访问路径。
  • 相关参数:
    1. value:指定url请求的实际地址,是@RequestMapping的默认值。例如@RequestMapping("/index")等价于@@RequestMapping(value="/index"),因此value可以省略。
    2. method:指定请求的method类型,GET,POST,PUT,DELETE等。例如@RequestMapping(value="/index",method=ReauestMethod.GET)表示这个注解修饰的方法只能接收get请求的url。浏览器请求大多都是get,表单请求是post,其他请求可以通过postman来实现演示。
    3. params:指定请求中必须包含某些参数,否则无法调用方法。@RequestMapping(value="/index",method=ReauestMethod.GET,params={"name","id=10"})表示请求中必须包含name和id两个参数,同时id的值必须是10
@Controller
  • @Controller在类定义处添加,表示将该类交给springMVC容器来管理(需要在springmvc.xml配置文件里开启自动扫描controller包),同时使其成为一个控制器,可以接收客户端请求。
@RestController
  • @RestController注解是@Controller和@ResponseBody的合集,表明这是个控制器Bean,并且将返回值直接返回到http响应体中。即作用也是@Controller和@ResponseBody两个注解 的合集。
  • @RestController返回的对象数据直接以json或xml形式写入http响应体中。
@RequestParam
  • 当请求参数的名称与Controller方法的参数名称不一致,即无法自动进行映射时,需要使用这个注解来完成手动映射。

请求参数为name,接收的形参为username,这时需要这个注解来进行映射。这个工作是由HandlerAdapter处理器映射器来完成的。

public void save16(@RequestParam(value="name") String username)

此外还有一些参数可供选择:

@RequestParam(value="name",required=false,defaultValue="hello")

value:与请求参数的名称对应,将请求参数的值赋给它所修饰的形参

required:默认不写时为true,表示请求中的请求参数不能为空。可以设置为false,表示请求参数为空。

defaultValue:当required为false时,即请求参数为空时,可以给请求参数一个默认值hello。

@Autowired*
@PathVariable

url:http://localhost:8080/quick17/zhangsan

@RequestMapping("/quick17/{name}")
@ResponseBody
 public void save17(@PathVariable(value="name") String username) throws IOException {
        System.out.println(username);
 }
  • @PathVariable注解主要是用来完成@RequestMapping("/quick17/{name}")中的占位符{name}与形参username的映射。在restful风格的url中常用。
  • 注意:@RequestMapping("/quick17/{name}")中的name要和@PathVariable(value=“name”)中的name匹配。后面的username形式可以变
  • 参考10.10 获得Restful风格的参数
@PathParam*
@ResponseBody
  • 这个注解用在Controller类下的方法上,表示告知SpringMVC框架这个方法不进行视图跳转,直接进行数据响应。ResponseBody即响应体,方法返回的字符串不是进行页面跳转而是直接在http响应体中返回。
@RequestBody
  • 作用在形参列表上,用于将前台发送来的固定格式的数据(json或者xml)封装为对应的javaBean对象(封装时用到的一个对象是SpringMVC默认配置的HttpMessageConverter进行解析,然后封装在形参)
  • @RequestBody注解常用来处理content-type为application/json类型(即content-type不是默认的application/x-www-form-urlcoded编码的内容,比如说:application/json或者是application/xml等)。同时使用@RequestBody时请求方式要选择post方式,get方式返回为null
@RequestHeader
  • 可以获取http请求消息的请求头信息。

  • @RequestHeader注解的属性如下:

    value:请求头的名称

    required:是否必须携带此请求头

  • 详见10.12 获取请求头中的消息。

@CookieValue
  • 获取请求头中Cookie 的值

  • @CookieValue注解的属性如下:

    value:指定cookie的名称

    required:是否必须携带此cookie

  • 详见10.12 获取请求头中的消息。

@PostMapping
  • @PostMapping("users") 等价于@RequestMapping(value="/users",method=RequestMethod.POST)
@GetMapping
  • @GetMapping("users") 等价于@RequestMapping(value="/users",method=RequestMethod.GET)

7. spring-mvc.xml配置文件的分析

7.1注解扫描
<context:component-scan base-package=“com.itheima.controller"/>

表示将controller包下面的所有标记@Controller的类装载进springMVC容器。

7.2 视图解析器
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/views/"></property>
    <property name="suffix" value=".jsp"></property>
</bean>

表示将handler返回的ModelAndView进行解析。例如返回字符串"index"后,会在前边加上/WEB-INF/views/ 后面加上.jsp。

7.3 注解驱动
<mvc:annotation-driven/>

在springmvc.xml里配置后,会自动加载RequestMappingHandlerMapping(处理器映射器)和RequestMappingHandlerAdapter(处理器适配器)。但不配置也会加载默认的处理器映射器和处理器适配器。

还有一个作用是配置后底层就会集成jackson进行对象或集合的json格式字符串的转换。

7.4 开通静态资源访问
<!--开通静态资源的访问-->
<mvc:default-servlet-handler />

这个标签的意思是当请求的url经过前端控制器来到HandlerMapping后,如果找不到Controller对应的RequestMapping,就交由默认的Tomcat的servelt来处理寻找默认资源。

7.5 文件上传
<!--配置文件上传解析器-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        //设置编码和上传文件的大小
        <property name="defaultEncoding" value="UTF-8"/>
        <property name="maxUploadSize" value="500000"/>  
    </bean>

详见11.2 单文件上传

8. web.xml配置文件的分析(补充)

8.1 中文过滤器
<!--中文过滤器-->
  <filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>utf-8</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
8.2 spring的配置文件和监听
<!-- spring的配置文件 指定为classpath下的applicationContext.xml-->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
  </context-param>
<!--  spring的监听-->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  • 主要的作用是通过spring配置的监听器自动加载配置文件,帮我们创建一个IOC容器。
  • 详见1.2 如何利用spring的监听器来获取IOC

8.3 SpringMVC的配置文件
<!-- spring mvc核心:分发servlet -->
<servlet>
    <servlet-name>mvc-dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <!-- spring mvc的配置文件 为classpath下的springMVC.xml-->
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:springMVC.xml</param-value>
    </init-param>
    <!--表示容器在启动时立即加载servlet -->
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <!-- 处理所有的url,即所有的请求都要经过DispatcherServlet-->
    <servlet-name>mvc-dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
  • 主要作用是在根据spring-mvc.xml配置文件在DispatcherServlet里创建一个springMVC容器,然后完成初始化。
  • 可以在springmvc.xml里开通静态资源的访问,这样在访问静态资源时,前端控制器会放行相应的url。

9.springMVC的数据响应

9.1 springMVC返回String和ModelAndView的区别
  • 这两种方法来自不同的Spring版本。ModelAndView方法是Spring2.0之前从控制器返回模型和视图的主要方法。现在可以结合Model参数和String返回值,但是旧的方法任然有效。原理上他们是一样的,根据习惯不同,选择哪个都行。
9.2 直接返回字符串String-页面跳转

  • 转发:转发的地址栏路径不变,是一次请求,可以使用request对象共享数据。只能访问当前服务器下的资源。
  • 重定向:地址栏路径发生变化,是两次请求,不能使用request对象来共享数据。可以访问其他服务器的资源。
9.3 通过ModelAndView对象返回-页面跳转

形式一:在Controller中方法返回ModelAndView对象,并设置视图名称:

@RequestMapping("/quick1")
public ModelAndView save1(){
    //Model:模型,作用是封装数据
    //View:视图,作用是展示数据
    //创建一个ModelAndView对象
    ModelAndView modelAndView = new ModelAndView();
    //设置模型数据
    modelAndView.addobject("username","itcast");
    //设置视图名称
    modelAndView.setViewName("success");
    return modelAndView;
}

返回的modelAndView会被视图解析器解析,将setViewName(“success”)中的逻辑视图success转换为物理视图xx/xx/success.jsp,在这个页面可以用EL标签取出模型数据中的值,${“username”}=itcast

补充:EL表达式:

EL表达式的作用是替换和简化jsp页面中java代码的编写。jsp默认支持el表达式,如果要忽略可以在jsp的page指令中<%@ page isELIgnored="false" %>设置为true


形式二:在Controller中方法形参上直接声明ModelAndView,无需在方法中自己创建,在方法中直接使用该对象设置视图,同样可以跳转页面。

@RequestMapping("/qiuck2")
public ModelAndView save2(ModelAndView modelAndView){
    modelAndView.addObject("username","itheima");
    modelAndView.setViewName("success");
    return modelAndView;
}

当SpringMVC调用相应的方法时,会根据方法中对象参数的类型自动创建相应的对象,然后调用方法。这个过程是SpringMVC自动完成的。


形式三:使用Model和字符串结合的方式

@RequestMapping("/qiuck3")
public String save3(Model model){
    model.addAttribute("username","itheima");
    return "success";
}

注意:Model和ModelAndView的区别是 Model只是用来传数据的,ModelAndView可以传数据并进行业务寻址,即返回到指定的静态文件(jsp)。

同样在success.jsp可以通过EL 标签${“username”}来获取值itheima。


形式四:在Controller方法的形参上可以直接使用原生的HttpServletRequest对象,只需声明即可。

@RequestMapping("/qiuck4")
public String save4(HttpServletRequest request){
    request.setAttribute("username","hello");
    return "success";
}

效果同上。

9.4 直接返回字符串-回写数据

形式一:将需要回写的字符串直接返回给客户端浏览器,需要加一个@ResponseBody注解告诉SpringMVC框架,方法返回的字符串不进行页面跳转而是直接在http响应体中返回,即直接显示在浏览器页面上。

@RequestMapping(value="/quick5")
@ResponseBody  //告知SpringMVC框架 不进行视图跳转 直接进行数据响应
public String save5() throws IOException{
    return "hello itheima";
}

会在浏览器中看到hello itheima字符串


形式二:通过SpringMVC框架注入的response对象,使用response.getWriter().print()方法回写数据。

@RequestMapping("/quick6")
pubilc void save6(HttpServletResponse response) throws IOException{
    response.getWriter().print("hello itheima");
}

同样会在浏览器看到hello itheima

9.5 返回对象或集合或json格式字符串-回写数据

json的快速入门:https://mp.weixin.qq.com/s/RAqRKZJqsJ78HRrJg71R1g

形式一:直接回写json格式字符串

@RequestMapping("/quick7")
@ResponseBody
public String save7() throws IOException{
    return "{\"username\":\"zhangsan\",\"age\":18}"
}

形式二:使用json转换工具返回json格式字符串

手动拼接json格式字符串的方式很麻烦,开发中往往要将复杂的java对象转换成json格式的字符串,我们可以使用json转换工具jackson进行转换,通过jackson转换json格式字符串,回写数据。

@RequestMapping("/quick8")
@ResponseBody
public String save8() throws IOEXception{
    User user=new User();
    user.setUsername("lisi");
    user.setAge(30);
    //使用json的转换工具将对象转换成json格式字符串再返回
    ObjectMapper objectMapper=new ObjectMapper();
    String json=objectMapper.writeValueAsString(user);
    return json;
}

注意:使用json转换工具要在pom.xml里导入三个包的坐标:jsonson-core,jackson-databind,jackson-annotations


形式三:通过SpringMVC配置来返回json格式字符串

在SpringMVC.xml里配置

//配置处理器映射器,springmvc内部帮我们实现了处理器映射器,但如果要进行json的转换就要手动重新配置这个处理器映射器
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
    <property name="messageConverters">
        <list>
     <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
        </list>
    </property>
</bean>

java代码:

@RequestMapping("/quick9")
    @ResponseBody
    //期望SpringMVC自动将User转换成json格式的字符串
    public User save10() throws IOException {
        User user = new User();
        user.setUsername("lisi2");
        user.setAge(32);
        return user;
    }

通过在springMVC.xml里配置代码,springmvc会帮助我们自动进行对象转json字符串,无需其他操作。


形式四:通过SpringMVC简化配置来返回json字符串

在springmvc.xml里配置注解驱动

<mvc:annotation-driven/>

java代码

@RequestMapping("/quick10")
    @ResponseBody
    //期望SpringMVC自动将User转换成json格式的字符串
    public User save10() throws IOException {
        User user = new User();
        user.setUsername("lisi2");
        user.setAge(32);
        return user;
    }

形式三中的配置比较繁琐,可以简化为形式四。使用<mvc:annotation-driven/>,底层就会继承jackson进行对象或集合的json格式字符串的转换。


总结

四种形式都是一样的作用,但逐步优化,使用形式四的最便捷,也最常用

10. SpringMVC的请求获取

10.1 请求参数的类型
  • 客户端请求参数的格式是:name=value&name=value……

  • 类型:

    基本数据类型参数

    pojo类型参数

    数组类型参数

    集合类型参数


10.2 获得基本类型的参数
  • Controller中的业务方法的参数名称要和请求参数的name一致,参数值会进行自动映射匹配,并且能够自动做类型转换。
  • 自动做类型转换指的是 从String类型转其他类型。例如String转int

Get请求url : http://localhost:8080/quick10?username=zhangsan&age=12

@RequestMapping("/quick11")
@ResponseBody
public void save11(String username,int age) throws IOException{
    System.out.println(username);
     System.out.println(age);
}

注意:请求url中的类型名字要和方法中的参数名字一致。这样SpringMVC框架就会自动帮我们进行映射,username=zhangsan中的zhangsan就会自动赋给参数username,age=12中的12自动赋值给参数age,并进行类型转换,转换为方法中定义的参数的类型


10.3 获得pojo/javaBean类型的参数
  • Controller中的业务方法的pojo参数的属性名与请求参数的name一致,参数值会自动映射匹配

    pojo类(pojo:简单的Java对象,就是Javabean)

    import lombok.Data;
    
    @Data//生成Getter,Setter,toString
    public class User{
        private String username;
        private int age;
    }
    

    Controller:

    @RequestMapping("/quick12")
    @ResponseBody
    public void save12(User user) throws IOException{
        System.out.println(user);
    }
    

    请求url:http://localhost:8080/quick12?username=zhangsan&age=12

    因为请求参数中的username和age正好和Controller方法参数中的user的属性名相对应,所以会自动进行参数映射,将传入的username和age的值自动赋值给user中的username属性和age属性。这个过程SpringMVC自动完成。


10.4 获得数组类型的参数
  • Controller中的业务方法数组名称与请求参数的name一致,参数值会进行自动映射匹配

请求url:http://localhost:8080/quick13?strs=111&strs=222&strs=333

@RequestMapping("/quick13")
 @ResponseBody
 public void quick13(String[] strs) throws IOException{
    System.out.println(Arrays.toString(strs));
 }

当传入的数组数组名和参数对应时,SpringMVC会自动进行封装,将请求参数封装进数组。然后调用Arrays.tostring()方法输出


10.5 获得集合类型的参数
  • 获得集合类型的参数时要将集合参数包装到一个pojo中才可以。

    先定义一个提交表单form.jsp:

    <from action="${pageContext.request.contextPath}/quick14" method="post">
         <%--表明是第一个User对象的username,age --%>
    	<input type="text" name="userList[0].username"><br/>
        <input type="text" name="userList[0].age"><br/>
        <input type="text" name="userList[1].username"><br/>
        <input type="text" name="userList[1].age"><br/>
        <input type="submit" value="提交"><br/>
    </from>
    

    这里的userList[0].username即表示集合userList中的第一个user对象的username属性。

    定义一个user类,有username,age属性。

    定义一个类VO

    import lombok.Data;
    
    @Data//生成Getter,Setter,toString
    public class VO {
        private List<User> userList;
    }
    

    Controller类

    @RequestMapping("/quick14")
        @ResponseBody
        public void save14(VO vo) throws IOException {
            System.out.println(vo);
        }
    

    请求url:http://localhost:8080/form.jsp

    然后输入表单信息后会跳转到Controller方法,将userList集合数据传给vo,然后输出:

    VO{userList=[User{username=‘zhangsan’,age=18},User{username=‘lisi’,age=20}]}


10.6 获得集合类型的参数2
  • 当使用Ajax提交时,可以指定contentType为json格式,那么在方法参数位置使用@RequestBody可以直接接收集合数据而无需使用pojo进行包装。

定义Ajax代码ajax.jsp

<html>
    <head>
        <script src="${pageContext.request.contextPath}/js/jquery-3.3.1.js"></script>
        <script>
            var userList = new Array();
            userList.push({username:"zhangsan",age:18});
            userList.push({username:"lisi",age:28});

            $.ajax({
                type:"POST",
                url:"${pageContext.request.contextPath}/user/quick15",
                data:JSON.stringify(userList),
                contentType:"application/json;charset=utf-8"
            });

        </script> 
    </head>
    <body></body>
</html>

Controller:

@RequestMapping("/quick15")
    @ResponseBody
    public void save15(@RequestBody List<User> userList) throws IOException {
        System.out.println(userList);
    }

分析:请求Url:http://localhost/ajax.jsp ,这个url会提交三次请求向服务器,第一次访问ajax,jsp,第二次访问/js/jquery-3.3.1.js,第三次访问要跳转的quick15。在访问quick15时,@RequestBody这个注解会将传入的json格式的数据转为javaBean,然后userList获取后输出。

结果:[User{username=‘zhangsan’,age=18}, User{username=‘lisi’,age=28}]

@RequestBody 的作用:作用在形参列表上,用于将前台发送来的固定格式的数据(json或者xml)封装为对应的javaBean对象user,封装时用到的一个对象是SpringMVC默认配置的HttpMessageConverter进行解析,然后封装在形参userList ,然后输出userList即得json转为javaBean的字符串。

@RequestBody注解常用来处理content-type不是默认的application/x-www-form-urlcoded编码的内容,比如说:application/json或者是application/xml等。一般情况下来说常用其来处理application/json类型。同时使用@RequestBody时要选择post方式,get方式返回为null


10.7 上面出现的问题
  • 当我们访问ajax.jsp页面时,通过谷歌抓包工具发现,没有加载到jQuery文件,原因是SpringMVC的前端控制器DispatcherServlet的url-pattern配置的是 / ,因此所有的url请求都会经过前端控制器DispatcherServlet,然后交由HandlerMapping处理,但对jsp页面的访问并不适应于 专门处理用户请求对某个Controller访问的HandlerMapping,因此我们不应该让DispatcherServlet来管理访问静态资源的请求url,要放行静态资源

  • 放行静态资源的方式:在Spring-mvc.xml里配置要放行的静态资源的位置

    <mvc:resource mapping="/js/**" location="/js/"/>
    

    或者使用如下标签:

    <mvc:default-servlet-handler/>
    

    这个标签的意思是当请求的url经过前端控制器来到HandlerMapping后,如果找不到Controller对应的RequestMapping,就交由默认的Tomcat的servelt来处理寻找默认资源。


10.8 配置全局乱码过滤器
  • 当我们进行请求时,如果参数中有中文可能会出现数据乱码。我们可以在web.xml配置文件里设置一个过滤器来进行编码的过滤。
  <!--中文过滤器-->
  <filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>utf-8</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

10.9 获得的参数与形参不匹配

参数绑定注解@RequestParam

  • 之前我们获得基本类型,pojo类型,数组类型的参数时都是要请求参数和Controller参数方法一一对应,然后SpringMVC才能帮我们完成映射。
  • 如果请求的参数名称与Controller的业务方法参数名称不一致时,就要通过@RequestParam注解来进行参数的绑定,完成http请求参数与Controller方法中形参的映射。
<form action="${pageContext.request.contextPath}/quick16" method="post">
    <input type="text" name="name"><br>
    <input type="submit" value="提交"><br>
</form>   
@RequestMapping(value="/quick16")
    @ResponseBody
    public void save16(@RequestParam(value="name") String username) throws IOException {
        System.out.println(username);
    }
  • 上面的代码将请求参数name与Controller方法的形参username进行映射,同时有必要的话会进行数据类型转换。这些工作都是由HandlerAdapter完成的。

  • 此外注解@RequestParam还有一些其他参数可以使用。@RequestParam(value="name",required=false,defaultValue="hello")

    value:与请求参数的名称对应,将请求参数的值赋给它所修饰的形参

​ required:默认不写时为true,表示请求中的请求参数不能为空。可以设置为false,表示请求参数为空。

​ defaultValue:当required为false时,即请求参数为空时,可以给请求参数一个默认值hello。


10.10 获得Restful风格的参数
  • 什么是Restful:Restful是一种软件架构风格,设计风格,而不是标准,只是提供了一组设计原则和约束条件。主要用于客户端和服务器交互的软件,基于这个风格,设计的软件可以更加简洁,更有层次,更易于实现缓存机制等。

  • Restful风格的请求是使用"url+请求方式"表示一次请求目的,http协议里面有四个表示操作方法的动词。

    GET:用于获取资源

    POST:用于新建资源

    PUT:用于更新资源

    DELETE:用于删除资源

    例如:

    /user/1 GET:得到id=1的user

    /user/1 DELETE: 删除 id = 1 的 user

    /user/1 PUT: 更新 id = 1 的 user

    /user POST: 新增 user

  • 传统类型与Restful风格的url:

    传统类型:http://localhost:8080/user?name=zhangsan&id=10

    Restful:http://localhost:8080/user/zhangsan/10

    这里要注意在浏览器中访问url时,大部分操作都是GET类型的。

  • 在SpringMVC中可以使用占位符进行参数绑定/quick17/zhangsan可以写成/quick17/{name},占位符{name}对应的值就是zhangsan, 在业务方法中可以通过使用@PathVariable注解进行占位符的匹配获取

url:http://localhost:8080/quick17/zhangsan

@RequestMapping("/quick17/{name}")
@ResponseBody
 public void save17(@PathVariable(value="name") String username) throws IOException {
        System.out.println(username);
 }
  • 注意这里value="/quick17/{name}中的name和@PathVariable(value=“name”)中的name要匹配,后面的username形式可以变。
  • 通过 @PathVariable 注解完成请求参数与形参的映射。

10.11 自定义数据类型转换器
  • 数据类型转换器是指将客户端http请求中的参数转换为业务方法中定义的形参。SpringMVC默认提供了一些常用的类型转换,例如String转int,String转double,表单数据的封装等。这些是由HandlerAdapter处理器适配器完成的。
  • 如果我们需要的数据类型HandlerAdapter处理器适配器不能转换,这时我们需要自定义一个数据类型装换器,来达到目的。

自定义一个将"2021-4-16"的String类型的数据转换成Date类型的类型转换器。

步骤

  1. 定义转换器类实现Converter接口

  2. 在spring-mvc.xml配置文件中声明转换器,在<annotation-driven>中引用转换器

  3. Controller包中的方法测试

实现

  1. 定义转换器类DateConverter,实现Converter接口
public class DateConverter implements Converter<String,Date>{
   public Date convert(String dateStr){
       //将字符串转换为Date对象
       SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
       Date date=null;
       try{
           date=format.prase(dateStr);
       }catch(ParseException e){
           e.printStackTrace();
       }
       return date;
   }
}
  1. 在Springmvc.xml配置文件里声明转换器,在<annotation-driven>中引用转换器
//声明转换器
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <list>
            <bean class="xxx.xxx.DateConverter"></bean>
            //这里是自定义的转换器类DateConverter所在的全类名
        </list>
    </property>
</bean>

//配置注解驱动,注意要将上面bean里的id加入下面的conversion-service里
<mvc:annotation-driven conversion-service="conversionService">
  1. Controller包中的方法测试
@RequestMapping("/quick18")
@ResponseBody
public void save18(Date date) throws IOException{
    System.out.println(date);
}

请求url:http://localhost:8080/quick18&date=2021-04-16

10.12 获得请求头中的信息
  • 在浏览器地址栏输入url后,浏览器会解析url,首先会根据请求方式的不同(GET,POST等)生成不同的http请求消息。
  • 补充一下HTTP的相关知识
  • GET请求消息格式:

    请求行:GET /url HTTP/1.1

    请求头:客户端浏览器告诉服务器的一些信息。例如日期,客户端支持的语言,数据类型,压缩格式等。格式:请求头名称:请求头值。

    请求空行:告诉服务器请求头已经结束。

  • POST请求消息格式:

    请求行:POST /url HTTP/1.1

    请求头:客户端浏览器告诉服务器的一些信息

    请求空行:告诉服务器请求头已经结束。

    请求体:封装post请求消息的请求参数的。

  • 二者的区别:

    **GET:**请求参数在请求行,在url后。请求的url长度有限制,且不太安全。

    **POST:**请求参数在请求体中,请求的url长度没有限制,相对安全。

  • 使用@RequestHeader可以获得请求头信息,相当于web阶段学习的request.getHeader(name)。
 @RequestMapping("/quick19")
    @ResponseBody
    public void save19(@RequestHeader(value="User-Agent",required=false) String user_agent) throws IOException{
        System.out.println(user_agent);
    }

@RequestHeader的属性:

value:请求头的名称

required:是否必须携带此请求头


  • 使用@RequestHeader注解也可以获取Cookie的值,但有一个更简便的获取Cookie的注解:@CookieValue
 @RequestMapping("/quick20")
    @ResponseBody
    public void save20(@CookieValue(value = "JSESSIONID") String jsessionId) throws IOException {
        System.out.println(jsessionId);
    }

@CookieValue注解的属性如下:

value:指定cookie的名称

required:是否必须携带此cookie

10.13 获取Servlet的相关api

SpringMVC支持使用原始ServletAPI对象作为控制器方法的参数进行注入,常用的对象如下:

HttpServletRequest

HttpServletResponse

HttpSession

@RequestMapping(value="/quick21")
    @ResponseBody
    public void save21(HttpServletRequest request, HttpServletResponse response, HttpSession session) throws IOException {
        System.out.println(request);
        System.out.println(response);
        System.out.println(session);
    }

11. SpringMVC文件上传

11.1 客户端表单的三要素
  • 表单项:type=“file”
  • 表单的提交方式:post
  • 表单的enctype属性是多部分表单形式:enctype=“multipart/form-data”。如果不设置这个属性,只能将文件名传给服务器。
  • 例如:
<form action="${pageContext.request.contextPath}/user/quick22" method="post" enctype="multipart/form-data">
    名称<input type="text" name="username"><br/>
    文件<input type="file" name="uploadFile"><br/>
    <input type="submit" value="提交">
</form>
  • 注意:文件上传的底层是使用Apache fileupload组件完成上传,SpringMVC对这种方式进行了封装。

  • 当form表单修改为多部分表单时,request.getParameter()将失效

11.2 单文件上传

步骤:

  1. 在pom.xml里导入fileupload和io坐标
  2. 在sping-mvc.xml里配置文件上传解析器
  3. 编写表单
  4. 编写Handler

实现:

  1. pom.xml里导坐标
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.1</version>
</dependency>
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.3</version>
</dependency>
  1. 在sping-mvc.xml里配置文件上传解析器
<!--配置文件上传解析器-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        //设置编码和上传文件的大小
        <property name="defaultEncoding" value="UTF-8"/>
        <property name="maxUploadSize" value="500000"/>  
    </bean>
  1. 编写表单
<form action="${pageContext.request.contextPath}/user/quick22" method="post" enctype="multipart/form-data">
    名称<input type="text" name="username"><br/>
    文件<input type="file" name="uploadFile"><br/>
    <input type="submit" value="提交">
</form>
  1. 编写Handler
@RequestMapping(value="/quick22")
    @ResponseBody
    public void save22(String username, MultipartFile uploadFile) throws IOException {
        System.out.println(username);
        //获得上传文件的名称
        String originalFilename=uploadFile.getOriginalFilename();
        //将上传的文件转存入c盘下的upload,文件名字就是上传的文件名
        uploadFile.transferTo(new File("c:\\upload\\"+originalFilename));
    }
  • 注意:Handler里的 MultipartFile uploadFile参数要和表单里的name属性名字一致
11.3 多文件上传
  • 多文件上传,只需将页面修改为多个文件上传项,将方法参数MultipartFile类型修改为MultipartFile[]即可。

实现:

  1. 在pom.xml里导坐标
  2. 在springmvc.xml里配置文件上传解析器
  3. 编写表单
<form action="${pageContext.request.contextPath}/user/quick23" method="post" enctype="multipart/form-data">
        名称<input type="text" name="username"><br/>
        文件1<input type="file" name="uploadFile"><br/>
        文件2<input type="file" name="uploadFile"><br/>
        <input type="submit" value="提交">
    </form>
  1. 编写Handler
@RequestMapping("/quick23")
    @ResponseBody
    public void save23(String username, MultipartFile[] uploadFile) throws IOException {
        System.out.println(username);
        for (MultipartFile multipartFile : uploadFile) {
            String originalFilename = multipartFile.getOriginalFilename();
            multipartFile.transferTo(new File("C:\\upload\\"+originalFilename));
        }
    }

12. SpringMVC的拦截器

13. SpringMVC异常处理

14 补充

14.1 EL表达式相关知识
  • https://how2j.cn/k/jsp/jsp-el/579.html#nowhere
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值