67-SpringMVC基本应用

SpringMVC基本应用

SpringMVC简介:
MVC模式:
MVC是软件工程中的一种软件架构模式,它是一种分离业务逻辑与显示界面的开发思想
/*
M(model)模型:处理业务逻辑,封装实体,虽然大多数是这样的说明,但是实际上M只是代表要返回的数据而已,只是这个数据由业务逻辑等等产生的,所以说成Service或者Dao层也行,说成返回的数据也行,但本质上是返回的数据而已
V(view) 视图:展示内容
C(controller)控制器:负责调度分发(1.接收请求、2.调用模型、3.转发到视图)
*/

在这里插入图片描述

SpringMVC概述:
SpringMVC 是一种基于 Java 的实现 MVC 设计模式的轻量级 Web 框架
属于SpringFrameWork 的后续产品,已经融合在 Spring Web Flow 中
SpringMVC 已经成为目前最主流的MVC框架之一,并且随着Spring3.0 的发布
全面超越 Struts2,成为最优秀的 MVC 框架
它通过一套注解,让一个简单的 Java 类成为处理请求的控制器,而无须实现任何接口
同时它还支持 RESTful 编程风格的请求

在这里插入图片描述

总结
SpringMVC的框架就是封装了原来Servlet中的共有行为;例如:参数封装,视图转发等
SpringMVC快速入门:
需求
客户端发起请求,服务器接收请求,执行逻辑并进行视图跳转
步骤分析:
/*
创建web项目,导入SpringMVC相关坐标
配置SpringMVC前端控制器 DispathcerServlet
编写Controller类和视图页面
使用注解配置Controller类中业务方法的映射地址
配置SpringMVC核心文件 spring-mvc.xml
*/
创建web项目,导入SpringMVC相关坐标
<!-- 设置为web工程 复制时,看看有没有一样的,一样的会报错-->
<packaging>war</packaging>
<dependencies>
    <!--springMVC坐标,含有控制器,如DispatcherServlet
且有对应的注解,也包括Spring的注解
就如spring-context类似,都有对应的操作
不同的spring是不同的容器,但基本都有容器
即spring-webmvc和spring-context容器可能是不同的(实际上是读取配置文件不同导致的)
由于基本一样,那么对应注解也是可以在配置里进行扫描的(读取配置方式一样)
其中注入操作是从对应容器里进行获取,在69章博客中存在ContextLoaderListener(对于他来说,mvc是子容器,他是父容器)
而由于spring的扫描更快进行,使得操作不了子容器还没有创建的对象
于是就出现了spring的扫描的地方,得不到对应实例
所以一般这样的操作,我们就称作spring是父容器,springmvc是子容器(前端控制器扫描的容器或者对应监听器的容器)
子容器可以访问父容器的实例,而父容器不可访问子容器的实例,只是对应访问而言,如注解的注入访问
但得到对应容器时都是分开的
-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.1.5.RELEASE</version>
    </dependency>
       <!--
servlet坐标,若不使用对应的类,如HttpServletRequest的话,可以不加
这里只是如,其实是必须要加的(这里不加是包括ServletRequest也会没有),只是在tomcat启动时,会默认加上其给出的这个类似的依赖(所以可以考虑设置在开发环境中处理),所以才会说成不加
如果以后说若不使用对应的类,如HttpServletRequest的话,可以不加,那么也是包括了ServletRequest不加,如果没有说,说明是tomcat运行环境为主的,因为这样都可以不加,即单独的不加并没有影响
-->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
        <scope>provided</scope>
        </dependency>
    <!--jsp坐标-->
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.2</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

配置SpringMVC前端控制器DispathcerServlet:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
                             http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <!--springmvc的前端控制器:DispatcherServlet-->
    <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>
        <!--当创建对应的DispatcherServlet实例时,会解析对应参数指定的配置文件,使得注解被扫描
        解析方式或spring的监听器解析方式与spring的类似,使得扫描注解
        -->
        <!--但是由于Servlet的生命周期,只有当请求来时,才会进行实例创建
        才可进行配置文件扫描,这是非常不好的,因为可能使得请求的第一次会慢(倒霉蛋的第一次碰到了)
        有什么办法可以在应用启动时,进行扫描呢,如启动时,就完成servlet的初始化操作,使用如下操作-->
        <load-on-startup>2</load-on-startup>
        <!--load-on-startup元素标记容器是否在启动的时候就加载这个servlet
        当值为0或者大于0时,表示容器在启动时就加载并初始化这个servlet
        当值小于0或者没有指定时,则表示容器在该Servlet被请求时,才会去加载(所以负数没有优先级,因为看请求的)
        且它的值必须是一个整数,表示servlet应该被载入的顺序
        正数的值越小(负数没有优先级),该Servlet的优先级就越高,应用启动时就优先加载
        当值相同的时候,容器就会自己选择优先加载,一般是读取顺序(从上到下)

        -->
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
        <!--若直接写/,而不指定路径,则匹配到所有的访问路径,匹配后就拦截(拦截后进行操作的,即找方法)
        但是不会匹配到像*.jsp这样的url 如/add会拦截,而/a.jsp不会,这里是静态资源拦截的主要原因,因为他只是不拦截.jsp,其他的都会拦截,包括静态资源,如css,js等等
        当然由于我们访问都是访问项目的,所以这里的/就相当于代表项目路径下全部路径的意思了
        而/*则都拦截,基本无视后缀名
	    使用上面的方式,就使得一个配置可以全部使用访问了,因为它会根据你的路径进行转发
         而不用编写多个配置了,就相当于将原来编写类,变成了对应方法
		即路径变成对应转发的参数的获取方式,由类变成方法,简便许多(多个类,变成对应方法,一个类可以操作了)
实际上也可以自己操作,这是他帮我们封装好了,只要判断路径对应方法返回值是对应的转发参数即可
        -->
    </servlet-mapping>
    <!--
    一般的,我们没有注解,普通的Servlet也是使用上面形式
    使得到达对应class来操作(一般都是实现对应的接口Servlet)
    然后根据访问方式,默认调用对应的get或者post方法

    当我们访问时,就会通过反射创建DispatcherServlet实例,而进行一系列操作
    -->
</web-app>
编写Controller类和视图页面并使用注解配置Controller类中业务方法的映射地址 :
package com.lagou.controller;

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

import javax.servlet.annotation.WebServlet;

/**
 *
 */
@Controller //IOC容器中的四个方式的一种,他们四个基本一样,为了语义即用这个,但也只是针对bean来说的,而MVC必须使用@Controller,否则不会起作用(并不报错,只是不起作用而已),即需要找关键类操作拦截结果,若不是这个@Controller注解,那么这里不会找,可以自己测试,因为判断时只判断这个类上面是否有这个注解,而没有判断其他的对应的同样作用的注解(这就是不同名称所造成的结果,虽然作用一样,但是名称不一样,所以再一定程度上,对应的四个是不同的(名称而言),也可以说是相同的(作用而言)),如@Service,当然,随着版本的变化,其他的可能也行了,但是我们为防止这种情况发生,所以还是最好一直使用@Controller,因为改变这个注解的而操作其他注解的可能几乎是不会出现的
public class UserController {

