第七节_深入浅出了解何为SpringMVC

SpringMVC--超级大爹

1、SpringMVC简介

image-20220322101911635

  • 在web层的环境下,我们每个请求都需要一个Servlet的类,封装为一个类,那么很多请求是不是就需要很多的Servlet类,并且在重复化的写doGet和doPost,这样麻烦吗?相当麻烦。
  • 如何解决?
  • 如果让一个层来专门处理关于前端发送的请求,每个请求都存在与这个类下,那么这样是不是就简单得多了?
  • 这个层是谁?
  • 之前接触过的Controller,YYDS

1.1、SpringMVC的出现及效果(文字叙述)

  1. 当真正进行业务开发的时候,还是那句话,会有相当多的Servlet,这些Servlet每个都是不同功能所对应的请求资源路径
  2. 他们的共同点是什么?接受请求,访问业务层,获取业务层传递给控制层的响应结果,最终,再把响应结果,返还到页面,指派视图、指派页面,这里是不是出现重复操作了?
  3. 那么我们能不能通过一个,一个组件,来帮我们完成通用的操作,或者是特有的操作
  4. 我们其他的Servlet再去完成一些特有的行为操作
  5. 而抽取的那一部分,就是web层的框架为我们完成的

1.2、图解

1.2.1、单个请求的过程图

image-20220322210021889

问题所在,从客户端发送一个请求到最后响应给客户端的页面,视图,这个过程如此的繁琐,如果是多个请求,

那么会需要多少个Servlet,有没有办法可以简化

1.2.2、SpringMVC到底做了什么,简化了开发?(文字+图解)

重点:我们知道多个请求对应多个Servlet,这些Servlet都需要进行某些相同的操作,例如封装请求数据,调用业务层,获取响应的数据,最后为客户端指定视图,页面。这些操作是相同的,我们要做的,就是将他们相同的部分抽取出来,然后由这个相同的部分,共有的Servlet操作,去调用我们的不同请求下的特有处理

图解

image-20220322211505047

概述:前三步的内容不变,将之前第四步,调用相对的Servlet资源中的Servlet进行一个拆分,将他们的行为分成两种,共有和私有行为,由共有行为,去调用相对的私有行为或者是特有行为,这就是SpringMVC的目的

2、SpringMVC开发步骤

需求:客户端发送请求,服务器接收请求,执行逻辑并进行视图跳转

2.1、导入SpringMVC相关坐标

<!-- 导入springmvc依赖环境 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.16</version>
</dependency>

2.2、配置SpringMVC核心控制器DispatherServlet(前端控制器)

  • 这个DispatherServlet的作用就是,它配置好了,其他的特有行为,就可以通过注解Controller的方式被他进行调用,进行相对的特化处理

  • 就是配置我们的web.xml配置文件

  • <!-- 配置SpringMVC的前端控制器 -->
    <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 当程序运行的时候执行一次 -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <!-- 配置映射地址 -->
    <servlet-mapping>
        <!-- 缺省地址?我也不懂 -->
        <servlet-name>DispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    

2.3、创建Controller类和视图页面(HTML或者JSP)

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>SpringMVC部署已完成</h1>
</body>
</html>

2.4、使用注解配置Controller层中的业务方法映射地址(@RequestMapping)

  • @RequestMapping:在该注解下的方法,被这个注解配置了请求映射
  • 当浏览器访问success,这个请求资源的时候,@RequestMapping这个注解会帮我映射到所对应的getSuccess()方法;
  • 当前端控制器被启动的时候,请求发送过来以后,控制器通过什么样的方式来寻找对应的请求资源、@RequestMapping中的资源路径,就是控制器接收到的请求路径,控制器会通过这个请求路径寻找到对应的请求资源,和请求资源下的映射方法
@Controller
public class UserController {

    @RequestMapping("/success")
    public String getSuccess(){
        System.Out.Println("Controller running....")
        // 这个return是我要跳转的视图
        return "success.jsp";
    }
}

2.5、配置SpringMVC核心文件,Spring-mvc.xml(SpringMVC层单独的包扫描)

2.5.1、我们创建好了控制层的对象之后,SpringMVC是不知道的,他怎么才能知道?(包扫描)

  • 控制层的对象我们会通过注解的方式注入到容器当中,SpringMVC通过包扫描这个方法,最终将我们的控制层对象注入到容器当中,这个方法是SpringMVC独有的包扫描方法

  • 创建spring-mvc.xml文件,这是SpringMVC独有的配置文件,关于SpringMVC的配置都会在这个配置文件当中进行

  • <!-- 引入context的命名空间进行我们的包扫描 -->
    <context:component-scan base-package="com.waves.controller" />
    

2.5.2、问题:这个配置文件如何才能被加载到?

  • 这个配置文件是谁用–SpringMVC前端控制器使用,他使用,那么他需要知道这个配置文件的位置吗?

  • 需要,非常需要!

  • 重新改造我们的SpringMVC启动器的配置

  • <servlet>
      <servlet-name>DispatcherServlet</servlet-name>
      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
      <!--当这个Servlet创建的时候,告诉他配置文件在哪里-->
      <!-- 初始化Servlet -->
      <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>
    

