SpringMVC 框架基础

1. Spring 集成 Web 环境

弊端: 应用上下文对象是通过 new ClasspathXmlApplicationContext 方式获取的,但是每次从容器中获得 Bean 时都要编写 ,这样的弊端是配置文件加载多次,应用上下文对象创建多次

解决: 在 Web 项目中,可以使用 ServletContextListener 监听 Web 应用的启动,我们可以在 Web 应用启动时,就加载 Spring 的配置文件,创建应用上下文对象 ApplicationContext ,在将其存储到最大的域 ServletContext 域中,这样就可以在任意位置从域中获得应用上下文 ApplicationContext 对象了

故我们需要获取 IOC 容器中的 Bean 时,仅需进行:

  1. 配置 ContextLoaderListener 监听器
  2. 使用 WebApplicationContextUtils 获得应用上下文

2. SpringMVC 组件解析

2.1 执行流程

SpringMVC 框架基础

  1. 用户发送请求至前端控制器 DispatcherServlet
  2. DispatcherServlet 收到请求调用 HandlerMapping 处理器映射器
  3. 处理器映射器找到具体的处理器(可以根据 Xml 配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给 DispatcherServlet
  4. DispatcherServlet 调用 HandlerAdapter 处理器适配器
  5. HandlerAdapter 经过适配调用具体的处理器(后端控制器 Controller)
  6. Controller 执行完成返回 ModelAndView
  7. HandlerAdapterController 执行结果 ModelAndView 返回给 DispatcherServlet
  8. DispatcherServletModelAndView 传给 ViewReslover 视图解析器
  9. ViewReslover 解析后返回具体 View
  10. DispatcherServlet 根据 View 进行渲染视图(即将模型数据填充至视图中),DispatcherServlet 响应用户

2.2 组件解析

  1. 前端控制器:DispatcherServlet
    • 用户请求到达前端控制器,它就相当于 MVC 模式中的 C,DispatcherServlet 是整个流程控制的中心,由它调用其它组件处理用户的请求,DispatcherServlet 的存在降低了组件之间的耦合性
  2. 处理器映射器:HandlerMapping
    • 根据用户请求找到 Handler 即处理器,SpringMVC 提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等
  3. 处理器适配器:HandlerAdapter
    • HandlerAdapter 的作用是根据映射器找到的处理器 Handler 信息,按照特定的规则去执行相关的处理器 Handler
  4. 处理器:Handler
    • 它就是我们开发中要编写的具体业务控制器,由 DispatcherServlet 把用户请求转发到 Handler,由 Handler 对具体的用户请求进行处理
  5. 视图解析器:ViewResolver
    • ViewResolver 将处理结果生成 View 视图,ViewResolver 首先根据逻辑视图名解析成物理视图名,即具体的页面地址,再生成 View 视图对象,最后对 View 进行渲染将处理结果通过页面展示给用户

2.3 注解解析

@RequestMapping

作用: 用于建立请求 URL 和处理请求方法之间的对应关系

位置:

  • 类上,请求 URL 的第一级访问目录。此处不写的话,就相当于应用的根目录
  • 方法上,请求 URL 的第二级访问目录,与类上的使用 @ReqquestMapping 标注的一级目录一起组成访问虚拟路径

参数:

  • value:用于指定请求的 URL
  • method:用于指定请求的方式
  • params:用于指定限制请求参数的条件
    • params={"username"} 表示请求参数必须有 username
    • params={"age!18"} 表示请求参数 age 不能为 18

使用 SpringMVC 注解配置需要引入 mvc 命名空间,同时由于 SpringMVC 基于Spring容器,所以在进行 SpringMVC 操作时,需要将 Controller 存储到 Spring 容器中(Spring 容器与 SpringMVC 容器为父子容器关系,SpringMVC 能访问 Spring 容器内资源,反之不行)

<context:component-scan base-package=“com.example.controller"/>

2.4 XML配置解析

视图解析器:修改前后缀

  • prefix:前缀
  • suffix:后缀
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/views/"/>
    <property name="suffix" value=".jsp"/>
</bean>

3. SpringMVC 数据响应

3.1 数据响应方式

  1. 页面跳转
    • 直接返回字符串
    • 通过 ModelAndView 对象返回
  2. 回写数据
    • 直接返回字符串
    • 返回对象或集合

3.2 页面跳转

直接返回字符串: 此种方式会将返回的字符串与视图解析器的前后缀拼接后跳转

SpringMVC 框架基础

返回 ModelAndView 对象:

@RequestMapping("/test")
public ModelAndView testMethod(){
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.setViewName("redirect:index.jsp");
    return modelAndView;
}

@RequestMapping("/test")
public ModelAndView testMethod(){
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.setViewName("forward:/WEB-INF/views/index.jsp");
    return modelAndView;
}

除了 new ModelAndView 还可以在参数种添加 ModelAndView 让 SpringMVC 自动注入

request 域存储数据:

  1. 通过 SpringMVC 框架注入的 request 对象 setAttribute() 方法设置

    @RequestMapping("/test")
    public String testMethod(HttpServletRequest request){
        request.setAttribute("name","zhangsan");
        return "index";
    }
    
  2. 通过 ModelAndViewaddObject() 方法设置

    @RequestMapping("/test")
    public ModelAndView testMethod(){
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("forward:/WEB-INF/views/index.jsp");
        modelAndView.addObject("name","lisi");
        return modelAndView;
    }
    

3.3 回写数据

使用 Response 对象:

通过 SpringMVC 框架注入的 response 对象,使用 response.getWriter().print(“hello world”) 回写数据,此时不需要视图跳转,业务方法返回值为 void

@RequestMapping("/test")
public void testMethod(HttpServletResponse response) throws IOException {
    response.getWriter().print("Hello World");
}

直接返回字符串:

将需要回写的字符串直接返回,但此时需要通过 @ResponseBody 注解告知 SpringMVC 框架,方法返回的字符串不是跳转是直接在 http 响应体中返回

@RequestMapping("/test")
@ResponseBody
public String testMethod() throws IOException {
    return "Hello SpringMVC";
}

返回对象或集合:

如果我们需要返回一个POJO,我们一般习惯于字符串返回 Json 格式的字符串,此时我们需要使用 jackson 工具进行转换

通过 SpringMVC 帮助我们对对象或集合进行 json字符串的转换并回写,为处理器适配器配置消息转换参数,指定使用 jackson 进行对象或集合的转换,因此需要在 spring-mvc.xml 中进行如下配置:

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

配置之后我们只需要这样就可以返回 json 字符串了:

@RequestMapping("/test")
@ResponseBody public User testMethod() throws IOException {
    User user = new User();
    user.setUsername("思思不羡仙");
    user.setAge(18);
    return user;
}

在方法上添加 @ResponseBody 就可以返回 json 格式的字符串,但是这样配置比较麻烦,配置的代码比较多,因此,我们可以使用 mvc 的注解驱动代替上述配置:

<mvc:annotation-driven/>

4. SpringMVC 获取请求数据

客户端请求参数的格式是:name=value&name=value&name=value

服务器端要获得请求的参数,有时还需要进行数据的封装,SpringMVC 可以接收如下类型的参数:

  • 基本类型参数
  • POJO类型参数
  • 数组类型参数
  • 集合类型参数

4.1 获取基本类型参数

Controller 中的业务方法的参数名称要与请求参数的 name 一致,参数值会自动映射匹配

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

4.2 获取POJO类型参数

Controller 中的业务方法的 POJO 参数的属性名与请求参数的 name 一致,参数值会自动映射匹配

// 请求地址:http://localhost:8080/test?username=apple&age=18
@Data
public class User {
    private String username;
    private int age;
}
@RequestMapping("/test")
@ResponseBody
public void testMethod(User user) throws IOException {
    System.out.println(user);
}

4.3 获取数组类型参数

Controller 中的业务方法数组名称与请求参数的 name 一致,参数值会自动映射匹配

// 请求地址:http://localhost:8080/test?strs=111&strs=222&strs=333
@RequestMapping("/test")
@ResponseBody
public void testMethod(String[] strs) throws IOException {
    System.out.println(Arrays.asList(strs));
}

4.4 获取集合类型参数

获得集合参数时,需要包装到一个 POJO 中才可以,即创建一个 VO 对象,内部有 List<User> userList 成员变量,表单可如下:

<form action="${pageContext.request.contextPath}/test" method="post">
    <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>
</form>

当使用 ajax 提交时,可以指定 contentType 为 json 形式,那么在方法参数位置使用 @RequestBody 可以直接接收集合数据而无需使用 POJO 进行包装

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

释放指定资源:

没有加载到 jquery 文件,原因是 SpringMVC 的前端控制器 DispatcherServleturl-pattern 配置的是 / ,代表对所有的资源都进行过滤操作,我们可以通过以下两种方式指定放行静态资源:

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

<mvc:default-servlet-handler/>

上述第一种方式需要注意:

  • / 不会拦截页面,只会拦截路径
  • /* 是拦截所有的文件夹的页面,不包含子文件夹
  • /** 是拦截所有的文件夹及里面的子文件夹的页面

4.5 请求数据乱码问题

当 post 请求时,数据会出现乱码,我们可以设置一个过滤器来进行编码的过滤

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

4.6 参数绑定注解

当请求的参数名称与 Controller 的业务方法参数名称不一致时,就需要通过 @RequestParam 注解显示的绑定

<form action="${pageContext.request.contextPath}/test" method="post">
    <input type="text" name="name"><br>
    <input type="submit" value="提交"><br>
</form>
@RequestMapping("/test")
@ResponseBody
public void testMethod(@RequestParam("name") String username) throws IOException {
    System.out.println(username);
}

@RequestParam 的参数:

  • value:与请求参数名称
  • required:此在指定的请求参数是否必须包括,默认是true,提交时如果没有此参数则报错
  • defaultValue:当没有指定请求参数时,则使用指定的默认值赋值

4.7 获取 Restful 风格的参数

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

Restful 风格的请求是使用 [url + 请求方式] 表示一次请求目的的,HTTP 协议里面四个表示操作方式的动词如下:

  • GET:用于获取资源
  • POST:用于新建资源
  • PUT:用于更新资源
  • DELETE:用于删除资源

上述 url 地址 /user/hello 中的 hello 就是要获得的请求参数,在 SpringMVC 中可以使用占位符进行参数绑定,地址 /user/hello 可以写成 /user/{id} ,占位符 {id} 对应的就是 hello 的值,在业务方法中我们可以使用 @PathVariable 注解进行占位符的匹配获取工作

// 请求地址:http://localhost:8080/test/zhangsan
@RequestMapping("/test/{name}")
@ResponseBody
public void testMethod(@PathVariable(value = "name",required = true) String name){
    System.out.println(name);
}

4.8 自定义类型转换器

SpringMVC 默认已经提供了一些常用的类型转换器,例如客户端提交的字符串转换成 int 型进行参数设置,但是不是所有的数据类型都提供了转换器,没有提供的就需要自定义转换器,例如:日期类型的数据就需要自定义转换器

自定义类型转换器的开发步骤:

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

    public class DateConverter implements Converter<String,Date>{
        @Override
        public Date convert(String source) {
            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
            try {
                Date date = format.parse(source);
                return date;
            } catch (ParseException e) {
                e.printStackTrace();
            }
            return null;
        }
    }
    
  2. 在配置文件中声明转换器

    <bean id="converterService" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <list>
                <bean class="com.example.converter.DateConverter"/>
           </list>
        </property>
    </bean>
    
  3. <annotation-driven> 中引用转换器

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

4.9 获取Servlet相关API

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

  • HttpServletRequest
  • HttpServletResponse
  • HttpSession

4.10 获取请求头

1. @RequestHeader 注解

相当于 request.getHeader(name) 方法,该注解有以下属性:

  • value:请求头的名称
  • required:是否必须携带此请求头
@RequestMapping("/test")
@ResponseBody
public void testMethod(@RequestHeader(value = "User-Agent",required = false) String headerValue){
    System.out.println(headerValue);
}

2. @CookieValue 注解

使用该注解可以获得指定 Cookie 的值,该注解有以下属性:

  • value:指定 Cookie 的名称
  • required:是否必须携带此 Cookie
@RequestMapping("/test")
@ResponseBody
public void testMethod(@CookieValue(value = "JSESSIONID",required = false) String jsessionid){
    System.out.println(jsessionid);
}

4.11 文件上传

文件上传三要素

  • 表单项 type = “ file ”

  • 表单的提交方式是 post

  • 表单的 enctype 属性是多部分表单形式,及 enctype = “ multipart/form-data ”

文件上传原理

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

  • enctype = “ application/x-www-form-urlencoded ” 时,form表单的正文内容格式是:key=value&key=value&key=value

  • 当 form 表单的 enctype 取值为 mutilpart/form-data 时,请求正文内容就变成多部分形式:
    SpringMVC 框架基础
    单文件上传步骤

  1. 导入 commons-fileuploadcommons-io 坐标

  2. 配置文件上传解析器

    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!--上传文件总大小-->
        <property name="maxUploadSize" value="5242800"/>
        <!--上传单个文件的大小-->
        <property name="maxUploadSizePerFile" value="5242800"/>
        <!--上传文件的编码类型-->
        <property name="defaultEncoding" value="UTF-8"/>
    </bean>
    
  3. 编写文件上传代码

    @RequestMapping("/test")
    @ResponseBody
    public void testMethod(String name,MultipartFile uploadFile) throws IOException {
        //获得文件名称
        String originalFilename = uploadFile.getOriginalFilename();
        //保存文件
        uploadFile.transferTo(new File("C:\\upload\\"+originalFilename));
    }
    

多文件上传步骤

多文件上传,只需要将页面修改为多个文件上传项,将方法参数 MultipartFile 类型修改为 MultipartFile[] 即可

@RequestMapping("/test")
@ResponseBody
public void testMethod(String name,MultipartFile[] uploadFiles) throws IOException {
    for (MultipartFile uploadFile : uploadFiles){
        String originalFilename = uploadFile.getOriginalFilename();
        uploadFile.transferTo(new File("C:\\upload\\"+originalFilename));
    }
}
上一篇:SpringMVC


下一篇:SpringMVC——方法映射