    @RequestMapping("quick")
    //一般我们使用Servlet来调用对应的get和post方法时,实际上只是使用对应方法来操作请求和响应信息
    //那么这个方法也是可以是其他的,当我们使用spring的DispatcherServlet(父类有Servlet)时
    //那么我们就可以使用他的方法,一般的普通的类,继承或者实现对应的Servlet时
    //使用xml或者注解会自动根据监听到的请求来调用对应的get和post方法
    //而DispatcherServlet虽然底层也是进行get和post方法的执行
    //但是他们都会调用同一个方法,即processRequest方法
    //如下
    /*
    protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws 
    ServletException, IOException {
        this.processRequest(request, response);
    }

    protected final void doPost(HttpServletRequest request, HttpServletResponse response) throws 
    ServletException, IOException {
        this.processRequest(request, response);
    }
     */
    //可以看到无论是get还是post最终的调用是一样的
    //而由于请求一般都是被监听的,就如xml和注解@WebServlet一样,对应请求到了,则初始化对应实例
    //那么DispatcherServlet由于(在这里)设置了/,那么所有的请求都会拦截,使得调用对应的get和post方法
    //而由于这两个方法一样,那就会根据这个方法,来得到请求信息
    //其中会进行IOC容器(只会在自己的容器里)的检查,也就是检查对应实例是否有@RequestMapping注解(对实例进行反射)
    //若有,看看对应值是否是对应请求的相应路径
    //若是,则调用注解所在方法(其他参数有默认也要满足,后面会说明),并获得值,根据值,再次进行对应操作
    //如这里进行转发,传递参数,转发到对应的页面路径

	//可以看到他帮我们执行IOC容器的实例方法了

    public String quick(){
        //业务逻辑
        System.out.println("哈哈哈");
        //视图跳转
        return "WEB-INF/pages/success.jsp";
        //返回值加上/开头,说明是当前项目开始,再多的/也是项目开始(因为是在项目内部操作的,自然不会离开项目)
    }
}

jsp:
<%--
  Created by IntelliJ IDEA.
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h3>嘿嘿嘿</h3>
</body>
</html>

配置SpringMVC核心文件spring-mvc.xml (名字当然不做要求) :
<beans xmlns="http://www.springframework.org/schema/beans"  
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:context="http://www.springframework.org/schema/context" 
    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/mvc   
    http://www.springframework.org/schema/mvc/spring-mvc.xsd  
    http://www.springframework.org/schema/context   
    http://www.springframework.org/schema/context/spring-context.xsd">
    
    <!--配置IOC注解扫描,基本spring的都会被扫描
所以@RequestMapping也会被扫描,因为spring-webmvc属于spring,不属于的其他,需要特定扫描,如事务支持的扫描
实际上除了Spring的扫描外,其他的一般叫做支持,也就是可以用,但方便的说,就叫扫描了,因为spring虽然是全部扫描
但不是全部可以使用的
-->
    <context:component-scan base-package="com.lagou"/>
    
</beans>
web工程执行流程 :

在这里插入图片描述

知识小结:
/*
SpringMVC是对MVC设计模式的一种实现,属于轻量级的WEB框架
SpringMVC的开发步骤:
 1.创建web项目,导入SpringMVC相关坐标
 2.配置SpringMVC前端控制器 DispathcerServlet
 3.编写Controller类和视图页面
 4.使用注解配置Controller类中业务方法的映射地址
 5.配置SpringMVC核心文件 spring-mvc.xml
*/
SpringMVC组件概述:
SpringMVC的执行流程:

在这里插入图片描述

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

可以理解:请求找路径并返回(1,2,3),给路径让其判断路径并返回且获得对应对象(4,5,6,7)
变成参数解析(如拼接)	进行转发(8,9),然后到jsp(10),最后渲染(11)
*/
SpringMVC组件解析:
/*
1. 前端控制器:DispatcherServlet
 用户请求到达前端控制器,它就相当于 MVC 模式中的 C(他代表C的根本处理,所以说成C也不为过),DispatcherServlet 是整个流程控制的
中心,由它调用其它组件处理用户的请求,DispatcherServlet 的存在降低了组件之间的耦合性。
 
2. 处理器映射器:HandlerMapping
 HandlerMapping 负责根据用户请求找到 Handler 即处理器,SpringMVC 提供了不同的映射器
实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。
 
3. 处理器适配器:HandlerAdapter
 通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型
的处理器进行执行。
 
4. 处理器:Handler【**开发者编写**】
 它就是我们开发中要编写的具体业务控制器。由 DispatcherServlet 把用户请求转发到 
Handler。由Handler 对具体的用户请求进行处理。
 
5. 视图解析器:ViewResolver
 View Resolver 负责将处理结果生成 View 视图,View Resolver 首先根据逻辑视图名解析成物
理视图名,即具体的页面地址,再生成 View 视图对象,最后对 View 进行渲染将处理结果通过页面展示给
用户。
 
6. 视图:View 【**开发者编写**】
 SpringMVC 框架提供了很多的 View 视图类型的支持,包括:jstlView、freemarkerView、
pdfView等。最常用的视图就是 jsp。一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展
示给用户,需要由程序员根据业务需求开发具体的页面

由上所述:
springmvc中的三大组件(重要的,在图中也是大框框)是:
处理器映射器:HandlerMapping
处理器适配器:HandlerAdapter
视图解析器:ViewResolver
若是四大组件:
那么在上述三大组件的基础上加上:
前端控制器:DispatcherServlet
*/
当我们不配置三大组件时,默认有对应配置(一般没有其他扩展),当然我们也可以配置对应三大组件
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       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/mvc
         http://www.springframework.org/schema/mvc/spring-mvc.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context.xsd">

    <!--配置注解扫描-->
    <context:component-scan base-package="com.lagou"/>

    <!--处理器映射器-处理器适配器-->
<mvc:annotation-driven></mvc:annotation-driven><!--可以省略不写(因为有默认,写上的话,通常会更好,即操作最合适的),但是最好写上,因为有些版本可能不能省略,且他可以操作对应的json-->
     <!--等同于配置了上面两个组件,且进行了功能的增强:如支持json的读写
	通常为后面的@RequestBody注解操作使用的json做支持(jackson包),下一章会说明该包
    所以需要用到该json时,通常添加这个标签(注意,如果对应的注解操作的,单纯就是字符串,说明没有进行任何处理,那么是否支持都可以,否则,必须需要这个,否则报错)
	其他的json的api基本不需要(实际上是基本操作不了,即不会使得起作用)
    -->

    <!--视图解析器:ViewResolver-->
    <bean id="viewResolver" 
          class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--在变成参数的时候,会进行拼接,prefix前面的拼接,suffix后面的拼接,默认是空的,即""
        使用这个就会使得我们可以省略返回值时的重复路径了
        若返回值是success,我们也叫做逻辑视图名,那么就会解析成具体的物理文件地址
        如WEB-INF/pages/success.jsp,success被WEB-INF/pages/和.jsp拼接了
        -->
        <property name="prefix" value="WEB-INF/pages/"></property>
        <property name="suffix" value=".jsp"></property>
        <!--其中WEB-INF/pages/不是/WEB-INF/pages/,所以就会认为是一个拼接路径
那么在使用一级目录时,会将这个拼接好(可能会默认加上对应的/,但一般不会),然后将拼接的一级目录的结果当作参数进行转发
若是/WEB-INF/pages/时,那么就不会将一级目录进行拼接,即始终认为是项目路径下
所以当你设置我这个时,一般再使用一级目录时,需要创建对应的文件夹,也是可以加上/的
即前面加/进行判断,使得是返回值是否拼接的主要因素(写/不拼接)

但是这个视图解析器最终操作的是返回值的那个变量,即进行/的判断也是那个变量,所以当不使用这个视图解析器时
返回值的/和不加/也是一样的效果
-->
    </bean>
</beans>
具体方法:
  @RequestMapping("quick1")
    public String quick2(){
        //业务逻辑
        System.out.println("哈哈哈");
        //视图跳转,当配置视图解析器时,会进行拼接的,所以就不能写全路径了,使得可能找不到资源
        return "a";
    }

    @RequestMapping("quick2") //不能有相同的对应值,会检查的,相同就报错
    public String quick3(){
        //业务逻辑
        System.out.println("哈哈哈");
        //视图跳转
        return "b";
    }
