Spring MVC从0到1,推荐新手收藏(超详细)

Spring MVC引言

面试官:简单谈谈什么是Spring MVC?

Spring MVC是java开源框架Spring Framework的一个独立的模块

MVC框架,在项目中开辟MVC层次架构

对控制器中的功能 包装 简化 扩展践行工厂模式

  • Spring MVC框架主要是操作MVC层面中的C,也就是控制器

MVC架构

MVC: Model View Controller

​ 模型 视图 控制器

  • 模型:即业务模型,负责完成业务中的数据通信处理,对应项目中的service和dao

  • 视图:渲染数据,生成页面.对应项目中的JSP

  • 控制器:直接对接请求,控制MVC流程,调度模型,选择视图.对应项目中的servlet

MVC是线下软件开发中最流行的代码结构形态

人们根据负责的不同逻辑,将项目中的代码分成M V C3个层次

层次内部职责单一,层次之间耦合度低

符合低耦合 高内聚的设计理念.也实际有利于项目的长期维护

开发流程

导入依赖

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

配置核心(前端)控制器

作为一个MVC框架,首先要解决的是:如何能够收到请求!

所以MVC框架大都会设计一款前端控制器,选型在Servlet或Filter两者之一,在框架最前沿率先工作,接收所以请求

此控制器在接收到请求后,还会负责SpringMVC的核心调度管理,所以既是前端又是核心.

在web.xml中配置前端控制器

    <servlet>
        <servlet-name>mvc_leiyu</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 可选配置 懒汉式加载 -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>mvc_leiyu</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

后端控制器

等价于之前定义的servlet

package per.leiyu.web;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @author 雷雨
 * @date 2020/6/24 8:50
 */
@Controller //声明这个类是一个后端控制器
@RequestMapping("/hello")  //这个类的访问路径是一个  /hello
public class HelloController {
    @RequestMapping("/hello1")
    public String hello1(){ //相当于之前的doget  和dopost方法
        System.out.println("hello1");
        return null;
    }

    //之前我们的servlet中只能定义一个方法,也就是访问了servlet的路径就是访问那唯一的方法
    //但是在我们的后端控制器中,一个servlet中可以定义多个方法,每个访问有自己单独的访问路径,这样servlet就不会造成浪费
    

}

  • 之前我们的servlet中只能定义一个方法,也就是访问了servlet的路径就是访问那唯一的方法
  • 但是在我们的后端控制器中,一个servlet中可以定义多个方法,每个访问有自己单独的访问路径,这样servlet就不会造成浪费

配置文件

为MVC的成功成功运行配置文件

在资源文件中创建mvc.xml文件

<?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"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
              http://www.springframework.org/schema/beans/spring-beans-3.0.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="per.leiyu.web"/>

    <!--注解驱动
     让注解能正常的运行-->
    <mvc:annotation-driven></mvc:annotation-driven>
</beans>

  • 注意要在web.xml(前端控制器)中对刚才的配置文件进行注册

前端(核心)控制器:

  1. 前端,接收所以参数
  2. 启动Spring MVC工厂时需要用到mvc,xml
  3. 负责SpringMVC 的流程调度
    <servlet>
        <servlet-name>mvc_leiyu</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:mvc.xml</param-value>
        </init-param>
        <!-- 可选配置 懒汉式加载 -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>mvc_leiyu</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
  • 这样在创建Springmvc工厂时就会加载mvc.xml的配置,启动注解驱动,扫描注解所在的包,最终把后端控制器也创建出来

测试运行

配置Tomcat,并运行访问我们的后端控制器方法

正常输出
image-20200624091629215
但是我们的页面却是404
image-20200624091719694

这是为什么?

原因是我们没有配置视图解析器

视图解析器作用:(对比下面的xml)

  1. 捕获后端控制器的返回值=“index”
    • 因此这时后端控制器的方法中不能返回null,而应该返回index
  2. 解析:在返回值的前后拼接 ==>"/index.jsp"
<!--mvc.xml -->
    <!-- 配置视图解析器
        作用:
            1.捕获后端控制器的返回值="index"
            2.解析:在返回值的前后拼接  ==>"/index.jsp"
    -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>

