SpringMVC

# 一、WEB阶段

1、BaseServlet

  • 在做什么?
    • 统一接收请求,根据url进行请求分发到具体的Servlet来处理
    • 具体的Servlet返回处理结果到BaseServlet,由BaseServlet统一得出响应结果给浏览器
  1. 客户端请求都通过servlet-mapping中url-pattern来找到对应Servlet来处理具体的请求
  2. 每个servlet在处理请求时,都会默认调用service方法。而我们自己写的Servlet继承了BaseServlet,而BaseServlet继承了HttpServlet,所以BaseServlet的service方法就作为了所有请求的入口方法,根据请求url中的标识参数【method】通过反射调用相应的方法真正的处理请求。【处理业务逻辑】
  3. 返回一个具体的响应结果,把这个结果交给BaseServlet的service方法,通过判断响应结果,得出最终响应的方式

二、SpringMVC概述

1、是Spring Frameworker 的一个子模块

2、是一个非常优秀的MVC框架【功能类似于Servlet】

三、SpringMVC快速入门

1、需求

浏览器发送一个请求,由控制器来接收请求,并调用指定方法来处理请求,最后给出响应结果到客户端

2、实现步骤

  1. 创建一个maven工程,打包方式指定成war包
  2. 导入依赖
  3. 在web.xml中配置一个前端控制器【SpringMVC的心脏】
  4. 编写一个控制器
  5. 编写SpringMVC的配置文件
  6. 启动项目,测试

3、具体实现

3.1 创建一个maven工程,打包方式指定成war包

  • pom.xml
<packaging>war</packaging>
  • 补全目录
    • src/main/webapp/WEB-INF/web.xml

3.2 导入依赖

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.1.6.RELEASE</version>
    </dependency>
</dependencies>

3.3 在web.xml中配置一个前端控制器【SpringMVC的心脏】

<!-- 配置前端控制器 -->
<servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:springmvc.xml</param-value>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <!-- / : 拦截所有资源,除jsp之外 -->
    <url-pattern>/</url-pattern>
</servlet-mapping>

3.4 编写一个控制器【HelloController】

@Controller
public class HelloController {
    
    @RequestMapping("/hello")
    public String helloWorld(){
        System.out.println("HelloController hello() running");
        return "/WEB-INF/success.jsp";
    }
    
}
  • 增加一个jsp页面
    • /WEB-INF/success.jsp

3.5 编写SpringMVC的配置文件

<beans 	xmlns="http://www.springframework.org/schema/beans"
          xmlns:context="http://www.springframework.org/schema/context"
          xmlns:mvc="http://www.springframework.org/schema/mvc"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          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
							http://www.springframework.org/schema/mvc
							http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 组件扫描 -->
    <context:component-scan base-package="com.qf.java2007.controller"></context:component-scan>

</beans>

3.6 启动项目,测试

  • 配置tomcat
  • 部署springmvc应用
  • 浏览器输入访问url,进行测试
测试效果

4、Spring执行流程简化版

Spring执行流程简化版

| image-20210302114355874 |

5、细节问题

<!-- 开启注解驱动 -->
<!-- 正常情况下,这个标签是必配的【直接写上】 
    会帮我们配置处理器映射器和处理器适配器
-->
<mvc:annotation-driven />

四、SpringMVC执行流程【详细版】

SpringMVC执行流程
image-20210302114010546

五、SpringMVC应用

1、Handler是如何接收请求参数

1.1 基本类型

八大基本数据类型及包装类,String

1.1.1 解析

handler中的形参名字 跟 请求参数名一致,就可以直接封装参数

如:请求url:http://localhost:8080/test01?name=jack name:参数名 jack:值

handler xxx(String name)

1.1.2 代码

  • 页面
<a href="test01?name=jack&num=10&money=100.6">get请求传参 test01</a>
  • Handler
/**
 * 接收基本类型的请求参数
 * @param name
 * @param num
 * @param money
 * @return
 */
@RequestMapping("/test01")
public String test01(String name, Integer num, Double money){
    System.out.println("name = " + name);
    System.out.println("num = " + num);
    System.out.println("money = " + money);
    return SUCCESS;
}

1.2 实体类型

1.2.1 解析

handler中的形参是一个实体类型,它的属性名 跟前台传递参数的名称一一对应【调的是setter方法】

1.2.2 代码

  • 实体类【User.java】
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {

    private Integer id;
    private String username;
    private String password;
    //规定createTime属性的格式为 yyyy-MM-dd HH:mm:ss
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    //本质上是通过解析该注解,把string转成date类型,由类型转换器完成的。
    private Date createTime;

}
  • 页面