SpringMVC注解解析:
@Controller:
SpringMVC基于Spring容器,所以在进行SpringMVC操作时,需要将Controller存储到Spring容器中
如果使用@Controller注解标注的话,就需要使用:
<!--配置注解扫描-->
<context:component-scan base-package="com.lagou.controller"/>
<!--之所以指定具体包路径,是防止SpringMVC在整合Spring时,双方都扫描创建容器,使得容器多出一份
所以为了空间,可以不需要全部扫描,而去扫描具体的路径
-->
@RequestMapping:
/*
作用:
 用于建立请求 URL 和处理请求方法之间的对应关系
 
位置:
 1.类上:请求URL的第一级访问目录,此处不写的话,就相当于应用的根目录,写的话一般需要以/开头
 若不写则默认加上/(有就不会加上,且没有指定手动加上的一般都会是默认加上),因为无论怎么都是代表着全局路径
 虽然浏览器会加上,但那是浏览器默认加上的(默认访问默认资源,如index.jsp或者index.html,html优先访问)
 而不是服务器的,获得路径是项目没有/的,所以需要/
 因为服务器路径必须完全(这就是/的默认添加或者需要手动添加由来)
 如:
 @RequestMapping("/user") //一级目录 
 基本上服务器的路径,一般都是从当前目录开始的,也就是请求的那个当前目录(加/除外)
 public class UserController {}
 它出现的目的是为了使我们的URL可以按照模块化管理:
 用户模块
 /user/add
 /user/update
 /user/delete
 ...
 账户模块
 /account/add
 /account/update
 /account/delete
  一般的由于浏览器会将对应的/和\都看成/
 这个要注意,具体来说,实际上是后端的服务器的操作,而不是浏览器的操作
 浏览器并不会真正的变成/,但通常情况下,因为后端服务器的缘故,所以说是浏览器的作用也是可以的(虽然并不是)
 那么多写/或者\基本是没事的,但在程序里一般不建议这样写
 而当路径在项目路径下时,一般都会将/或者\变成/,中间的/和\也会,注解和xml的形式基本除外,基本只能是/
 因为他们两个是会将\当成对应的值,而不是路径(因为他们与浏览器路径进行比较的)
 \在程序里需要转义,即要写\\
 之所以springmvc与其他不同,主要是因为检查
 其中一级目录和其他级别目录以及注解或者xml的监听那里一般后面最好不要加上/,因为加上就需要对应访问路径
 而前面则会默认加上/(有/除外)
 而加上/*就代表这个路径下所有,上面三个路径(一级,其他级别,注解或者xml)都符合
 前面的一级和其他级别实际上就是注解的操作
 可以发现,资源就是路径的操作
 总结:xml的/代表项目下,如项目名/,其他如转发和重定向,代表最后的路径变化(无论你怎么操作层级,因为反正是/或者\的寻找),如a/b
 那么当转发和重定向到c时,就算a/c,但重定向若在前面加上/,代表端口开始,且若是全部的路径(如http://)
 则直接过去,而不是最后路径变化,而转发若在前面加上/,则代表项目路径开始,其他都是最后路径的变化
 而其他的使用/也基本与重定向一样
 如action或者超链接(或者类似的)等等,不使用时,一般就是相对路径了,根据最后的路径变化的
 两个或者两个以上,则是http://开始了
 转发特殊一点,因为他是在服务器里操作的,所以/或者//基本只表示项目路径
 而全部的不会当成全部路径,也只是一个最后路径的变化,即除了加上/,其他的都是最后路径的变化,之后无论重定向还是转发是否加/都无意义了,因为没有再次的下一级了
 
 那么由上知道,只要使用/,那么一定是正确的,所以我们通常使用/来操作
 
 2.方法上:请求URL的第二级访问目录,和一级目录组成一个完整的 URL 路径
属性:
 1.value:用于指定请求的URL,它和path属性的作用是一样的
 一般只有一个参数且没有指定属性名称时一般默认是value的值,如上面的路径
 2.method:用来限定请求的方式
 3.params:用来限定请求参数的条件
 例如:params={"accountName"} 表示请求参数中必须有accountName,对应值不做考虑
      params={"money!=100"} 表示请求参数中money值不能是100
       params={"money=100"} 表示请求参数中money值必须是100
       当扫描注解时
       发现有!=,则进行!"100".equals(对应值)判断
       发现有=,则进行"100".equals(对应值)判断
       对应值变成字符串进行判断
       不是操作他们本身,是进行对应判断
       将这两个左右边拆分,进行请求里键值对的取值与后面进行上述比较
       当然,没有对应值,就是null,即出现!"100".equals(null)或者"100".equals(null)
       判断返回值是否是false,若是则报错
*/
代码解释:
    //path:作用与value一样,同样是设置方法映射地址,即扫描注解被读取的数据操作一样
    //method:用来限定请求的方式,如RequestMethod.POST
    //使得只能以post的请求方式访问该方法,其他请求方式会报错
    //之所以这样,前面说过,get和post都调用同一个方法,其中传递了对应的请求和响应相关参数
    //通过这些参数可以得到请求方式,当扫描到这里进行数据的读取时
    //会判断method数据的请求方式值与请求参数的请求方式值是否一致
    //若不一致,则报错,当然这个只是限定约束,若不写,则没有限定约束,即post和get都可以
    //params:用来限定请求参数的条件,默认为null,即不是"",即可以不带对应参数,当然也可以带,而""不能添加
    //因为""添加后,浏览器没有直接可以加上""的参数(可以加上,但是java中没有""这个变量,所以自然会报错的,虽然有语法的分析使得直接出现错误的页面),所以一般访问不了,因为浏览器?后面的""不代表是数据""
    //而就是"",所以若要为空,则什么都不写就可以
    //params={"accountName"} 表示请求参数(不是参数值)中必须有accountName,对应值不做考虑
    //当然了,对于注解来说,若数组属性值是单个,则可以省略{},若是多个,则必须加上{}
    //当上述所有条件满足后,那么就会执行该方法
    //一般只要写了参数,那么值就默认为"",而不写则会得到null(如对应得到参数名方法)
    @RequestMapping(path = "/quick",method = RequestMethod.GET,params = {"accountName"})
知识小结:
/*
SpringMVC的三大组件
 处理器映射器:HandlerMapping
 处理器适配器:HandlerAdapter
 视图解析器:View Resolver
 
开发者编写
 处理器:Handler
 视图:View
*/
SpringMVC的请求:
请求参数类型介绍 :
客户端请求参数的格式是: name=value&name=value…… 获得的对应值一般都是字符串(数组也是)
所以有时候服务器要获取请求的参数的时候要进行类型转换,有时还需要进行数据的封装
SpringMVC可以接收如下类型的参数:
基本类型参数
对象类型参数
数组类型参数 如表单的复选框(多选框)提交
一般也有对应的方法来获得,如request.getParameterValues()方法,指定参数名称,返回一个字符串数组,包含对应值
也可以在URL后面加上多个相同参数名称,使得变成数组
若加上多个相同参数名称(可以叫参数)而后台并没有使用上面的方法来获得全部参数值
那么使用request.getParameter()方法,指定参数名称,就默认是获得第一个参数名称的值,即最左边的参数值
集合类型参数
获取基本类型参数 :
Controller中的业务方法的参数名称要与请求参数的name一致,参数值会自动映射匹配
并且能自动做类型转换;自动的类型转换是指从String向其他类型的转换
<%--
  Created by IntelliJ IDEA.
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
${pageContext.request.contextPath}
<%--
${pageContext.request.contextPath}代表当前项目路径,我这里是/springmvc_quickstart  参数
--%>
<a href="${pageContext.request.contextPath}/user/simpleParam?id=1&username=好好">
    <%--注意:若好好变成'好好'这是一个整体,也就是说,后台获得的也是'好好'--%>
    基本参数类型