总结一下

项目分为创建工厂----和运行项目两个阶段

工厂的创建过程:

前端控制器启动—>加载后端控制器---->后端控制器中的注解驱动和注解所在的包以及所对应的后端控制器的路径和方法路径都被前端控制器扫描到

运行项目:

当发送一个请求时-----先被前端控制器接收处理---->处理到一定程度会交给后端控制器(通过路径匹配定义为某一个方法),执行这个方法,这个方法的返回值---->返回值会被视图解析器捕获到(拼接前缀和后缀)----->跳转到JSP----JSP把内容输出给浏览器

接收请求参数

通过控制器中方法的形参 接收请求参数

基本类型参数

请求参数和方法形参 同名即可

Spring MVC默认可以识别的日期字符串格式为:YYYY/MM/dd HH:mm:ss

通过@DataTimeFormat可以修改日志格式

package per.leiyu.web;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.Date;

/**
 * @author 雷雨
 * @date 2020/6/24 11:41
 */
@Controller
@RequestMapping("/param")
public class ParmController {


    //Spring 中定义参数可以直接在参数列表中直接定义
    //怎么接受呢?
    //只要请求参数名和方法的参数名同名,那么就会用方法的参数名来接收请求的参数
    //2020/12/12这种格式是Spring MVC能够自动识别的日期类型的参数,在方法中可以直接定义Data的类型来接收
    //http://xxxx/param/test1?id=1&name=leiyu&gendertrue&birthday=2020/12/12 12:13:20
    @RequestMapping("/test1")
    public String test1(Integer id, String name, Boolean gender, Date birthday){
        System.out.println("test1");
        //测试打印我们传递的请求参数
        System.out.println("id="+id+"name="+name+"gender="+gender+"birthday="+birthday);
        return "index";
    }
}

  • 不建议在方法的参数位置写太多的参数,不利于管理
  • 建议使用实体类来将参数包装在传递

实体收参

在方法参数位置是一个实体类的对象

那么不像传递普通参数那样:要求方法的参数和请求参数直接同名

而是要求:请求参数和实体类的属性同名

    //将参数封装为实体类的对象
    @RequestMapping("/test2")
    public String test2(User user){
        System.out.println(user);
        return "index";
    }
接收参数和实体类对象的对应关系
image-20200624121052035

数组收参

@RequestMapping("test3")
public String test3(String[] hobby){
    System.out.println("test3");
    for(String str: hobby){
         System.out.println(str);
    }
    return "index";
}

集合收参

//UserList类中封装了User的对象,但是提供了获取对象的方法getUsers
@RequestMapping("test4")
public String test4(UserList userList){
    System.out.println("test4");
    for(User user: userList.getUsers()){
         System.out.println(user);
    }
    return "index";
}

路径收参

    //将{name路径匹配到的值赋值给username参数}
    @RequestMapping("test4/{id}/{name}")
    public String test4(@PathVariable Integer id,@PathVariable("name") String username){
        System.out.println("id"+id);
        System.out.println("username"+username);
        return "index";
    }

中文乱码

首先页面中字符集统一

JSP : <%@page pageEncoding="utf-8" %>
HTML: <meta charset="UTF-8">

其次,tomcat中字符集设置,对get请求中,中文参数乱码问题有效

Tomcat配置: URIEncoding=utf-8

最后,设置filter,对post请求中,中文乱码问题有效

没用Spring MVC之前,我们在获取页面的内容时第一步就是设置字符集,但是在Spring MVC中我们没有机会设置,因此使用过滤器来过滤所有的页面,设置其字符集编码格式为utf-8

    <filter>
        <filter-name>encoding</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>encoding</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

跳转

跳转关键字 forward: redirect:

原理是在我们的视图解析器中如果有关键 forward: redirect:就不会自动为我们拼接,但是如果没有就会为我们自动拼接

转发

请求转发 关键字: forward

    @RequestMapping("test1")
    public String test1(){
        System.out.println("请求转发的一种方式,return相当于forward:");
        //return "index";
        return "forward:/index.jsp";
    }

    @RequestMapping("test2")
    public String test2(){
        System.out.println("请求转发路径的写法有两种");
        return "forward:test1";//相对路径,转发到了本类中的其他方法
        //retuen "forward:jump/test1";   和上面的作用是相同的
        //需要注意的是如果是在不同的类的方法中进行转发需要指定完整路径(绝对路径)
    }

