SpringMVC-01

SpringMVC-01

1、SpringMVC的概述

Spring MVC框架是一个开源的Java平台,为开发强大的基于JavaWeb应用程序提供全面的基础架构支持,并且使用起来非常简单容易。

Spring web MVC框架提供了MVC(模型 - 视图 - 控制器)架构,用于开发灵活和松散耦合的Web应用程序的组件。 MVC模式使应用程序的不同组件(输入逻辑,业务逻辑和UI逻辑)合理有效的分离,同时又有效的将各组件组合一起完成功能。

· 模型(Model) 封装了应用程序数据,通常它们将由POJO类组成。

· 视图(View) 负责渲染模型数据,一般来说它负责生成客户端浏览器可以解释HTML输出。

· 控制器(Controller) 负责处理用户请求并构建适当的模型,并将其传递给视图进行渲染。

2、SpringMVC的核心DispatcherServlet程序

SpringMVC的核心是一个前端控制器。它负责将收到的所有请求。分发给不同模块的Controller控制器,去接收业务处理

其实这个前端控制器,它就是一个Servlet程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<servlet>
<!--
配置SpringMVC的前端控制器
-->
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--
配置 SpringMVC 容器 的配置文件路径
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>-->
<!-- 配置在web工程启动的时候创建前端控制器 -->
<load-on-startup>1</load-on-startup>
</servlet>
<!--
给前端控制器配置访问地址
/ 表示所有请求
-->
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

3、SpringMVC的第一个Hello示例程序

3.1、SpringMVC——Hello world程序的步骤:

1 先创建一个web模块

2 导入需要的jar包

spring-aop-5.2.5.RELEASE.jar

spring-aspects-5.2.5.RELEASE.jar

spring-beans-5.2.5.RELEASE.jar

spring-context-5.2.5.RELEASE.jar

spring-core-5.2.5.RELEASE.jar

spring-expression-5.2.5.RELEASE.jar

spring-jcl-5.2.5.RELEASE.jar

spring-web-5.2.5.RELEASE.jar

spring-webmvc-5.2.5.RELEASE.jar

3 到web.xml中去配置SpringMVC的前端控制器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<servlet>
<!--
配置SpringMVC的前端控制器
-->
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--
配置 SpringMVC 容器
-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!-- 配置在web工程启动的时候创建前端控制器 -->
<load-on-startup>1</load-on-startup>
</servlet>
<!--
给前端控制器配置访问地址
/ 表示所有请求
-->
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
  1. 在src源码目录下,创建SpringMVC的容器配置文件 springmvc.xml
1
2
3
4
5
6
7
8
9
10
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

<!-- 配置 包扫描 -->
<context:component-scan base-package="com"></context:component-scan>

</beans>

5 编写Controller控制器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Controller
public class HelloController {

/**
* @RequestMapping 注解用于给请求映射到方法 <br/>
* value属性配置方法对象的请求地址(地址唯一) <br/>
* / 斜杠在服务器上表示地址为:http://ip:port/工程路径/ <br/>
* /hello 表示地址为: http://ip:port/工程路径/hello <br/>
* @return
*/
@RequestMapping(value = "/hello")
public String hello(){
System.out.println(" springmvc hello ... ");
/**
* / 斜杠在服务器上表示地址为:http://ip:port/工程路径/ 映射到代码的web目录 <br/>
* 返回值是跳转的路径 , 默认是请求转发
*/
return "/pages/ok.jsp";
}


}

3.2、SpringMVC的配置文件的另一种存放方式

SpringMVC也有一个Spring容器。也需要配置文件。

它有两种配置方法:

一:是在web.xml中通过给前端控制器 配置init-param实始化参数来进行配置:

1
2
3
4
5
6
7
<!--
配置 SpringMVC 容器 的配置文件路径
-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>

二:不需要到web.xml中进行配置,SpringMVC默认会到WEB-INF目录下去加载。

加载的文件名的格式是: (servlet-name的值)-servlet.xml

比如:

1
2
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

则加载的配置文件名为: dispatcher-servlet.xml

如果servlet-name标签的值是:

1
2
<servlet-name>abc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