</a>
    <!--一般来说,在前端操作/或者//,前者是到端口,后者都去除,相当于重定向(虽然转发特殊,即无论/还是//,始终是在项目名下)-->
</body>
</html>

/*
    获取基本类型请求参数
     */
    @RequestMapping("simpleParam") //也默认加上/,有/则不加
    public String simpleParam(Integer id,String username) { //默认情况下,得到的都是字符串,所以后续才会进行转换
        //当我们通过实例进行反射时,在判断注解信息后,调用方法前,会对对应方法参数进行判断
        //若请求(被当成参数过来的)的对应参数名称
        //有与这个方法的对应参数名称有一样的(必须一样,没有拼接的操作,否则不赋值)
        //就与Spring中的构造注入一样名称必须一致,因为一般获取参数名称是需要特定操作的
        //所以一般进行参数操作时,反射一般不会进行拼接对应操作(如字母的大小写操作等等)
        //可以循环名称的,如request.getParameterNames()方法
        //则将该参数名称的值当成参数
        //会判断参数对应类型,使得可能会自动进行类型转换,如判断到了Integer时
        //将String转换成Integer,对应的null和""都会使得Integer是null,或者默认值(类来说)
        //将String转换成int,这里可能会出现错误,如null和""不能使得转换成功,不会变成默认值0
        //因为0是很危险的,一般如数据库里,字段就没有0,当然这只是规定而已
        //类型只是标识,这个要理解,因为都是二进制,标识使得有约束
        //并执行方法,参数值获取如对应的获得值方法

        System.out.println(id);
        System.out.println(username);

        return "success";
    }
注意:项目中的WEB-INF的资源浏览器默认是不可直接访问的
一般需要转发进行跳转,重定向不可以,因为重定向实际上也是浏览器进行访问,而不是服务器
而转发是服务器的操作,所以可以(主要原因是,浏览器发送时,服务器第一次接收这个,会有标识,比如某个变量设置为1,那么判断这个为1时,不能访问安全文件里面的数据,当然浏览器访问时,他会变成2,那么我们操作时自然可以选择转发来访问安全文件里面的数据了,浏览器每次访问,这个变量初始化为1,访问后,这个值变成2,这样就使得只有转发可以操作安全文件了),即WEB-INF这个文件夹也通常叫做安全文件夹
且在windows中,若创建了某个文件夹,那么在创建其他文件夹时
文件名称不能是创建好的文件名称(忽略大小写的,也就是说a和A是一样的),linux不忽略大小写,a和A不一样
但他们都不能创建同名(这是必定的)
所以当你创建一个WEB-INf文件夹是不可以的,因为有了WEB-INF文件夹,当然在项目了,只有WEB-INF是安全文件夹
所以创建其他文件夹时,是可以访问其他文件夹的资源的
获取对象类型参数 :
Controller中的业务方法参数的POJO属性名与请求参数的name一致,参数值会自动映射匹配
  /*
   获取对象类型请求参数
    */
    @RequestMapping("pojoParam") //也默认加上/
    public String pojoParam(User user,Integer id,String username) {

        //在这里注意一下:前面可能说过,不同的包相同的类的结合,实际上是反射时进行判断类的所在包
        //所以虽然是一样的类,但包一定不同,根据导入来判断
        //当对象和参数一起时,都会进行操作,因为对应的值并不会在赋值后没有
        //而使用对象时(判断到对象,反射可以得到包)
        //然后根据对应的set方法(对应名称,无视全部大小写(如果没有set,那么不会操作,当然,一般情况下,框架会尽量的避免无视全部大小写,这是防止一些测试者,使用相似的来进行处理,所以框架为了省事,通常只会操作自身首字母大写或者自身已经是大写,现在的策略方式,现在一般都是这样的(所以看到无视全部大小写,他可能也没有问题,只是对方源码提供者可能改变了而已,因为之前是没有的),以前可能还是无视全部大小写的,随着时间的推移,这种无视会越来越少,我们也需要越来越规范了,当然时候规范可能是不会去除无视的,这可能是因为对方也是如此,比如数据库中字段一直为小写,那么在mybatis中进行对应时,是可以无视大小写的(他的column,具体可以看62章博客)),然后将得到的值当成参数),来操作对应的变量
        //而后面的参数也会出现值
        //注意:使用post方式,会出现中文乱码,而之所以get请求没有
        //是因为在tomcat的高版本下已经解决了对应乱码问题
        //如tomcat8.5及其以上(可能是8及其以上),现在我们回顾前面使用Servlet时解决的乱码问题
        //是因为我们设置了对应编码,而不是使用默认编码读取和写入,get请求则设置了中文对应编码,使得没有乱码
        //post一般没有,后面会给出解决方式
        //一般请求方式和响应方式一般都不可以操作中文,而jsp一般都只设置响应请求编码,因为他并不使用请求信息
        //所以只操作了响应
        //如<%@ page contentType="text/html;charset=UTF-8" language="java" %>

        System.out.println(user);
        System.out.println(id);
        System.out.println(username); //如果对应的有多个相同的名称,那么进行合并,比如username=1&username=2,那么结果就是"1,2"字符串
        //在mvc中,几乎只要是相同的,或者说位置相同,就会操作合并(也就是数组的操作,在给单独的一个字符串而不是数组接收时,就是合并,否则就是操作数组的保存)

 //如果username是Integer呢,存在多个怎么办,答:只拿取第一个,也就是说,由于他不是字符串,所以只能存放一个,且只有第一个,那么这个时候,就算是name=1&name=a,那么得到1,如果第一个的1是a,那么自然报错,他转换不了的
 
//当然,由于没有任何的数据处理时,是传递默认值的,但是这个默认值通常是null,所以对于int类型来说,如果不传反而会报错


        return "success";
    }
<!--请求方式是post方式,对象-->
<form action="${pageContext.request.contextPath}/user/pojoParam" method="post">
    编号:<input type="text" name="id" value="1"/>
    用户名:<input type="text" name="username" value="好好"/>
    <input type="submit" value="对象类型参数"/>
</form>
中文乱码过滤器 :
当post请求时,数据会出现乱码,我们可以设置一个过滤器来进行编码的过滤
 <!--配置中文过滤器-->
    <filter>
        <filter-name>CharacterEncodingFilter</filter-name>
        <!--springmvc自带的-->
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
       <!--创建实例后,会使用下面的编码对对应的响应或者请求进行设置编码(这样,如果jsp没有操作,那么他由于设置可能会解决中文乱码,但是由于jsp基本上与输出是最近的,所以以jsp的编码是为主的)-->
        <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>

<!--实际上过滤器就是设置编码,这样我们就不用再所有的servlet里面进行设置了-->

获取数组类型参数 :
Controller中的业务方法数组名称与请求参数的name一致,参数值会自动映射匹配
  /*
   获取数组类型请求参数
    */
    @RequestMapping("arrayParam") //也默认加上/
    public String arrayParam(Integer[] id) {
        //前面说过,单个参数使用request.getParameter()来赋值
        //多个参数使用request.getParameterValues()来获得全部值
        //当判断这个类型是数组类型时,就使用这个方法,将值给这个数组(数值自动进行转换,从下标0开始)
        //当然了也会判断变量名称是否有对应参数名称一致的,有就会进行上述操作


        System.out.println(Arrays.toString(id));

        return "success";
    }