2.6、发起请求测试

image-20220322221911594

image-20220322221927758

2.7、配置SpringMVC的时候遇到的问题–IOException parsing XML document from ServletContext resource /WEB-INF/dispatcherServlet-servlet.xml

image-20220322221726275

  • 问题:对控制器进行包扫描初始化的时候这里写成了大写image-20220322221803617
  • 还有就是我之前并没有严格按照老师的要求进行操作

3、快速入门的流程小结

image-20220322224711269

4、SpringMVC组件解析

image-20220322230536507

  1. 客户端发送一个请求,这个请求到项目里面找谁呢—前端控制器-DispatcherServlet,(然而)只负责组件的调用
  2. 优先需要干个什么事情呢他需要知道根据你的请求,去寻找哪个资源?而找资源和解析资源的这个过程他会负责去寻找一个叫HandlerMapping(处理器映射器)这个组件,这个组件负责对发送过来的请求进行一个解析,知道这个请求最后寻找的资源在哪个组件当中,他帮我们执行完工作之后,他会返回一串的资源地址前端控制器
  3. 为什么是一串?因为在一个资源发送过来的时候,实际上是需要经过很多资源的,拿拦截器举例子,这个请求从发送,到被资源点接收并实现,这个过程,他需要经过拦截器的一系列的辨认,经过很多的资源,阻拦,才能到达目的地
  4. 他会帮我返回一个HandlerExecutionChain(处理器的执行链)给前端控制器,这个链中封装着我要执行的这一系列资源的顺序
  5. 前端控制器拿到这个处理器执行链之后,他依旧不会帮我执行那些请求,而是去请求HandlerAdaptor(处理器适配器)处理器适配器,他来帮我去执行那些要被调度的资源(也就是我执行链中需要被执行的资源)
  6. 接下来HandlerAdaptor会请求处理器(原则意义上是请求Handler),实际上我(web应用下)的处理器就是Controller,处理器之后会返回一个模型和视图对象(ModelAndView)处理器的适配器,然后处理器适配器最终再将这个模型和视图对象返回给前端控制器
  7. 然后前端控制器将得到的模型和视图对象,放到视图解析器(ViewResolver)当中去对视图对象进行一个解析,解析出来之后,视图解析器返回一个视图的对象View给前端控制器(这里常用的就是JSP)
  8. 前端控制器对视图解析器处理好的视图对象进行一个解析渲染、最终将内容返回给客户端
  9. image-20220322231320106

5、SpringMVC注解解析

5.1、@RequestMapping

5.1.1、他的作用和用途

他主要是对客户端请求的这个虚拟地址,进行映射,到具体的某个方法上

客户端发送一个资源名称,这个东西需要跟我最终的某个类的某个方法上进行一个匹配

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

5.1.2、作用的位置

放在类上,作为请求的一级目录,如果类上不放的话,就相当于应用的根目录

放在方法上,请求URL的二级目录,与放在类上的@RequestMapping标注的一级目录,一起组成一个虚拟访问的路径

image-20220322232035588

所以就直接找8080/下的quick资源

image-20220322232142466