则加载的配置文件名为: abc-servlet.xml

4、视图解析器

SpringMVC中有一个组件叫视图解析器。它的作用很直观,将Controller方法中返回的结果转换为视图对象

我们在开发中,有可能需要移动所有文件的位置,这时候有可能就需要到源代码去做批量的修改。

还有可能会更改,视图的具体实现技术。也需要批量到代码中去做批量的修改。

这个时候使用视图解析器可以很好的解决这个问题

有视图解析器之前,我们需要写上完整的跳转路径。

有视图解析器之后,我们只需要写上前缀和后缀之外的内容(文件名)

1
2
3
4
5
6
7
<!-- InternalResourceViewResolver配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/pages/user/" />
<!-- 后缀 -->
<property name="suffix" value=".jsp" />
</bean>

有了视图解析器之后,在Controller中返回的字符串,它会自动的和视图解析器中的前缀和后缀做字符串拼接操作。

然后再进行跳转。

5、@RequestMapping注解详解

@RequestMapping是给个方法配置一个访问地址。就比如web学习的Servlet程序,在web.xml中配置了访问地址之后,它们之间就有一个访问映射关系。

5.1、value属性(重点)

value的作用就是给方法设置一个访问地址,形成映射关系。

1
2
3
4
5
6
7
8
/**
* @RequestMapping 注解用于给请求映射到方法 <br/>
* value属性配置方法对象的请求地址(地址唯一) <br/>
* / 斜杠在服务器上表示地址为:http://ip:port/工程路径/ 映射到代码的web目录<br/>
* /hello 表示地址为: http://ip:port/工程路径/hello <br/>
* @return
*/
@RequestMapping(value = "/hello")

就表示此的访问地址为http://ip:port/工程路径/hello 就可以访问此方法。

5.2、params属性

params是要求此请求的参数进行匹配

params=”username” 表示 请求地址必须带有username参数

params=”username=abc” 表示 请求参数中必须要有username,而且值还必须是abc

params=”username!=abc” 表示 1、请求参数中不能有username参数。2、有username参数,但值不能等于abc

params=”!username” 表示 请求地址不能带有username参数

params= {“username!=abc”,”!password”} params可以有多个值,每个值之间是&&关系

以上条件表示要求:

( 请求地址中不能有username参数 || username参数值不能等于 abc && 不能有password参数 )

5.3、headers属性

headers属性在用法上和params属性一样。

只不过他们要求匹配的内容 不一样。

params是要求匹配请求参数

headers要求匹配请求头

1
2
3
4
5
6
7
8
9
/**
* headers要求匹配请求头 <br/>
* @return
*/
@RequestMapping(value="/headers",headers = "User-Agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36")
public String headers(){
System.out.println(" headers() ");
return "ok";
}

5.4、method属性(次重点)

method限制匹配请求的方式。

如果不写的请求下。不要求匹配。如果写了,必须严格匹配才允许访问。

1
2
3
4
5
6
7
8
9
10
/**
* method = RequestMethod.GET GET请求才允许访问 <br/>
* method = RequestMethod.POST POST请求才允许访问 <br/>
* @return
*/
@RequestMapping(value = "/method",method = RequestMethod.POST)
public String method(){
System.out.println(" method() called!!! ");
return "ok";
}

5.5、@RequestMapping标注在Controller类上

@ReqeustMapping如果在类上使用,就给每个方法的请求地址都添加上一个前缀(模块路径)

5.6、通配符在@RequestMapping中的使用(ant模式匹配)

1、绝对匹配

1
2
@RequestMapping(value = "/params1")
//要求访问地址必须是: http://ip:port/工程路径/params1

2、? 问号 匹配资源路径

? 问号表示一个任意字符,比如匹配fu[0-9a-zA-Z]

1
2
3
4
5
6
7
8
9
10
11

/**
* value = "/fu?" 在路径中的问号? 表示任意一个字符都可以匹配<br/>
*
* @return
*/
@RequestMapping(value = "/fu?")
public String fun1(){
System.out.println("fun1() ...");
return "ok";
}

3、* 星号 匹配资源路径

* 星号 可以匹配任意个字符