<!--请求方式是post方式 数组-->
<form action="${pageContext.request.contextPath}/user/arrayParam" method="post">
    编号:<input type="checkbox" name="id" value="1"/>1
    <input type="checkbox" name="id" value="2"/>2 
    <input type="checkbox" name="id" value="3"/>3
    <input type="checkbox" name="id" value="4"/>4
    <%--都是值,所以type也可以是其他的,如text--%>

    <input type="submit" value="数组类型参数"/>
</form>
获取集合(复杂)类型参数 :
获得集合参数时,要将集合参数包装到一个POJO中才可以
  /*
   获取复杂类型请求参数(包含集合,基本类型,对象,数组)
   对象自然是调用对应的set操作,但引用类型基本都是最后操作数据的,数组除外(与基本类型一样,直接赋值再当参数)
   而不是先当参数,最后赋值
    */
@RequestMapping("/queryParam")
    public String queryParam(QueryVo queryVo) {
        //当有对应类里面存放了类或者集合时,需要在前面有指定名称的对象名称
        //如setUser,那么前端名称就要有user,如user.id(全部忽略大小写)
        //当看到user时,会创建一个空参user对象传入参数中(没有空参则报错)
        //最后在通过id对空参对象进行set操作赋值
        //所以可以在setUser里打印user时对应值是null的
        //因为是最后将id进行赋值,那么由于地址一样,所以对应user值也变化了
        //而当是集合时,也要对应名称,如userList[0].id,[0]被识别,且代表最高位置
        //如[2],那么前面就有[0],[1],先行判断,然后创建一个空的集合
        //若有具体识别则赋值,否则对应对象的值就是默认值(创建对象赋值的)
        //若[]里面的值一样,那么就会字符串合并(这里的合并可以自行测试),而不是覆盖,若是整型则取前面的参数
        //若不写[],但写了[]里面必须有值,否则报错
        //那么不会对任何识别进行赋值(即对应位置的user的set操作),与上面的对象一样
        //setUserList也是一个空的集合,最后进行赋值(创建User对象,也进行了set执行)
        //map集合与其他集合不同的是[]代表key,即[]里面不写时,就代表了""的key,当然也是会合并的(这里的合并需要注意,具体测试就知道了)
        //即没有最大位置的区分,其他的与上面集合一样,也是先创建集合,然后通过参数使用set赋值
        //有反射操作,自然可以set赋值(拼接的,忽略大小写),map也是setUserMap的赋值
        //然后操作小的User赋值,再然后放入集合中,地址一样,所以最终值也一样
        //由于可以获得类型的对应包或者创建泛型的实例使得可以得到对应的实例,而进行User小的操作放入
        //操作完后,上面的queryVo就有对应值了


        System.out.println(queryVo);
        return "success";
    }
<!--集合-->
<form action="${pageContext.request.contextPath}/user/queryParam" method="post">
     搜索关键字:
     <input type="text" name="keyword"> <br>
     user对象:
     <input type="text" name="user.id" placeholder="编号">
     <input type="text" name="user.username" placeholder="姓名"><br>
     list集合<br>
     第一个元素:
     <input type="text" name="userList[].id" placeholder="编号"><!--可以写0或者不写,当然有些版本可能必须要写0了-->
     <input type="text" name="userList[].username" placeholder="姓名"><br>
     第二个元素:
     <input type="text" name="userList[1].id" placeholder="编号">
     <input type="text" name="userList[1].username" placeholder="姓名"><br>
     map集合<br>
     第一个元素:
     <input type="text" name="userMap['u1'].id" placeholder="编号">
     <input type="text" name="userMap['u1'].username" placeholder="姓名"><br>
     第二个元素:
     <input type="text" name="userMap['u1'].id" placeholder="编号">
     <input type="text" name="userMap['u1'].username" placeholder="姓名"><br>
     <input type="submit" value="复杂类型">
</form>
当然,数组和类可以结合,了解即可
自定义类型转换器:
SpringMVC 默认已经提供了一些常用的类型转换器(一般并不只是包括Date,具体可以百度),例如:客户端提交的字符串转换成int型进行参数设置
而对应的日期格式类型转换要求为:yyyy/MM/dd或者yyyy/MM/dd HH:mm:ss
注意:字符串变成Date类型,基本都需要被类型格式进行转换,因为String类型不是Date类型
springmvc会先检查字符串
然后交给对应的类似于SimpleDateFormat format = new SimpleDateFormat(“yyyy-MM-dd”)的检查
或者SimpleDateFormat format = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”)的检查,通过先检查字符串而确定去哪个检查
所以可以有两个,而检查,只要满足就可以,如yyyy-MM-dd,可以是"2019/1/1",或者是"2019/1/1 1:1:1"
只要字符串前面的刚好满足,那么后面的就不会进行操作了,而不符合检查的话就会报错
最后,报错未必不会跳转页面,比如捕获的存在(try-catch),使得后面的代码也会进行,对于特有的行为
其中/和:都是一个分割符,所以我们编写时,要按照他的形式来,如1/1,或者1:1
其中字母表示对应位置的数,也可以看成是检查你编写的字符串的格式,所以也可以说Date也是一个字符串,只是有格式检查
当不符合时,就会报错
且写的参数那里也要是英文的,否则都会报错
SpringMVC提供了自定义类型转换器方便开发者自定义处理

    /*
    获取日期类型参数(自定义类型转换器)
     */
    @RequestMapping("data")
    public String data(Date date){
        //再赋值前判断是Date类型,然后看看字符串是否有/分割的(内置的操作),不是则报错
        //即一般如2012/12/10这样的形式
        //则将这些变成对应字符串,然后转换成Date类型,但对应的数字是不能越springmvc日期的界的
        //之所以特定数码springmvc日期的界,是因为在springmvc中月在1-69是可以被操作的
        //日则在0-69,超过的月和日,会被分解(月开始变年,然后日开始变月),即变成年或者月
        //时分秒也是上面的操作,顺序也是一样的

        System.out.println(date);
        return "success";
    }
<!--演示自定义类型转换器 :错误的产生 必须是2010/10/22的这样的类型-->
<form action="${pageContext.request.contextPath}/user/data" method="post">

    生日:<input type="text" name="date">
   <input type="submit" value="自定义类型转换器">
</form>
上面是使用默认的操作
上面是使用默认的操作(当然了,可能还存在其他的默认,比如LocalDateTime)
接下来我们自定义一个类型转换器
 <!--conversion-service使得springmvc默认的日期设置,使用下面的配置-->
<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>

 <!--自定义类型转换器配置-->
    <bean id="conversionService" 
          class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <bean class="com.lagou.converter.DateConverter"/>
                <!--
                相当于这个实例放入对应集合(可以这样写的)相当于ref的标签,只是这里是自己定义的,只是这里是自己定义的
                当不写name和id时,这个实例默认key是全限定名,也可以说是class的值了(name和id基本一样)
                也就是com.lagou.converter.DateConverter
                -->
            </set>
        </property>
    </bean>
<!--
这样使得我们使用这个时,即conversion-service="conversionService",会调用对应方法,即下面的convert方法
实现了Converter接口才会调用对应方法(实际上这个接口只有这一个方法),所以必须要实现这个接口,否则报错
参数是前端传递过来的参数(request),返回值给Date类型,然后给data方法的参数date
但是这时虽然不可以使用/了,但并不会结束后续运行,可以知道,这是一个新的线程
开启的conversion-service="conversionService"使得原来的等待得到对应Date
即下面的方法返回值(这个返回值基本只能是Date,否则报错)