<form action="test02" method="post">
    <input type="hidden" name="id" value="10"/><br/>
    用户名:<input type="text" name="username" /><br/>
    密码:<input type="text" name="password" /><br/>
    创建时间:<input type="text" name="createTime" /><br/>
    <input type="submit" value="传递参数为实体对象"/>
</form>
  • Handler
@RequestMapping("/test02")
public String test02(User user){
    //springmvc 默认支持的时间格式是 yyyy/MM/dd HH:mm:ss
    //如果需要定制时间格式。 @DateTimeFormat
    System.out.println("user = " + user);
    return SUCCESS;
}

1.2.3 可能出现的错误

  • 400:参数封装错误

1.3 @RequestParam注解

  • 页面
<a href="test03?size=5">test03 首页</a>
<a href="test03?pageNo=2&size=5">test03 分页</a>
  • handler
/**
 * @RequestParam : 代表请求参数
 *   name : 指定请求参数的名称
 *   required : 指定请求参数是否必须
 *   defaultValue : 默认值【如果前台没有传递该参数,则使用defaultValue】
 * @param pageNo
 * @param pageSize
 * @return
 */
@RequestMapping("/test03")
public String test03(Integer pageNo,
                     @RequestParam(name = "size", required = false, defaultValue = "0") int pageSize){
    System.out.println("pageNo = " + pageNo);
    System.out.println("pageSize = " + pageSize);
    return SUCCESS;
}

1.4 请求参数为数组

  • 页面
<form action="test04" method="post">
    ids: <input type="checkbox" name="ids" value="1"/>1<br/>
    <input type="checkbox" name="ids" value="2"/>2<br/>
    <input type="checkbox" name="ids" value="3"/>3<br/>
    <input type="submit" value="传递参数为数组"/>
</form>
  • handler
/**
 * 请求参数为数组
 * @param ids
 * @return
 */
@RequestMapping("/test04")
public String test04(Integer[] ids){
    if(ids != null && ids.length > 0) {
        System.out.println("ids = " + Arrays.asList(ids));
    }
    return SUCCESS;
}

1.5 集合参数【了解】

当前集合类型参数必须封装到一个JavaBean中,作为属性存在

  • 页面
<form action="test05" method="post">
    <input type="hidden" name="id" value="10"/><br/>
    用户名:<input type="text" name="username" /><br/>
    密码:<input type="text" name="password" /><br/>
    创建时间:<input type="text" name="createTime" /><br/>
    List(爱好):<input name="hobbies[0]" value="Java"/>
        <input name="hobbies[1]" value="Game"/><br/>
    maps:<input name="maps['aa']" value="AA"/>
    <input name="maps['bb']" value="BB"/>
    <input type="submit" value="传递参数为集合"/>
</form>
  • handler
/**
 * 请求参数为集合
 *  集合参数必须作为JavaBean的属性存在
 * @param user
 * @return
 */
@RequestMapping("/test05")
public String test05(User user){
    System.out.println("user = " + user);
    return SUCCESS;
}

1.6 post请求解决中文乱码

  • web.xml配置中文乱码过滤器
<!-- 解决post请求乱码 -->
<filter>
    <filter-name>encodingFilter</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>
    <init-param>
        <param-name>forceEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

1.7 @PathVariable【路径传参】

对后期学习SpringMVC的Restful风格有关系

参数在url中

原始传参:http://localhost/test01?name=jack&num=10&money=100.6

路径传参:http://localhost/test01/jack/10/100.6

  • 页面
<a href="test06/jack/10/100.6">路径传参</a>
  • handler
/**
 * url : /test06/jack/10/100.6
 *  如果是小数,数据会丢失小点后后面的
 * @PathVariable 用来绑定URL路径上的参数
 *      name : 指定url路径上{}中名字,以此来获取对应的参数
 *      required : 是否必须,默认值就是true。
 *          如果指定成false,会报 No mapping for GET /test07
 * 后期跟 restful 有点关系
 * @param name
 * @param age
 * @param balance
 * @return
 */
@RequestMapping("/test06/{name}/{age}/{b}")
public String test06(@PathVariable String name,
                     @PathVariable Integer age,
                     @PathVariable(name = "b", required = false) Double balance){
    System.out.println("name = " + name);
    System.out.println("age = " + age);
    System.out.println("balance = " + balance);
    return SUCCESS;
}

2、响应结果类型

2.1 响应结果是字符串

2.1.1 普通字符串

是一个逻辑视图名,经过视图解析器解析成物理视图