5.1.3、测试遇到的问题

  • 将请求的虚拟路径改为二级目录配置

  • image-20220323001543806

  • 找不到user这个目录下的success.jsp文件

  • 为什么呢?

  • success.jsp文件应该是放在webapp这个包下的,应该要在user的路径之前

  • localhost:8080/目录下

  • 将success.jsp前面加个**/**

  • @Controller
    @RequestMapping("/user")
    public class UserController {
        @RequestMapping("/success")
        public String getSuccess(){
            System.out.println("Controller running......");
            return "/success.jsp";
        }
    }
    

5.1.4、@RequestMapping的属性

image-20220322235351257

5.1.4.1、method的值是枚举方式的

image-20220323002409141

我们试用下post的请求方式

image-20220323002135869

我们选择post请求看下效果

image-20220323002250383

5.1.4.2、params:这个就相当于要求前端必须传递过来一个,或者多个较为简单的请求参数过来

image-20220323002739775

image-20220323002717214

6、SpringMVC的XML配置文件解析

6.1、包结构

  • 之前关于SpringMVC的一个操作流程基本是分析明白了,我们导入的SpringMVC的这个包下,执行的那些配置文件可以单独讲一下
  • 在我们的jar包中找到后缀为webmvc的那个jar包
  • image-20220323154548435
  • image-20220323154603571
  • 在这里我们就可以看到DispatcherServlet(前端控制器的)文件,点开看看,看见这几个熟悉的大兄弟
  • image-20220323154822063
  • 找到我们的视图解析器,点击进入它的内部方法当中查看原码–InternalResourceViewResolver.class

6.2、视图解析器–InternalResourceViewResolver.class原码查看

  • 我们在内部看到了一个有参构造
  • image-20220323155022477
  • 他会接受一个前缀和后缀,这是什么?有印象不?接下来我们到他爹那个地方去看看

6.3、InternalResourceViewResolver的父类—UrlBasedViewResolver

  • 父类的内部有两个参数
  • image-20220323155202547
  • 翻译的意思就是,重定向的前缀,和请求转发的前缀
  • 在联想到我们之前控制层写的那个方法,返回一个/success.jsp
  • 这个请求,最终的效果是这样的
  • image-20220323155442722
  • 地址并未改变,那么实际上这个东西的本质是什么?–请求转发
  • 那么实际上从前端控制器,传递给视图解析器的时候,传递的这个参数–/success.jsp,实际上是将那个forward省略掉了,但是我们也可以加上去,甚至也可以把它转换成重定向–redirect,内部的响应方式实际上是一串字符串的拼接,视图解析器或者说是前段控制器最终执行的一个效果是这样的,就是把那些字符串进行拼接,然后组成一串虚拟路径。
  • 最终进行渲染响应给服务器,然后服务器在响应给客户端,是这么一个操作

6.4、我们可以通过设置这个spring-mvc.xml文件,对我们需要的功能,进行一个增强,怎么个增强法?

  • 我将我的那个jsp文件专门放到一个文件夹当中image-20220323160315056

  • 然后,我controller内部的参数是不是要修改下,修改成这种image-20220323160353481

  • 刚刚看原码的时候,父类(UrlBasedViewResolver)当中有set方法image-20220323160543627

  • 设置发送过来的前缀和后缀,我发送过来的前缀是不是/jsp/,后缀是.jsp

  • 那么我是不是可以通过配置spring-mvc.xml配置文件的方式,为我需要的功能进行一个增强?

6.5、配置spring-mvc.xml

  • UrlBasedViewResolver,这个类是属于视图解析器的一个实现类,那我对他进行配置就行

  • 通过什么配置,属性嘛,set的属性嘛

  • <!-- 配置视图解析器 -->
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        // setPerfix,setSuffix
        <property name="prefix" value="/jsp/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>
    
  • 这样我的Controller的内部方法的返回值是不是就可以直接写成这样

  • @RequestMapping(value = "/success",method = RequestMethod.GET)
    public String getSuccess(){
        System.out.println("Controller running......");
        return "success";
    }
    
  • 运行效果

  • 一下回到最初的起点~

  • image-20220323161853468

7、SpringMVC的请求和响应

7.1、SpringMVC数据响应的方式

7.1.2页面跳转

1、直接返回字符串–快速入门的案例
  • 此种方式会将返回的字符串与视图解析器的前后缀拼接后跳转(默认情况是转发)
  • image-20220323164314591
2、通过ModelAndView对象返回,返回一个视图的对象
  • 返回一个ModelAndView对象,不返回字符串

  • 案例:success2

  • 只返回一个视图对象

  • // 返回一个模型对象
    @RequestMapping(value="success2")
    public ModelAndView getSuccess2(){
        // 这里还是要new 对象
        ModelAndView model = new ModelAndView();
        /**
         * Model:模型;作用:封装数据的
         * View:视图;作用:展示数据的
         */
        // 只返回一个视图对象,设置视图名称
        model.setViewName("success");
        return model;
    }
    
  • 效果

  • image-20220323165246575

  • 封装一个数据给前端

  • // 返回一个模型对象
    @RequestMapping(value="success2")
    public ModelAndView getSuccess2(){
        // 这里还是要new 对象
        ModelAndView model = new ModelAndView();
        /**
         * Model:模型;作用:封装数据的
         * View:视图;作用:展示数据的
         */
        // 只返回一个视图对象,设置视图名称
        model.setViewName("success");
        // 封装一个数据过去,添加数据
        model.addObject("username","黄先生!");
        return model;
    }
    
  • jsp页面获取数据–EL表达式image-20220323165540862

  • 效果

  • 这里遇到了一个bug,JSP识别不了这个表达式

  • 解决方法–JSP中加入–<%@ page isELIgnored=“false” %>

  • image-20220323171107516

3、ModelAndView2,页面跳转的一些优化写法
  • 如果需要返回ModelAndView,可以直接在参数哪里写入,为什么呢,因为SpringMVC检测到你这个方法需要一个内部的参数ModelAndView,他会自动帮我们注入这个形参,参数不为空,肯定是可以使用的

  • @RequestMapping(value="success3")
    public ModelAndView getSuccess3(ModelAndView model){
        // 封装一个数据过去,添加数据
        model.addObject("username","SpringMVC参数注入");
        // 只返回一个视图对象,设置视图名称
        model.setViewName("success");
        return model;
    }
    
  • 效果分析

  • image-20220323171746490

  • 也可以返回字符串,但是参数需要一个Model,相当于把原先的拆开了,我们依然可以对其进行参数的注入和设置值

  • // 优化写法2
    @RequestMapping(value="success4")
    public String getSuccess3(Model model){
        // 封装一个数据过去,添加数据
        model.addAttribute("username","拆分写法");
        return "success";
    }
    
  • 效果

  • image-20220323171859006