1
2
3
4
5
6
7
8
9
10
/**
* value = "/fu*" 路径中的星表示任意个字符都可以匹配<br/>
*
* @return
*/
@RequestMapping(value = "/fu*")
public String fun2(){
System.out.println("fun2 () * ...");
return "ok";
}

注:当一个路径同时匹配多个规则的时候,调用方法的优先顺序是:

绝对匹配—>>>>?问号匹配—->>>>*星号匹配

一句话总结,就是越精确,越优先

4、? 问号 匹配一个字符的目录

1
2
3
4
5
6
7
8
/**
* /?/fun 中的问号,表示一个字符的目录 <br/>
*/
@RequestMapping(value = "/?/fun")
public String fun3(){
System.out.println("fun3 () /?/fun ...");
return "ok";
}

5、* 星号匹配多个字符的一层目录

1
2
3
4
5
6
7
8
/**
* /星/fun 中的*星号,表示多个字符的目录 <br/>
*/
@RequestMapping(value = "/*/fun")
public String fun4(){
System.out.println("fun4 () /*/fun ...");
return "ok";
}

6、** 星星号 匹配多层目录

1
2
3
4
5
6
7
8
/**
* /** /fun 中的**星号,表示任意个字符的多层目录 <br/>
*/
@RequestMapping(value = "/**/fun")
public String fun5(){
System.out.println("fun5 () /**/fun ...");
return "ok";
}

源码解析SpringMVC如何调用Controller目标方法:

在浏览器中调访问地址之后。SpringMVC底层是如何访问到的相对应的Controller中的访问的。

Handler就是Controller控制器中用于处理业务的方法。

HandlerAdapter 用来执行 Handler 对应的方法

1 在DispatcherServlet程序中1016行 getHandler() 方法通过 请求的url地址,得到 Controller控制器方法的反射对象

/hello ====>>>> xxxController#xxxx()

/param1 ====>>> xxxxController#xxx()

2 在DispatcherServlet程序中 1023行,getHAndlerAdapter() 获取Handler的适配器对象,适配器对象用于执行Handler

3 在dispatcherServlet程序中 1040行,ha.handle() 方法 通过反射执行Controller#xxx() 目标方法

6、Controller中如何接收请求参数

6.1、原生API参数类型

#### 6.1.1、HttpServletRequest类、HttpSession类,HttpServletResponse类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 如果有某些原因,需要用到原始的API,比如HttpServletRequest,HttpSession,HttpServletResopnse类,可以直接把此写到Controler的参数上即可!!!
* @return
*/
@RequestMapping(value = "/param1")
public String param1(HttpServletRequest request, HttpSession httpSession, HttpServletResponse httpServletResponse){
System.out.println("reqeust => " + request);
System.out.println("请求的url地址:" + request.getRequestURI());
System.out.println("请求的方式:" + request.getMethod());
System.out.println("请求的工程路径:" + request.getContextPath());
System.out.println("======================");
System.out.println(httpSession);
System.out.println( "编号:" + httpSession.getId() );
System.out.println("超时时长:" + httpSession.getMaxInactiveInterval());
System.out.println("======================");
System.out.println(httpServletResponse);

return "ok";
}

6.2、普通类型入参

1
2
3
4
5
6
7
8
9
10
11
/**
* 要求是客户端发送的参数名和Controller方法中的参数名必须一致!!! <br/>
* @return
*/
@RequestMapping(value = "/param2")
public String param2(String username,String password){
System.out.println("username =>" + username);
System.out.println("password =>" + password);

return "ok";
}

6.3、普通类型数组的参数

要求是客户端发送的参数名和Controller方法中的参数名必须一致!!!

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 要求是客户端发送的参数名和Controller方法中的参数中必须一致!!! <br/>
* @return
*/
@RequestMapping(value = "/param3")
public String param3(String[] hobby){

System.out.println("hobby =>" + Arrays.asList(hobby));

return "ok";
}

6.4、普通类型使用@RequestParam入参

@RequestParam作用是将客户端的参数赋值给方法的参数