注意:一般特殊的变化,如@ResponseBody需要他,否则可能会出现500错误,转换失败,也就是说,他不只是操作Date哦,当然,只要存在即可,并非一定要操作上面的</property>,还要记得<beans里面的xml的信息位置也要加哦,否则可能不会认为他存在,即位置存在,虽然除了空间(一般指命名),也包括约束,按道理说,约束可以不加,但是一般他会判断(底层,使用了他就会判断他,要不然为什么基本都是一些固定写法),如果不加就会报错,所以都需要加上

-->
package com.lagou.converter;


import org.springframework.core.convert.converter.Converter;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 *
 */
//Converter<String, Date>接收String转成Date
public class DateConverter implements Converter<String, Date> {
    //dateStr就是表单传递过来的请求参数
  public Date convert(String dateStr) {
  //使用这个后,springmvc只会操作这个方法获得对应Date类型了,而不会使用默认的两个转换(方法)进行操作了
    //将日期字符串转换成日期对象 返回
    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
    Date date = null;
    try {
      date = format.parse(dateStr); 
        //虽然可以操作-了,但/也不可操作了(这里会报错,但并没有停止运行,因为try)
        //但还是会输出,只是为null而已,但是-使用的多一点(可以在catch中继续处理其他的格式,如/)
       
       
    } catch (Exception e) {
      e.printStackTrace();
    }
    return date;
  }
}


 /*
    获取日期类型参数(自定义类型转换器)
     */
    @RequestMapping("data")
    public String data(Date date){
      
        System.out.println(date);
        return "success";
    }
相关注解:
@RequestParam
当请求的参数name名称与Controller的业务方法参数名称不一致时,就需要通过@RequestParam注解显示的绑定
 /*
  @RequestParam() 注解
   defaultValue 设置参数默认值
  name 匹配页面传递参数的名称
  那么就按照这个设置的进行判断赋值了,即就算有原来的传递也不会进行赋值,如下面传递pageNum
  这样使得将name对应的名称值给这个参数,若不设置,则需要名称一样才可赋值(这是基本一定的)
   required 设置是否必须传递参数(即给pageNum赋值,设置了name,则需要name的)
   默认值为true,即没有传递参数则会报错
   如果设置了值(false),值自动改为false

   可以看出在判断注解信息后,调用方法前,对参数判断时,会先看对应参数是否有对应注解
   当找到@RequestParam()注解时,获得对应参数值
   name值使得不会根据参数名称进行赋值了,而是根据name值进行赋值了
   即getParameter()的参数是name的,而不是参数名称,而defaultValue的值,在没有被赋值时进行赋值
   实际上最后赋值时都是给一个变量,由这个变量来赋值,而当这个变量为null时
   若required是true则报错,然后在判断是不是""
   一般有参数没值的,如a=,或者a,那么getParameter方法的结果就是""(前提是字符串)
   若是则赋值null(前提是包装类等,或者不支持""的,也就是不是字符串的)
   否则赋值对应值(自动类型转换,要注意不要有其他不符合Integer的数,否则当然会报错)
   

*/
//一般来说,他只能操作参数,而不是对象,否则可能会报错(当然,可能会操作对应的set方法来赋值,具体看测试吧)
    @RequestMapping("/findByPage")
    public String findByPage(@RequestParam(name = "pageNo",defaultValue = "1",required = true)
                                     Integer pageNum, @RequestParam(defaultValue = "5") Integer 
                             pageSize) {
        System.out.println(pageNum);
        System.out.println(pageSize);
        return "success";
    }

<a href="${pageContext.request.contextPath}/user/findByPage?pageNo=2">
    分页查询
</a>
@RequestHeader :
获取请求头的数据
 /*
    获得请求头信息

     */
    //得到请求头中cookie的信息
    //为什么可以得到,在前面说过,get和post最终结果一致,且传递了request参数,那么对应cookie也是可以得到的
    //将cookie的信息给cookie这个参数,所以值都拼接在String里,包括名称和对应值,=连接,;分割
    @RequestMapping("cookie")
    public String requestHead(@RequestHeader("cookie")String cookie) {
        System.out.println(cookie);
        return "success";
    }
@CookieValue
获取cookie中的数据
/*
    也是获得cookie,但这是直接使用cookie得到他里面的值,而不是请求头获得cookie全部数据
     */
    //得到cookie的名称是JSESSIONID的对应值,给这个参数
    @RequestMapping("cookieValue")
    public String cookieValue(@CookieValue("JSESSIONID")String jesessionid){
        System.out.println(jesessionid);
        return "success";
    }

获取Servlet相关API :
SpringMVC支持使用原始ServletAPI对象作为控制器方法的参数进行注入,常用的对象如下:
/*
    原始servletAPI获取
     */
    @RequestMapping("/servletAPI")
    //之前的基本都是判断参数名称,然后根据类型进行自动转换使得赋值
    //而当出现servlet的一些基本的类型时,只会根据类型进行赋值(赋值的是servlet的,而不是传递的参数),一般这个操作会先处理,若没有,再处理参数的操作
	//且不会看参数名称,其中传递的参数不会参与到对应servlet类型中,但会参与到不是对应的servlet类型中
    //所以可以在这里进行其他赋值
    public String servletAPI(HttpServletRequest request, HttpServletResponse
            response, HttpSession session, @RequestParam(name="request")Integer s ) {
        //因为参数不能是一样的,所以需要@RequestParam注解来表示使用赋值
          System.out.println(request);
          System.out.println(response);
          System.out.println(session);
          return "success";
    }

SpringMVC的响应:
SpringMVC响应方式介绍:
页面跳转:
返回字符串逻辑视图
void原始ServletAPI
ModelAndView
返回数据:
直接返回字符串数据
将对象或集合转为json返回(下个博客会演示)
返回字符串逻辑视图:
直接返回字符串:此种方式会将返回的字符串与视图解析器的前后缀拼接后跳转到指定页面
@RequestMapping("/returnString")
public String returnString() {
    return "success";
}
void原始ServletAPI :
我们可以通过request、response对象实现响应:
/*
    通过原始servletAPI进行页面跳转
     */
    @RequestMapping("returnVoid")
    //返回值是void(且有HttpServletResponse类型的参数),或者转发和重定向都不会经过视图解析器
    //实际上只会操作当前方法,其他的基本都不会经过
    //也就是说不会通过视图解析器的转发(状态判断)
    //而设置void(且有HttpServletResponse类型的参数)且没有转发和重定向
    //那么返回的就是一个空页面,即普通的servlet的运行完毕(响应一个空信息)
    //若没有HttpServletResponse类型的参数,就把@RequestMapping的值(一般是返回值)当成拼接对象去操作
    //若有HttpServletResponse类型的参数,就不会经过视图解析器,也就是说,当传递时,也就设置了对应的状态了
    //使得不会经过视图解析器
    public void returnVoid(HttpServletRequest request, HttpServletResponse
            response) throws ServletException, IOException {

        //借助request对象完成请求转发一次请求
        request.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(request,response);

        //response.sendRedirect(request.getContextPath() + "/index.jsp");
    }
转发和重定向:
企业开发我们一般使用返回字符串逻辑视图实现页面的跳转,这种方式其实就是请求转发
我们也可以写成:forward转发
如果用了forward:则路径必须写成实际视图url,不能写逻辑视图。它相当于:
request.getRequestDispatcher("url").forward(request,response)
使用请求转发,既可以转发到jsp,也可以转发到其他的控制器方法
/*
    演示forward关键字进行请求转发
     */
    @RequestMapping("forward")
    public String forward(Model model){
        //springmvc会自动进行注入(实际上是进行传参),就如上面的servlet的类型一样的操作
        //这样可以设置一些属于这个操作的值了
        model.addAttribute("name","哈哈哈");
        //键值对形式,由于本质上是在request域中存储,那么就可以在jsp中使用ek(l)表达式来获取
        //而转发自然的也会将request转发过去,所以jsp在转译,编译后(访问时才会进行,启动服务器不会)
        //但若已经转译,编译后了,则直接使用(前提文件没有被改变,改变的话也要重新转译,编译)
        //变成servlet时是可以得到request的值的
        return "forward:/WEB-INF/pages/success.jsp";
        //一般的,返回的字符串会被视图解析器进行拼接,但当被找到forward:这个关键字时
        //就会进行转发操作
        //也就是直接request.getRequestDispatcher("返回的forward:后面的数据").forward(request,response)
        //这样不会经过拼接了(状态判断),从而直接转发
    }