4、request对象也可以被注入(在域中存储数据)–不常用
  • 基于SpringMVC的编写者,我们知道在方法体当中SpringMVC会将我们需要的参数进行一个注入,我们能想到的参数,人家基本都帮我们封装好了,直接调用即可

  • Request对象也可以,并且这个Request的对象就是请求发送过来的时候服务器给我们前段控制器封装好的那个Request对象,所以绝对不是空的

  • 代码编写–在域中存储数据

  • // 优化写法3
    @RequestMapping(value="success5")
    public String getSuccess5(HttpServletRequest request){
        // 封装一个数据过去,添加数据
        request.setAttribute("username","域中存储");
        return "success";
    }
    
  • 测试

  • image-20220323172822769

7.1.3回写数据

1、@ResponseBody–直接返回字符串

这个注解的作用是告诉SpringMVC我要返回的是一个字符串,而不是一个视图

image-20220323173855582
  • 代码书写

  • // 回写数据告诉服务器我响应的是一个字符串
    @RequestMapping(value="success6")
    @ResponseBody
    public String getSuccess6() {
        return "hello ResponseBody!";
    }
    
  • 响应效果

  • image-20220323191428261

2、返回一个JSON串
  • 导入fasterJSON的依赖

  • <!-- 导入fastjson的坐标 -->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.62</version>
    </dependency>
    
  • JSON与java对象互相转换的方法

  • String js = JSON.toJSONString(user);
    // 这里放的是JSON对象的值
    user us = JSON.parseObject("{\"password\":\"123123\",\"userId\":1,\"username\":\"张\"}",user.class);
    
    
  • 代码编写

  • @RequestMapping(value="success7")
    @ResponseBody
    // 转换JSON对象
    public String SetJson(){
        // 创建User对象,我们这里只是模拟一下
        User user = new User();
        user.setName("黄先森");
        user.setAge("21");
        String js = JSON.toJSONString(user);
        return js;
    }
    
  • 跑程序

  • image-20220323191451540

3、·返回对象或集合–Json对象(SpringMVC帮我们集成了这个功能)
  • Jackson工具

  • 导包

  • <!-- JACSON转换JSON工具的包-->
    <dependency>
      <!-- 这个是最核心的 -->
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.11.4</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.11.4</version>
    </dependency>
    <!-- 注解相关的 -->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-annotations</artifactId>
      <version>2.11.4</version>
    </dependency>
    
  • 使用

  • // Jackson转换工具
    @RequestMapping(value="success8")
    @ResponseBody
    // 转换JSON对象
    public String SetJson1() throws JsonProcessingException {
        // 创建User对象,我们这里只是模拟一下
        User user = new User();
        user.setName("黄先森");
        user.setAge("21");
        // 使用json转换工具--Jackson
        ObjectMapper objectMapper = new ObjectMapper();
        String json = objectMapper.writeValueAsString(user);
        return json;
    }
    
  • 跑一转

  • image-20220323194242871

4、配置spring-mvc核心配置文件,让他来帮我们完成JSON对象的转换
  • 只要我返回的是一个对象或者集合,他就会自动的帮我转换为JSON对象(在内部设置一个JSON的转换器)

  • <!-- 配置处理器适配器,在内部进行JSON串的转换 -->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
        <!-- 内部需要配置一个消息转换器-list集合类型的 -->
        <property name="messageConverters">
            <list>
                <!-- 内部需要一个HTTPMessageConverter的对象,那就直接用Bean标签引入 -->
                <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
            </list>
        </property>
    </bean>
    
  • 编写方法,测试效果

  • @ResponseBody依然要写,因为我并不是跳转页面,而是返回一个JSON串

  • // 我不想每次都使用工具来帮我转换对象,SpringMVC为我们集成了这个功能
    @RequestMapping(value="success9")
    @ResponseBody
    // 转换JSON对象
    public User SetJson2() throws JsonProcessingException {
        // 创建User对象,我们这里只是模拟一下
        User user = new User();
        user.setName("luoxiang");
        user.setAge("45");
        return user;
    }
    
  • image-20220323194918727

5、优化处理–mvc:annotation-driven–SpringMVC的注解驱动(手动配型的缺陷)

目的就是代替我们之前配置过的那个JSON转换器,因为之前的配置过程很繁琐,SpringMVC确实为我们集成了这些功能,但是想要使用,是不是得告诉SpringMVC呢?

在SpringMVC的各个组建中,处理映射器适配器视图解析器被称为SpringMVC的三大组件,使用mvc:annotation-driven,会自动给我们加载这三个组件,可以再SpringMVC的配置文件当中使用-mvc:annotation-driven代替他们的配置

同时:使用-mvc:annotation-driven,底层会默认为我们集成jackson进行对象或者说集合的json字符串的转换

  • 配置-mvc:annotation-driven
  • 引入命名空间–mvc
  • image-20220323195829041
  • 使用mvc:annotation-driven
  • 在跑一变代码
  • image-20220323200021513

8、Spring获取请求参数(类型)

8.1、基本类型的参数(SpringMVC的自动注入)

重点:Controller中的业务方法,参数名称要和请求参数的名称一样,就是HTML页面的标签的name属性值,要和我Controller方法体中的参数值一样(请求参数的KEY 与 方法体的 形参 名字要一致)例如