2.1.2 带前缀的字符串

  • forward: 转发【不经过视图解析器】
  • redirect: 重定向【不经过视图解析器】
  • handler
@RequestMapping("/test11")
public String test11(Model model){
    System.out.println("ResponseController test11");
    //使用model传递数据,底层介质request
    model.addAttribute("address","杭州");
    model.addAttribute("school","千锋教育");
    //1.可以转发到具体的视图
    return "forward:/WEB-INF/success.jsp";
    //2.可以转发到具体的handler
    //return "forward:/hello";
}

@RequestMapping("/test12")
public String test12(){
    System.out.println("ResponseController test12");
    //1.可以重定向到具体的视图
    //return "redirect:/index.jsp";
    //2.可以重定向到具体的handler
    return "redirect:/hello";
}

2.2 响应结果是ModelAndView

ModelAndView:模型和视图

Model:存放数据的模型

View:响应的视图

  • handler
@RequestMapping("/test13")
public ModelAndView test13(){
    System.out.println("ResponseController test13");
    ModelAndView mav = new ModelAndView();
    //封装参数,底层存储的介质是 request
    mav.addObject("name", "刘胖子");
    mav.addObject("gender", "男");

    //设置视图,走视图解析器
    mav.setViewName("test");  
    return mav;
}

2.3 响应结果被@ResponseBody修饰

@RequestMapping("/test14")
//@ResponseBody   //返回的结果,直接当做响应体给到浏览器
public @ResponseBody String test14(){
    System.out.println("ResponseController test14");
    return "GOOD";
}

后期经过响应一些json串

六、获取原生ServletAPI

1、直接在hander的形参中定义即可

  • handler
/**
 * 获取原生ServletAPI
 * @param request
 * @param response
 * @param session
 * @return
 */
@RequestMapping("/test11")
public String test11(HttpServletRequest request,
                    HttpServletResponse response,
                    HttpSession session){
    System.out.println("request = " + request);
    System.out.println("response = " + response);
    System.out.println("session = " + session);

    String realPath = session.getServletContext().getRealPath("/");
    System.out.println("realPath = " + realPath);

    return "success";
}

/**
 * 如果使用原生的ServletAPI,就相当于操作一个Servlet
 * @param request
 * @param response
 * @param session
 * @throws ServletException
 * @throws IOException
 */
@RequestMapping("/test12")
public void test12(HttpServletRequest request,
                    HttpServletResponse response,
                    HttpSession session) throws ServletException, IOException {
    System.out.println("request = " + request);
    System.out.println("response = " + response);
    System.out.println("session = " + session);

    String realPath = session.getServletContext().getRealPath("/");
    System.out.println("realPath = " + realPath);

    //request.getRequestDispatcher("/WEB-INF/success.jsp").forward(request, response);
    response.sendRedirect(request.getContextPath() +"/redirect.jsp");
}

2、直接从Spring容器中注入即可

  • HttpServletRequest可以使用@Autowired从SpringMVC容器中获取,当做属性注入到Controller中,是一个线程安全的请求对象,功能跟原生Request对象相近。最好仅用于域对象操作
  • 目的就是为了让多个方法同时使用一个Request对象即可。
  • handler
// HttpServletRequest 作为成员变量在controller中存在,是没有线程安全问题的。
// ThreadLocal
@Autowired
HttpServletRequest request;

@RequestMapping("/test13")
public String test13() throws Exception {
    System.out.println("request = " + request.getClass().getName());

    request.setAttribute("username", "李四");

    return "success";
}

七、释放静态资源

1、DispatcherServlet的url-pattern以后缀的方式去拦截资源

<servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <!-- *.do 或者 *.action -->
    <url-pattern>*.do</url-pattern>
</servlet-mapping>

2、让经过DispatcherServlet的资源,如果没有找到对应的Handler,交由tomcat默认的Servlet来处理

  • springmvc配置文件
<!-- 开启tomcat默认servlet的使用 -->
<!-- 方式二: 可以释放静态资源 -->
<mvc:default-servlet-handler/>

3、手动释放

  • springmvc配置文件
<!-- 第三种: 手动释放静态资源 -->
<!--
    mapping : 映射路径
    location : 静态资源存放的目录
-->
<mvc:resources mapping="/css/**" location="/css/"></mvc:resources>
<mvc:resources mapping="/html/**" location="/html/"></mvc:resources>
<mvc:resources mapping="/js/**" location="/js/"></mvc:resources>

八、处理json

1、@ResponseBody

直接把结果响应给页面

  • 如果字符串,就原封不动响应
  • 如果对象或者集合,转成json给出响应【不要忘了加jackson依赖】