1
2
3
4
5
6
7
8
9
10
/**
* @RequestParam(name = "user") String username是指将客户端user参数的值,赋给方法参数username <br/>
* required = false 表示指定的客户端参数可能没有<br/>
* defaultValue = "我是默认值,我萌萌哒!!!" 表示客户端参数不存在,就使用默认值<br/>
*/
@RequestMapping(value = "/param4")
public String param4(@RequestParam(name = "user",required = false,defaultValue = "我是默认值,我萌萌哒!!!") String username){
System.out.println("username =>" + username);
return "ok";
}

6.5、@RequestHeader获取请求头入参

@RequestHeader 获取指定请求头的值赋给方法参数,用法跟@RequestParam注解一样

1
2
3
4
5
6
7
8
@RequestMapping(value = "/param5")
public String param5(
@RequestHeader(name = "User-Agent") String userAgent,
@RequestHeader(name = "Accept-Encoding") String acceptEncoding) {
System.out.println("请求头UserAgent => " + userAgent);
System.out.println("请求头acceptEncoding => " + acceptEncoding);
return "ok";
}

6.6、@CookieValue获取Cookie值入参

@CookieValue的使用和@ReqeustHeader以及@ReqeustParam一样

1
2
3
4
5
6

@RequestMapping(value = "/param6")
public String param6(@CookieValue(name="JSESSIONID") String jsessionId){
System.out.println(" Cookie的值是 ==>> " + jsessionId);
return "ok";
}

6.7、一个Pojo对象类型的参数

JavaBean对象