切记:只是把前端的请求参数给你注入到你的新参当中,哦,好像没什么问题,他注入进来了,无论你是对象还是普通类型亦或者是集合,你这个新参当中的值都不会为空

<h1 name="zhangsan">123</h1>
// 名称一致
<%public void getName(String zhangsan){
}%>

这样的好处是,参数值会进行自动映射匹配–很智能

image-20220323201533513

形象了吧

  • 测试上代码

  • // 获得基本的参数,我要参数
    @RequestMapping(value="success10",params = {"username","password"})
    public void SetJson2(String username,String password){
        // 直接打印即可,因为已经封装好了
        System.out.println(username);
        System.out.println(password);
    }
    
  • 报错原因

  • image-20220323202733520

  • @RequestBody,注解的含义是什么?–不进行页面的跳转

  • 如果不写,我们之前配置了springmvc的配置文件,他会默认去寻找这个路径,那自然是找不到的,所以要写

  • 重新加上@ResponseBody

  • // 获得基本的参数,我要参数
    @RequestMapping(value="success10",params = {"username","password"})
    @ResponseBody
    public void SetJson2(String username,String password){
        // 直接打印即可,因为已经封装好了
        System.out.println(username);
        System.out.println(password);
    }
    
  • 运行

  • image-20220323202951190

  • 查看控制台

  • image-20220323203002928

8.2、POJO类型的参数(Bean)

1、导论

概述,如果我的请求参数key值和我实体类中的属性值对应,那么SpringMVC就会自动帮我把请求参数的值封装到我的实体类中,为什么呢?

实体类中是否有get和set的方法,按照常理来说,你set和get的方法后面跟着的是不是首字母大写的参数,但是我们如果要为一个Bean的对象当中注入值的话,我们怎么做,有现成的set方法我们是不是只需通过这种方式来完成注入就可以了?因为什么,propertyname属性,就是set后面的那一坨参数,并且,首字母是小写的,SpringMVC就通过这种反射的方式来为我们将参数和实体进行一个封装。

请求参数的那个name值,只要和方法体的那个pojo类形参的属性名一致,SpringMVC依旧会为我们封装

2、图解

image-20220323203824314

3、代码测试
  • // 获得基本的参数,我要参数
    @RequestMapping(value="success10",params = {"name","age"})
    @ResponseBody
    public void SetJson2(User user){
        // 直接打印即可,因为已经封装好了
        System.out.println(user.getName());
        System.out.println(user.getAge());
    
    }
    

    image-20220323210718266

8.3、数组类型的参数(name = hobby;value=1,2,3…)

1、图解

image-20220323210924719

2、代码

// 获取数组类型的请求数据
@RequestMapping(value="success12")
@ResponseBody
public void SetJson4(String [] stus){
    // 数组默认情况下打印的是一个地址,用集合对其进行转换
    System.out.println(Arrays.asList(stus));
    // 要不然你就只能for循环打印出来
}

image-20220323211334925

image-20220323211351324

8.4、集合类型(跟数组差不多)

1、问题所在

获得集合的参数的时候,需要将集合的参数封装到一个POJO类中才行–JavaBean

例如这样写,他是没有办法通过你集合的名字来为你进行封装和注入的

// 获取集合类型的请求数据
@RequestMapping(value="success12")
@ResponseBody
public void save12(valueObject vo){

}

我们需要将这个集合封装到一个对象当中,这个对象一边叫做VO或者是valueObject

2、准备步骤(页面,VO(valueObject)实体)

valueObject类

image-20220323212239699

JSP页面

这个地方很细致我要说一下,你,怎么才能让服务器知道你传递的是第几个对象的什么属性?这个name属性怎么写?首先我接受这个请求的方法体的形参是什么?–Vo对象,Vo对象当中有什么?–List集合(名字是userList)那么我这个name的名字写什么?我写一个userList[num],什么意思,这个集合的泛型是什么?user对象,userList[num]是不是代表的就是这里面的第几个元素?集合是根据下标来对元素进行索引的嘛~那么后面的**.name**是不是就很明了了,就这么简单!

<form action="${pageContext.request.contextPath}/user/success12" method="post">
    <input type="text" name="userList[0].name"><br>
    <input type="text" name="userList[0].age"><br>
    <input type="text" name="userList[1].name"><br>
    <input type="text" name="userList[1].age"><br>
    <input type="submit">
</form>
  • action里面还是需要写全路径名称的

3、${pageContext.request.contextPath}:这个代码的意思是
  • localhost:8080/SpringMVC__war_exploded/

  • 测试代码

  • // 获取集合类型的请求数据
    @RequestMapping(value="success12",method=RequestMethod.POST)
    @ResponseBody
    public void save12(valueObject vo){
        System.out.println(vo);
    }
    

image-20220323220340770

image-20220323220353401

8.5、集合类型的第二种场景

客户端发起ajax请求的时候,可以发送json数据我们可以指定contentTypeJSON的数据类型,在方法的参数上,我们可以直接使用**@RequestBody**,这个注解,他的作用是可以直接收集集合的数据,并且不需要POJO类的封装