请求重定向

请求重定向 关键字: redirect

    @RequestMapping("test3")
    public String test3(){
        System.out.println("请求重定向到页面");
        return "redirect:index.jsp";
    }
    
    @RequestMapping("test4")
    public String test4(){
        System.out.println("请求重定向的路径的两种写法:决定和相对路径");
        return "redirect:test1";
        //return "redirect:/jump/test1";
    }

跳转细节

  1. 在增删改之后,为了防止请求的重复提交重定向跳转(如果用户点击刷新,如果是转发的话,那么就又会执行一遍)
  2. 在查询之后,可以做转发跳转(这里做成转发是因为我们希望的是在用户查询之后,刷新页面的话,会帮用户再查询一次)

传值

C(控制器)得到数据后,跳转到,并向V(视图)传递数据.进而V(视图)中可以渲染数据,让用户可以看到含有数据的页面

转发跳转:Request作用域

重定向跳转:Session作用域

request和session

需要导入相关的依赖

    //使用原生的servlet的request和session来进行传值
    //要导入相关的依赖
    //要存入的域的参数是可选的
    //存入的方式setAttribute和原来的方式相同
    @RequestMapping("test1")
    public String test1(HttpServletRequest request, HttpSession session){
        System.out.println("test1");
        request.setAttribute("name","张三");
        session.setAttribute("age",18);
        return "index";
    }

在JSP中取值

使用的是EL JSEL表达式

${requestScope.name}
${sessinScope.age}

Model(建议使用)

使用Model来存值
Model可以直接和request域对应
使用的是键值对的方式存值

    //使用Model来存值
    //Model可以直接和request域对应
    //使用的是键值对的方式存值
    @RequestMapping("test2")
    public String test2(Model model){
        model.addAttribute("name","李四");
        model.addAttribute("age",17);
        return "index";
    }

ModelAndView

使用ModelAndView来存值

  • 注意:存的值是在request域中
  • 两个重要的方法,一个决定视图,一个决定数据
    //使用MVC本土化的对象ModelAndView(用来做视图整合的对象来传值)
    //它是将数据存入到request域中
    @RequestMapping("test4")
    public ModelAndView test4(){
        //新建ModelAndView对象
        ModelAndView modelAndView = new ModelAndView();
        //设置视图名,即如何转跳
        modelAndView.setViewName("forward:/index.jsp");
        //添加数据
        modelAndView.addObject("name","雷雨");
        return modelAndView;
    }

@SessionAttributes

在类上面定义这个注解,表示有这样的Session域

  • @SessionAttributes(value={“name”,“age”})
  • @@SessionAttributes(names={“name”,“age”})

这两个注解表示的意义是相同的

  • 参数names和value都可以使用
  • 如果有多个参数用大括号括起来

配合Model一起使用

  • 如果没有在类的上Ian定义对应的session域,那么就当做request域来存值
  • 如果有对应的session域那么优先以session为准
@Controller
@RequestMapping("data")
@SessionAttributes(value = {"age"})//model中的age会存入到session域中
public class DataConroller {    
    //使用Model来存值
    //Model可以直接和request域对应
    //使用的是键值对的方式存值
    @RequestMapping("test2")
    public String test2(Model model){
        model.addAttribute("name","李四");
        model.addAttribute("age",17);
        return "index";
    }
}
//

能够通过SessionStatus 移除session中的数据

    //清除Session域中的所有数据
    @RequestMapping("test3")
    public String test3(SessionStatus status){
        status.setComplete();
        return "index";
    }

我是雷雨,一个普本科的学生,主要专注于Java后端和大数据开发

如果这篇文章有帮助到你,希望你给我一个大大的赞
如果有什么问题,希望你能留言和我一起研究,学习靠自觉,分享靠自愿

转载注明出处
https://blog.csdn.net/qq_40742223

猜你喜欢

转载自blog.csdn.net/qq_40742223/article/details/106946859