@RequestMapping("/test17")
@ResponseBody
public User test17() {
    User user = new User();
    user.setUname("李四");
    user.setPassword("123456");
    user.setBirthday(new Date());
    user.setGender(1);
    return user;
}

@RequestMapping("/test18")
@ResponseBody
public List<User> test18() {
    User user1 = new User("刘备", "111", new Date(), 1);
    User user2 = new User("关羽", "222", new Date(), 2);
    User user3 = new User("张飞", "333", new Date(), 0);

    List<User> list = new ArrayList<>();
    list.add(user1);
    list.add(user2);
    list.add(user3);

    return list;
}

2、@RequestBody

把请求参数为json的字符串数据转成对应的实体或集合

  • 页面
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>test.html</title>
    <script src="../js/jquery-3.4.1.min.js"></script>
</head>
<body>

    <button onclick="testAjax();">传递一Json到后台</button>

    <!--<script>

        var user = {
            uname : "赵六",
            password : "123456",
            birthday : new Date(),
            gender : 1
        };

        function testAjax() {
            $.ajax({
                url : "../test19",
                type : "POST",
                data : JSON.stringify(user),
                contentType : "application/json",
                //dataType : ,
                success : function (result) {
                    alert(result);
                },
                error: function () {
                    alert("出错了");
                }
            });
        }
    </script>-->


    <script>
        var users = [
            {uname : "赵六",password : "123456",birthday : new Date(),gender : 1},
            {uname : "钱七",password : "456456",birthday : new Date(),gender : 0}
        ];

        function testAjax() {
            $.ajax({
                url : "../test20",
                type : "POST",
                data : JSON.stringify(users),
                contentType : "application/json",
                //dataType : ,
                success : function (result) {
                    alert(result);
                },
                error: function () {
                    alert("出错了");
                }
            });
        }
    </script>

</body>
</html>
  • handler
/**
 * @RequestBody : 就是把请求的json格式参数转成实体或集合
 * @param user
 * @return
 */
@RequestMapping("/test19")
@ResponseBody
public String test19(@RequestBody User user) {
    System.out.println(user);
    System.out.println("-----------------------");
    return "success";
}

@RequestMapping("/test20")
@ResponseBody
public String test20(@RequestBody List<User> list) {
    System.out.println(list);
    System.out.println("-----------------------");
    return "success";
}

3、@RestController

@Controller + @ResponseBody

标记在类上

package com.qf.java2007.controller;


import com.qf.java2007.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.support.SessionStatus;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;


/**
 * @author ghy
 * @version 1.0
 */
//@Controller
//@ResponseBody  //标记在类上,相当于当前Controller中的Handler方法返回都标记了@ResponseBody一个注解
@RestController   //等同于@Controller + @ResponseBody
public class Demo3Controller {

    @RequestMapping("/test21")
    public String test21() {
        System.out.println("Demo3Controller test21");
        return "success";
    }

    @RequestMapping("/test22")
    public User test22() {
        User user = new User();
        user.setUname("李四");
        user.setPassword("123456");
        user.setBirthday(new Date());
        user.setGender(1);
        return user;
    }

}

4、如何更换Json转换工具

使用fastjson

  • Springmvc配置
<!-- 无敌模式 -->
<mvc:annotation-driven>
    <!-- 安装FastJson,转换器 -->
    <mvc:message-converters>
        <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
            <!-- 声明转换类型:json -->
            <property name="supportedMediaTypes">
                <list>
                    <value>text/html;charset=UTF-8</value>
                    <value>application/json;charset=UTF-8</value>
                </list>
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

九、@RequestMapping

建立请求URL和处理请求Handler之间的映射关系

根据请求URL找到对应处理请求的Handler

  • handler
/**
 * @RequestMapping
 *  有一个属性必不可少,就是path或者value
 *  path : 指定请求的url们。【多个请求URL可以指定同一个handler来处理,url(n) --> handler(1),反之不行】
 *  value : 作用跟path一样
 *  method : 限定请求方式,可以不写【任何请求方式都可以】。
 *      method = RequestMethod.POST
 *  params : 指定请求必须指定请求参数
 *  headers : 指定请求必须包含指定请求头
 * @return
 */
@RequestMapping(path = {"/", "/index", "/hello/index"}, 
        params = {"username=jack"}, 
        headers = {"User-Agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.190 Safari/537.36"})
@ResponseBody
public String test33(){
    return "success";
}

posted @ 2021-07-21 13:30  牛奶配苦瓜  阅读(40)  评论(0编辑  收藏  举报