1
2
3
4
5
public class Person {
private Integer id;
private Integer age;
private String name;
private String phone;

表单:

1
2
3
4
5
6
7
<form action="http://localhost:8080/springmvc_01/param7" method="get">
姓名:<input type="text" name="name"> <br/>
编号:<input type="text" name="ids"> <br/>
年龄:<input type="text" name="age"> <br/>
电话:<input type="text" name="phone"> <br/>
<input type="submit" value="提交">
</form>

Controller代码:

1
2
3
4
5
6
7
8
9
10
/**
* 如果传入的参数刚好封装成为一个Bean对象的时候,要求客户端参数名必须要和Bean对象的属性名一致(Bean对象一定要有set方法)
* @param person
* @return
*/
@RequestMapping(value = "/param7")
public String param7(Person person){
System.out.println(" Person => " + person);
return "ok";
}

6.8、对象中套对象(级联属性)

JavaBean对象

1
2
3
4
5
6
7
8
9
10
11
12

public class Car {
private String name;
private String carNo;

public class Person {
private Integer id;
private Integer age;
private String name;
private String phone;
private Car car;
}

表单:

1
2
3
4
5
6
7
8
9
10
<form action="http://localhost:8080/springmvc_01/param7" method="get">
姓名:<input type="text" name="name"> <br/>
编号:<input type="text" name="id"> <br/>
年龄:<input type="text" name="age"> <br/>
电话:<input type="text" name="phone"> <br/>
<%-- 级联属性写法 --%>
车名:<input type="text" name="car.name"> <br/>
车牌:<input type="text" name="car.carNo"> <br/>
<input type="submit" value="提交">
</form>

Controller中的代码:

1
2
3
4
5
6
7
8
9
10
/**
* 如果传入的参数刚好封装成为一个Bean对象的时候,要求客户端参数名必须要和Bean对象的属性名一致(Bean对象一定要有set方法)
* @param person
* @return
*/
@RequestMapping(value = "/param7")
public String param7(Person person){
System.out.println(" Person => " + person);
return "ok";
}

7、返回值的设置

7.1、返回String

7.1.1、返回String默认情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@RequestMapping(value = "/return1")
public String return1(){
System.out.println(" return1() ");
/**
* 1.默认情况下,如果没有视图解析器,我们需要写上完整的跳转路径<br/>
* 2.默认情况下,如果有视图解析器,我们只需要返回文件名即可。<br/>
* <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
* <property name="prefix" value="/pages/user/" />
* <property name="suffix" value=".jsp" />
* </bean>
* 因为有视图解析器的时候,返回的结果会自动的跟视图解析器中的前后缀做拼接操作,然后再跳转<br/>
*
* 返回值 ok =>> 又叫视图名
*
* 默认情况下。SpringMVC底层是使用请求转发来跳转页面<br/>
*/
return "ok";
}

7.1.2、显示转发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@RequestMapping(value = "/return2")
public String return2(){
System.out.println("return2()");
/**
* 显示的要求请求转发<br/>
* 1 只需要在返回值的前面添加 forward: 前缀。表示显示的请求转发<br/>
* 2 当我们显示的写为请求转发的时候,这个路径不会和视图解析器中的前后缀做拼接操作<br/>
* 3 显示的请求转发后缀的地址分为绝对路径和相对路径 两种 <br/>
* 4 以斜杠打头的地址是绝对路径 ===>>> forward:/pages/user/ok.jsp <br/>
* 5 没有以斜杠打头的地址是相对路径 ===>>> forward:pages/user/ok.jsp <br/>
* 6 开发的时候,只能使用绝对路径(相对路径没出错,是运气好)<br/>
* 7 相对路径在工作的时候,需要参数当前请求地址 <br/>
* forward:pages/user/ok.jsp 参照的请求地址是: http://localhost:8080/springmvc_01/return2 <br/>
* 得到最后的跳转地址: http://localhost:8080/springmvc_01/pages/user/ok.jsp <br/>
* 如果请求地址是: http://localhost:8080/springmvc_01/abc/return2 <br/>
* 最后得到的跳转路径是: http://localhost:8080/springmvc_01/abc/pages/user/ok.jsp 得到错误的地址<br/>
*/
return "forward:/pages/user/ok.jsp";
}

7.1.3、显示重定向

1
2
3
4
5
6
7
8
9
10
11
12
13
@RequestMapping(value = "/return3")
public String return3(){
System.out.println(" return3() 显示重定向 ");
/**
* 显示重定向<br/>
* 1 在返回值前面添加 前缀 redirect: 表示显示重定向<br/>
* 2 显示重定向不会和视图解析器中的前后缀一起拼接<br/>
* 3 显示的重定向后面的路径也分绝对路径和相对路径。<br/>
* 4 以斜杠打头的地址是绝对路径,没有以斜杠打头的地址是相对路径<br/>
* 5 开发的时候只用绝对路径<br/>
*/
return "redirect:/pages/user/ok.jsp";
}

7.2、返回ModelAndView

7.2.1、返回modelAndView的默认情况

1
2
3
4
5
6
7
8
9
10
11
12
    @RequestMapping(value = "/modelAndView1")
public ModelAndView modelAndView1(){
ModelAndView modelAndView = new ModelAndView("ok");

System.out.println(" modelAndView1() ");
// 通过调用以下方法设置跳转路径
// 设置视图名。这个视图名会跟 视图解析器中的前后缀做拼接操作<br/>
// 默认使用的是请求转发
// modelAndView.setViewName("ok");

return modelAndView;
}

7.2.2、显示转发 和 显示重定向

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@RequestMapping(value = "/forwardAndRedirect")
public ModelAndView forwardAndRedirect(){

System.out.println(" modelAndView 中 forwardAndRedirect() ");

ModelAndView modelAndView = new ModelAndView();
// 设置跳转的路径
// 如果显示的请求转发或显示的重定向,只需要在前面添加forward:或redirect:即可<br/>
// 在ModelAndView中的显示请求转发和显示的重定向也不会和视图解析器中的前后缀做拼接操作<br/>
// 在ModelAndView中显示的请求转发或显示的重定向,一定要使用绝对路径<br/>
// modelAndView.setViewName("forward:/pages/user/ok.jsp");//显示的请求转发
modelAndView.setViewName("redirect:/pages/user/ok.jsp");//显示的请求重定向
return modelAndView;
}

源码解析:目标方法返回值最终都被封装成为ModelAndView

在ReqeustMappingHandlerAdapter类中,第884行, getModelAndView() 方法,将ModelAndViewContainer中的Model和View封装成为ModelAndView返回


SpringMVC-01
http://www.kju147.com/2021/07/21/SpringMVC-01/
作者
个人学习
发布于
2021年7月21日
许可协议