<%--
  Created by IntelliJ IDEA.
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h3>嘿嘿嘿</h3>
${name}
</body>
</html>

从这里到上面的网页,基本都是jsp变化的,当然对应的html也可以,但有些el表达式需要jsp,且设置了jsp不拦截
Redirect重定向(/会表示加在后面,全部路径则会当作全部路径)
我们可以不写虚拟目录,springMVC框架会自动拼接,并且将Model中的数据拼接到url地址上
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
index...
${name}
<%--空的null不会显示出来,即返回"",而""在servlet通过输出流到前端时,显示的就是空的,即什么都没添加--%>
</body>
</html>
/*
    演示redirect关键字进行请求转发
     */
    @RequestMapping("redirect")
    public String redirect(Model model){

        //由于底层也是request域,那么jsp使用不了el表达式来操作
        //因为使用重定向是不同的request,所以获取不到
        model.addAttribute("name","哈哈哈");
        //当然使用上面的并不是没有作用,会使得请求数据的url,加上上面的对应参数和值(重定向默认是get方式的)
        //如http://localhost:8080/springmvc_quickstart/index.jsp?name=哈哈哈,进行了拼接
        //这就是与request域的一个区别,即虽然model的addAttribute方法最终也是request的setAttribute方法
        //但多一个可以在重定向的重定向的url里添加参数的能力(代码多一点,则效率低一点)

        return "redirect:/index.jsp";

    }
ModelAndView(包括Model):
方式一 :
在Controller中方法创建并返回ModelAndView对象,并且设置视图名称
 /*
    modelAndView进行页面跳转:方式一
     */
    @RequestMapping("Model")
    public ModelAndView returnModelAndView(){
        /*
            model:模型,也就是存放了对应数据的地方,实际上注入的是ModelAndView的model
            view:返回的视图路径,一般就是前面说过的要拼接的路径
            实际上他们都是存在在ModelAndView对象里面,最后交给视图解析器,然后操作里面的数据
            中间有对应的传递,如request
            当单独使用model或者view时,model是被取出赋值的
            由于地址原因,那么ModelAndView的model的值也是一样的
            而view则直接会添加到对应的ModelAndView里面
            若你返回的就是ModelAndView,那么原来的ModelAndView被你返回的ModelAndView赋值
            而不会将放回的值进行操作放入,即你创建的赋值给原来的
         */
        ModelAndView modelAndView = new ModelAndView();

        //由上面注释可知:那么我们可以直接设置model,而不用自己取出model进行设置了
        //model设置实际上是设置request的设置,如request.setAttribute("name","哈哈哈");
        modelAndView.addObject("name","哈哈哈");

        //也可以设置返回的视图,而不用我们手动进行返回在放入modelAndView了
        //在交给视图解析器时,会进行view的拼接(前提没有设置void,转发或者重定向)
        //实际上设置void,会使得view为null,当视图解析器判断到是null,则不会进行拼接,转发
        //而转发和重定向设置后,由于状态变化,那么也不会进行拼接转发
        modelAndView.setViewName("success");

        return modelAndView;
    }
方式二:
在Controller中方法形参上直接声明ModelAndView,无需在方法中自己创建
在方法中直接使用该对象设置视图,同样可以跳转页面
/*
   modelAndView进行页面跳转:方式二
    */
    @RequestMapping("Modell")
    public ModelAndView returnModelAndViewl(ModelAndView modelAndView){
        //我们创建ModelAndView对象是会占资源的,为了不用频繁的创建
        //我们可以将servlet的ModelAndView进行传递过来(自动判断参数类型进行注入,servlet的参数)
        modelAndView.addObject("name","哈哈哈");


        modelAndView.setViewName("success");

        return modelAndView;
    }
直接返回字符串数据:
 /*
  直接返回数据
   */
    @RequestMapping("ret")
    public void ret(HttpServletResponse mm) throws IOException {

        mm.setContentType("text/html;charset=utf-8");
        PrintWriter writer = mm.getWriter();
        writer.println("哈哈哈");
        //提前设置了response的对应值,那么在最后响应时,就会取出这个值,放在响应报文里面
        //之所以这样,是因为request和response都是在一个方法里共用的
        //即get和post方法里对应方法的实际参数就是这两个参数

    }
@SessionAttributes :
如果在多个请求之间共用数据,则可以在控制器类上标注一个 @SessionAttributes,配置需要在session中存放的数据范围
Spring MVC将存放在model中对应的数据暂存到 HttpSession 中
注意:@SessionAttributes只能定义在类上
@Controller //IOC容器中的四个方式的一种,他们四个基本一样,为了语义即用这个
@RequestMapping("/user") //一级目录
@SessionAttributes("name") //使得 向request域(model)中存入key为name时,会同步到session中
//也就是说,这里实际上是model在使用addAttribute方法时,会检查有没有这个注解操作的信息
//注解操作的信息被读取时,一般由变量进行获得,那么就会判断这个变量值
//即若这个变量值是name,那么key是name的值则会操作session.setAttribute方法进行放入,且key也是name
//否则只会放入request域中去,即操作request.setAttribute方法进行放入
//也就是说,当读取这个注解时,创建的实例中会被这些注解操作
//注意:注解一般只会操作spring的对应实例
//那是因为在创建对应实例时,他们进行了操作,而你直接的创建对象,他们是没有进行操作的
public class UserController {
}
  /*
   演示SessionAttributes
    */
    @RequestMapping("re")
    public String re(Model model,HttpServletRequest request){
      return "success";
    }
注意:当model的addAttribute方法执行时,若你进行打印request.getAttribute对应方法,返回的null
之所以不是对应值,那是因为model的addAttribute方法只是先将值存放好,最后才进行放入的
就如前面的集合中set对应集合名称类似,最后才进行User赋值
之所以这样设置,是为了不让数据明显的直接设置成功,即我们基本不能直接操作数据,而是让框架帮我们操作数据
我们只是编写数据而已,并没有操作数据,这样的好处,就在于我们不用写具体的业务代码,而是直接指定数据去操作
springmvc帮我们封装好的具体业务代码了
知识小结:
/*
页面跳转采用返回字符串逻辑视图
 1.forward转发
 可以通过Model向request域中设置数据
 2.redirect重定向
 直接写资源路径即可,虚拟目录springMVC框架自动完成拼接
 
* 数据存储到request域中
 Model model
 model.addAttribute("username", "哈哈哈");

*/
静态资源访问的开启 :
当有静态资源需要加载时,比如jquery文件,通过谷歌开发者工具抓包发现,没有加载到jquery文件
原因是SpringMVC的前端控制器DispatcherServlet的url-pattern配置的是 /(缺省)