1、集合对象应用场景的操作步骤
  • 首先,发送过来的必须是一个JSON数据的格式
  • 其次,在AJAX请求当中,contenttype的格式必须为:contentType: “application/json;charset=utf-8”
2、AJAX请求的准备
  • 导入jQuery的文件

  • 为什么这里写了

  • <%-- 这里就直接发送一个AJAX的请求 --%>
    <script src="${pageContext.request.contextPath}/js/jQuery.js"></script>
    
3、**${pageContext.request.contextPath}**的详细解析
  • 我们把这段路径拆分来看一下,这个东西实际上指的是localhost:8080/SpringMVC__war_exploded/
  • localhost:8080–指代本机的地址
  • /SpringMVC__war_exploded/—本机下的项目名称
  • 那么,我寻找我这个项目下的(/SpringMVC__war_exploded/)下的js文件当中的jQuery.js,没毛病吧?
  • 因为我发送请求的时候需要访问这个静态资源来发送我的ajax请求
4、报错–找不到(未配置缺省目录(<mvc:resources mapping=“/js/**” location=“/js/”/>))

image-20220325084932182

  • 为什么会出现这个报错呢,我资源地址目录写的没问题啊?
  • 我们在配置SpringMVC的DispatcherServlet,的时候,还记得那个url-parrtern吗,当时配置的是什么
  • image-20220325085523256
  • 详细原理你不知道,那么这个“/”号知道是什么意思吧,就是可以匹配url地址的,例如/login,/register,等等请求,他都可以匹配到,并且可以寻找到该资源路径,我好像懂了,为什么我的@RequestMapping的请求资源路径前面要加/了!
  • 那么,这个/,并不能匹配携带了后缀的文件,例如,jsp,html,js等等文件,他都匹配不到,那么我们怎么才能设置呢?
5、设置<mvc:resources mapping=“/js/**” location=“/js/”/>
  • 上面这一坨是啥意思?
  • mapping:资源的意思,/js/**,意为静态目录下的,js文件夹当中的所有文件均可以访问
  • location是什么意思:在请求进行访问的时候设置的一个前缀
6、ajax请求,我们打开控制台查看发送的请求(location为什么会是/js/)
  • 先将我们JSP的代码完善,我JSP的这个文件要访问的是映射资源success13

  • 在这个页面被调用的时候就发送一个ajax请求

  • <head>
        <title>233</title>
        <%-- 这里就直接发送一个AJAX的请求 --%>
        <script src="${pageContext.request.contextPath}/js/jQuery.js"></script>
        <script type="text/javascript">
            // 模拟的数据
            var userList = new Array();
            // 填充数据
            userList.push({name:"zhangsan",age:"18"})
            userList.push({name:"lisi",age:"28"})
            console.log(userList)
            // 发送AJAX请求
            $.ajax({
                type: "POST",
                url: "/user/success13",
                data: JSON.stringify(userList),
                contentType: "application/json;charset=utf-8"
            });
        </script>
    </head>
    
7、仔细看控制台发生了什么

image-20220325091148694

  • 出现了三次请求,第一次请求是我们请求这个AJAXTest.JSP这个页面的请求,他给我们找到了

  • 接着在这个页面内部他为我们先去寻找JQuery.js这个文件,我们已经配置过了,所以他可以找到

  • 接着,我们发送了一个请求,这个请求的资源路径是**/success13**

  • 看我的idea控制台image-20220325091415342

  • 现在应该没有什么不懂的地方了吧

8、@RequestBody–请求体
  • 对象或者集合类型的数据获取的时候,我们在后台写上这个注解,就相当于,传递过来的请求参数,必须是JSON格式的(一般都是这样),而我传递过来的是一个List类型的集合,当中存储的是user对象,所以在传递过来的时候,通过SpringMVC的自动装配,我可以不通过POJO类的封装直接获取到这个集合的数据

  • 前台请求的数据,ajax请求发送,并且请求数据为JSON格式

  • image-20220325092013615

  • 这是我后台@RequestBody注解获取到的参数,可以通过自动注入的方式直接来获取

  • image-20220325092119242

9、优化处理(mvc:default-servlet-handler)
  • 在访问资源时候,springMVC会帮我匹配这个资源的地址,那么当他找不到的时候,配置这个东西(mvc:default-servlet-handler),将权限交给Tomcat,让他来帮我寻找那些找不到的静态资源
  • Tomcat是具有查询静态资源的能力哦,具体怎么查找的我好像也忘了
  • image-20220325092857914

8.7、post请求乱码的问题(配置全局过滤器)

1、问题描述

  • 我们之前不是设计了一个表单数据吗,通过post的方式提交到success12这个请求中
  • 输入了中文的时候会乱码
  • image-20220325093220101

2、解决思路–图解

image-20220325093323880

3、开干(原理解析)

  • 在springframework.web这个包下有个filter,我们对这个fuilter传递一个变量encoding,这个encoding是初始化默认的参数,值传递为UTF-8
  • image-20220325093550403
  • 对所有的资源进行一个编码的解析
  • image-20220325093556600

4、开干–物理解析

<!-- 配置全局过滤器识别UTF-8 -->
  <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>
  • 跑代码–very -deep!
  • image-20220325094148155

8.8、参数绑定注解@RequestParam

当请求的参数名称与控制层的业务方法参数名称不一致的时候,就需要通过RequestParam注解,来为我们的形参注入数据的绑定

1、实践

  • 新设置一个请求success14

  • 这是我的访问路径

  • image-20220325102724875

  • 这是方法的内部参数

  • image-20220325102621070

  • 看内部打印的结果image-20220325102735957

2、绑定请求参数

  • 重新设计方法

  • @RequestMapping(value = "success14")
    @ResponseBody // 不进行页面跳转
    // 将请求参数注入到我的对象当中User(name和age这俩参数)
    public void save14(@RequestParam("name")String username,
                       @RequestParam("age") String myAge){
        System.out.println(username);
        System.out.println(myAge);
    }
    
  • 实践结果

  • image-20220325102955143

3、@RequestParam其他参数的介绍

  • value:请求参数的名称
  • required:在指定请求的时候,是否包含了这个参数,默认值为true,如果请求参数里面没有这个参数的话,直接报错
  • defaultValue:如果没有指定请求参数的时候,使用我们设置的默认值
3.1、required
  • 我现在直接访问我刚刚那个请求数据
  • image-20220325103558502
  • 他找不到这个参数name
  • 将其设置为falseimage-20220325103705970
  • 进行访问
  • image-20220325103759924
  • 只是我们获取到的数据为空
  • image-20220325103816585
3.2、defaultValue
  • 我们对其设定一个默认值name为张三

  • image-20220325103856119

  • 查看结果

  • image-20220325103921835

8.9、获得Restful风格的参数

1、概述

  • 我们平常发送一个携带请求参数的请求过去的时候,默认写法是不是url/ + 请求的资源路径+ ? + 参数1 = 参数值 & 参数2 = 参数值…这样的方式来发送请求
  • 而Restful的风格是你直接使用 url / 请求资源路径 / 请求参数,这样的方式来进行传递

2、图解

image-20220325105212864

3、代码块讲解

image-20220325105538025

4、@PathVariable注解实战,我准备两个参数看看得不得行

  • 代码块

  • // Restful风格的请求参数
        @RequestMapping(value = "success15/{uname}&{psw}")
        @ResponseBody
        public void save15(@PathVariable("uname") String username,
                           @PathVariable("psw") String password){
            System.out.println(username);
            System.out.println(password);
        }
    
  • 访问地址:localhost:8080/SpringMVC__war_exploded/user/success15/张三&123

  • 访问结果

  • image-20220325110028773

  • 说明是没问题的,多个参数之间用&符号隔开就行7

5、Restful风格–请务必遵守他的请求方式

image-20220325110415931

8.10、自定义类型转换器

1、概述

我们知道,在客户端发送一个请求的时候,如果携带了请求参数,那么在后端,保持参数名一致或者使用注解的方式,SpringMVC会将请求参数的值自动给我们注入到我们的形参当中。

并且,无论客户端发送过来的参数是什么类型的,到了后端是不是都会转换成字符串类型?这个在咱们web阶段就已经知道了,但是

当我们的形参设置为其他类型,例如int,boolean等,SpringMVC也会帮我们自动注入,这是为什么呢?

说明SpringMVC内部具有自定义的类型转换器,但是他有类型转换器还不够,业务的需求是无限的,有些时候我们遇到SpringMVC无法为我们转换的数据类型的时候,我们就需要自定义一个类型转换器了

2、编写自定义类型的转换器(自定义转换器类,实现Converter接口)

Converter是SpringMVC提供的一个转换器的接口

  • 实现Converter接口,两个泛型,第一个是你传递进去的数据类型,第二个是你需要转换的数据类型、

  • 其次,实现这个方法convert

  • public class DataConverter implements Converter<String, Date> {
            @Override
            public Date convert(String source) {
                // 将日期字符串转换成日期对象
                SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
                // 创建时间类型的对象
                Date date = null;
                try {
                    date = formatter.parse(source);
                } catch (ParseException e) {
                    e.printStackTrace();
                }
                // 返回一个这个时间类型的对象
                return date;
            }
        }
    

3、配置文件中声明转换器

  • 实际上我们声明的是一个转换器的工厂对象

  • 内部需要注入一个converter的列表

  • 通过指定转换器工厂ConversionServiceFactoryBean,让他来帮我创建我自定义的那个转化器对象DataConverter

  • <!-- 声明这个转换器 -->
    <bean id = " conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <!-- 实际上声明的是一个工厂对象 -->
        <!-- 内部需要注入一个converter的列表 -->
        <property name="converters">
            <!-- 把我们自定义的那个转换器对象的全限定名放进去 -->
            <list>
                <bean class="com.waves.converter.DataConverter"></bean>
            </list>
        </property>
    </bean>
    

4、在配置文件中,使用**<-annotation-driven->**中引用转换器

image-20220325175924078

  • 在conversion-service属性中放入我们配置好的转换器工厂对象

  • <mvc:annotation-driven conversion-service = "conversionService"/>
    
  • 实战跑一下

  • 报错了

  • image-20220325181328889

  • 不常用,了解就行

8.11、获取请求头(@ReuqestHeader),获取Cookie的值(本身也是头,@CookieValue)

image-20220325203919250

image-20220325203938970

  • 二者携带的参数分别是User-Agent,和JSESSIIONID

  • 编写代码(required设为false,就算不携带这个头也可以访问我的这个方法)

  • // 获得请求头,和Cookie的值
    @RequestMapping(value = "success17")
    @ResponseBody
    public void save17(@RequestHeader(value = "User-Agent",required = false) String userAgent,
                       @CookieValue(value = "JSESSIONID") String cookieValue){
        System.out.println("请求头的值是:"+userAgent);
        System.out.println("cookie的值是:"+cookieValue);
    }
    
  • 效果截图

  • image-20220325204726415

  • image-20220325204732767

8.12、SpringMVC文件上传

1、文件上传三要素

  • 肯定要个input的标签
  • post的提交方式
  • 文件上传上的什么什么enctype属性必须是一个多部分表单形式的,不能是原始的url方式,enctype=" multipart/form-data"

2、创建表单(uoload.jsp)

<%-- 设置一个表单 --%>
    <from action="${pageContext.request.contextpath}/user/success18"
          method="post" enctype="multipart/form-data">
        上传文件:<input type="file"><br>
        <input type="submit" value="上传">
    </from>

3、文件上传的原理

当我们表单的enctype属性修改为: multipart/form-data的时候,上传类型变为多部分表单形式,这个时候,我们无法通过request.getxxx,的形式来获取表单的任何属性,因为全部失效了,表单的默认enctype属性是"application/x-www-form–ulrencoded",**默认属性的表单的内容正文形式是,key=value&key1=value1…**的形式,所以修改了该属性以后,方法就失效了

这个时候我们去抓包,他这个正文就是这样的,不是键值对的形式了

image-20220325210754813

这个图好看点,是可以获取到我们内部的属性的

image-20220325211010173

4、单文件上传步骤

image-20220325211208504

  • 导入fileupload和id的坐标

  • <!-- 文件上传 -->
    <dependency>
      <groupId>commons-fileupload</groupId>
      <artifactId>commons-fileupload</artifactId>
      <version>1.2.2</version>
    </dependency>
    <dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>2.6</version>
    </dependency>
    
  • 配置文件上传解析器

  • 注意:这里有一个小坑,这个id必须为multipartResolver,因为这是一个公用接口,你要写成这样才能被框架识别到

  • <!-- 配置文件上传解析器 -->
    <bean id="multipartResolver"
            class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!-- 上传文件的总大小 -->
        <property name="maxUploadSize" value="50000"></property>
        <!-- 上传单个文件的大小 -->
        <property name="maxUploadSizePerFile" value="50000"></property>
        <!-- 上传文件的编码类型 -->
        <property name="defaultEncoding" value="UTF-8"></property>
    </bean>
    

5、配置文件上传代码,属性MultipartFile(源自SpringBoot的笔记)

  • MultipartFile接口是SpringMVC提供的一个接口,这个接口为我们包装了获取文件类型的数据(任何类型的file都可以接收到这个对象参数当中)
  • 而Springboot整合了SpringMVC,所以我们只需要在处理该类请求的方法参数列表上声明一个参数类型为MultipartFile的参数然后Springboot会自动将客户端传递给tomcat的文件数据赋值给这个参数

6、编写代码

  • 将文件保存到–,这个我就注释掉了,后期开发的时候再用,肯定不是存在我自己的这个电脑上、 而是服务器的电脑上
// 上传文件
@RequestMapping(value = "success18")
@ResponseBody
public void save18(MultipartFile upload) throws IOException {
    System.out.println("获取到文件类型的地址为:"+upload);
    // 获得上传文件的名称
    String originalFilename = upload.getOriginalFilename();
    System.out.println("文件名称为:"+originalFilename);
    //
    //upload.transferTo(new File("D\\file\\"+originalFilename));
}

7、多文件上传,数组解决(MultipartFile [] xxxx)

  • jsp文件修改

  • <%-- 设置一个表单 --%>
    <form action="${pageContext.request.contextPath}/user/success18"
          method="post" enctype="multipart/form-data">
        上传文件:<input type="file" name="uploads"><br>
        上传文件:<input type="file" name="uploads"><br>
        上传文件:<input type="file" name="uploads"><br>
        <input type="submit" value="上传">
    </form>
    
  • 代码修改–多文件上传

  • @RequestMapping(value = "success19")
    @ResponseBody
    public void save19(@RequestParam("uploads")MultipartFile[] uploadss) throws IOException {
    
        for (MultipartFile multipartFile : uploadss) {
            // 获得上传文件的名称
            String originalFilename = multipartFile.getOriginalFilename();
            System.out.println("文件名称为:"+originalFilename);
        }
    }
    
  • 效果

  • image-20220325221033758

  • image-20220325221044439

  • 24
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值