<%--
引入jquery-3.4.1.min.js,由于一些外部资源也是需要进行请求的,在设置了/(除了jsp不会拦截外)
那么基本都会被拦截(前端控制器DispatcherServlet的url-pattern配置拦截),使得请求就会受springmvc进行操作
然后就出现找这个名称的对应注解,也就是RequestMapping注解的值
当然没找到就会报错,并说明没有资源存在,即一般都是404
且一般服务器的报错,一般都会转发到错误页面,所以我们看到的页面实际上是服务器自带的对应错误页面
即这样的访问使得变成注解的对应判断(一般这里就会使得报错)并进行操作转发了
即使得对应资源并没有访问到(jsp动态资源)
js,css,img,html一般称之为静态资源
动态资源:当受到程序的影响时,可能对应资源数据不同
静态资源:当受到程序的影响时,资源数据基本固定
静态资源也会参与访问获取,所以就会被拦截,拦截一般不会拦截动态资源,因为一般设置/
--%>
<script src="${pageContext.request.contextPath}/js/jquery-3.4.1.min.js"></script>
<%--
    若这个资源是复制过来的,注意进行maven的clean和compile来进行重新编译(服务器基本不会再次操作这个编译)
    防止没有被加载(即路径加载),最好也把浏览器缓存也清除
    防止springmvc使得cookie中可能会默认存放对应资源文件的一些信息,使得你删除了且编译了,还是认为存在
    即需要刷新,因为普通的编译或者部署,可能没有对他重新加载(即路径加载)
    --%>
代表对所有的静态资源都进行处理操作,这样就不会执行Tomcat内置的DefaultServlet处理,我们可以通过以下两种
方式指定放行静态资源:
<!--
起始条件下,建议看完
    方式一:放行指定的静态资源,mapping:放行的映射路径,location:静态资源所在的目录
    一个*表示当前路径,两个*表示当前路径及其子路径,他们在对应名称里时,即不是单独的,那么基本会去找对应的值
	如j*,而由于文件中不可以有*,那么注定是找不到的
    当前端控制器读取这个配置文件后,若有对应mapping路径,则不会进行拦截
    而是去location里的路径里寻找对应的静态资源
    当然了真正的指定资源路径的,如src不要有对应的WEB-INF安全文件夹的对应位置(其他文件的WEB-INF没事)
    因为有了,则直接报错,因为在拦截和下面的配置之前

    对于下面的配置的/问题,location和mapping最前面的/可以加可以不加(不加默认加上/),且都只会匹配/后面的(无论/还是//都一般代表项目的路径开头)
    mapping是将值与当前路径上新添加的对应路径进行比较(也就是我们去请求的路径)
    看看是不是js开头的(这里是),注意mapping结尾有/和没/最终都会变成没/的
    且location必须的指定对应文件夹里面,即最后也必须需要加/
	使得请求路径的最后的文件,去这里面找(注意是最后),但是随着版本的改变,可能并不需要加上了,而不是在一个/下了,所以一般可以省略
    即请求a/b.js,那么就去指定的文件里找b.js
    
    所以这样的配置使得最终的路径是location指定的操作,只有页面上的最终路径在其中有即可
    如<script src="${pageContext.request.contextPath}/js/a.js"></script>
    上面的js和a.js中间有其他文件没有写,但实际上还是会到location路径里找a.js
	所以这样的src路径也就可以省略写了
    只要满足mapping即可
    -->
    <mvc:resources mapping="/js/**" location="/js/WEB-INF/"/>
    <mvc:resources mapping="/css/**" location="/css/"/>
    <mvc:resources mapping="/img/**" location="/img/"/>

<!--
被读取的注解文件,一般不会进行转义(一般只有java的直接字符串会进行转义)
也就是说这里的\就代表\,而不是使用\\代表\

如果上面有误差,可以看这里的例子
这里可以举例:
<mvc:resources mapping="/resources/**" location="classpath:/"/>

这也就可以访问资源文件夹里面的了,首先mapping匹配你的路径,由于存在开头的/,那么去项目/路径,那么若你访问http://localhost:8081/springmvc/resources/s.html(springmvc项目名称,即开头/的开头,这里要注意),由于是开头的/,即是resources/s.html,那么就是匹配了resources/**,即放行,放行后,就可以去对应的文件中找了,由于指定的文件为classpath:/,代表项目根目录,所以再该根目录里查找resources/s.html,正好是资源文件里面的s.html,那么将他的结果给你看,这就是全部的流程
当然,大多数mapping默认是在项目/下的,所以加不加好像并无所谓,所以这里删除开头/,照样可能找resources/s.html,而不是s.html,所以注意即可


当然,大多数/的省略只是绝对和相对路径的说明,所以我们最好使用/,在前面的情况http://localhost:8081/springmvc/resources/s.html如果出现省略还是一样的,大多数是因为请求就是resources/s.html,而非s.html,所以在起始条件下,上面的说明都是正确的,无论是否省略都是如此,但是不是在起始条件下就不是了,所以需要特别的注意,所以这里可以解释如下:
<mvc:resources mapping="/js/**" location="/js/WEB-INF/"/>中的mapping属性省略第一个斜杠/:如果省略了第一个斜杠,路径模式将被解释为相对路径,在这种情况下,js/**将被解释为相对于当前请求上下文路径的路径,例如,如果当前请求上下文路径为/myapp,那么js/**将解析为/myapp/js/**
<mvc:resources mapping="/js/**" location="/js/WEB-INF/"/>中的location属性省略第一个斜杠/:如果省略了第一个斜杠,路径将被解释为相对于当前请求上下文的相对路径,在这种情况下,js/WEB-INF/将被解释为相对于当前请求上下文路径的路径。同样,如果当前请求上下文路径为/myapp,那么js/WEB-INF/将解析为/myapp/js/WEB-INF/
省略第一个斜杠可能导致资源路径解析错误,因为最终的路径可能不是预期的路径,主要他是可变的,因此,为了确保路径的正确解析和映射,通常建议在<mvc:resources>标签的mapping属性和location属性中开头都明确地使用斜杠/,那么结尾的/呢,实际上结尾的可以加可以不加


实际上大多数配置中,路径只会分为相对和绝对的,特别的,开头加上斜杠大多数都会是绝对路径,且相对于项目来说,虽然有些加多了可能到起始浏览器(如重定向),所以需要这样说,对于项目里面的路径,大多数框架都只会分为相对和绝对(项目路径的绝对),这里了解即可

-->

方式二:
<!--
方式二:在springmvc配置文件中开启DefaultServlet处理静态资源
即基本上所有的静态资源都会处理,即都不会被拦截,而是使用servlet默认的处理方法(即本来的处理方式)
也就是直接去找对应资源
servlet处理资源就是默认的,没有对应拦截也是一样,只是资源被拦截了而已

使用这个相当于上面的全部配置了(实际上相当于项目路径下的所有静态文件,通常包括js,html,css,可能还有其他的,具体可以百度),但方式一优先于方式二,即同时存在且对应与方式一的操作时,使用方式一的操作,若方式一无法找到对应的静态资源,或者请求不匹配该路径时,那么就会自动使用方式二
-->
    <mvc:default-servlet-handler/><!--由于这个比较简单,所以建议使用这个方式,一般他是需要加上<mvc:annotation-driven></mvc:annotation-driven>的,否则可能会导致访问不了路径了(即可能使得url访问,明明是对的,但是找不到,具体可以自行测试)-->
<!--但是在某些时候(可能是一定的,具体可以百度),他可能并不能使得WEB-INF里面的资源被访问,所以如果需要,那么最好还是第一种-->

<!--
拦截是针对一开始的,如果是内部的,那么基本不会处理,所以前面可以是return "WEB-INF/pages/success.jsp";
-->

大多数情况我们都会使用< mvc:default-servlet-handler/>,因为比较简单,当然,有些时候可能需要前面的方式了,并且,前面对第一种方式的说明,可能随着时间的推移而有错误,因为该框架是会更新的,而该博客可能并不会更新,所以我们只需要知道大致操作就行了
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值