一、SpringMVC 概述
1.1、概述
- SpringMVC是基于Spring的一个框架,是Spring的一个模块,专门用来做web开发的,可以看做是Servlet的升级。
- SpringMVC就是一个Spring容器,可以创建对象放入容器中,SpringMVC容器放的是@Controller控制器对象。
- SpringMVC的优点:
- 轻量级:jar包很小。
- 使用简单:使用注解开发变得简单。
- 基于MVC架构:分工明确,各司其职。
1.2、SpringMVC入门程序
1.2.1、新建maven项目引入依赖
<dependencies>
<!--SpringMVC依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!--Servlet依赖-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
1.2.2、创建处理器
@Controller //处理器对象,放在springMVC容器中
public class MyController {
/**
* @RequestMapping修饰的方法叫做处理器方法
* @RequestMapping(value = "/some.do") 将some.do请求与该方法绑定
* 发送some.do请求时执行该方法
* value = {}可以是一个字符串数组,如果需要多个请求路径都执行该方法可以写进数组,用逗号隔开
* 返回值ModelAndView 表示本次请求处理的结果
* Model:数据,显示给用户的数据
* View:视图,展示给用户的页面
*/
@RequestMapping(value = "/some.do") //也可以使用在类上
public ModelAndView doSome(){
ModelAndView mv = new ModelAndView();
//向Model中添加数据,Model的底层是HashMap,Model中的数据存储在request域中
mv.addObject("data1","request域中数据1");
mv.addObject("data2","request域中数据2");
//springmvc框架执行转发操作,相当于request.getRequestDispatcher("/show.jsp").forward(req,rep)
mv.setViewName("/show.jsp");
//配置视图解析器后只需要写转发页面的名字
mv.setViewName("show");
//返回mv给DispatcherServlet
return mv;
}
}
1.2.3、springmvc配置文件
- 只要配置了视图解析器,并且setViewName()方法内没有使用forward转发和redirect重定向,那么setViewName()方法里面的参数都会加上视图解析器的前缀和后缀。
<!--添加包扫描,扫描对应包下的注解-->
<context:component-scan base-package="com.lhl.controller"/>
<!--注册视图解析器,用来帮助我们简化开发,在写转发的请求路径时只需写名字-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--
前缀: 页面所在的路径 例如/WEB-INF/jsp/show.jsp
配置完成后视图解析器会自动添加路径及扩展名
web回顾:
资源放在WEB-INF下,在浏览器地址栏直接访问该资源无法访问,也就是说资源受保护了,只有前端访问后端,后端在转发到该资源
-->
<property name="prefix" value="/WEB_INF/jsp/"/>
<!--后缀: 页面的文件的扩展名 例如show.jsp ==> .jsp-->
<property name="suffix" value=".jsp"/>
</bean>
1.2.4、配置web.xml文件
<!--
指定SpringMVC的中央处理器,就是一个servlet继承了HttpServlet
是依赖org.springframework.web.servlet下的一个类
-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<!--指定SpringMVC配置文件-->
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!--
设置服务器启动时创建DispatcherServlet对象
当值大于等于 0 时,表示服务器启动时创建该对象,数值越小优先级越高,被创建也就越早
当值小于 0 或者没有指定时,则表示该 Servlet 在真正被使用时才会去创建。
当值相同时,容器会自己选择创建顺序
-->
<load-on-startup>1</load-on-startup>
</servlet>
<!--配置中央处理器对应的请求路径,凡是以.do结尾的请求都会-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
1.2.5、创建页面
- index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>欢迎页面</title>
</head>
<body>
<a href="/ch01/some.do">发送some.do请求</a>
</body>
</html>
- show.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>数据1:${data1}</h3>
<h3>数据2:${data2}</h3>
</body>
</html>
1.2.6、配置服务器部署项目
1.2.7、启动服务器访问页面
- 欢迎页
- 请求响应页面
1.3、SpringMVC请求的执行流程
-
1)发起some.do
2)tomcat(web.xml–url-pattern知道 *.do的请求给DispatcherServlet)
3)DispatcherServlet(根据springmvc.xml配置知道 some.do—doSome())
4)DispatcherServlet把some.do转发个MyController.doSome()方法
5)框架执行doSome()把得到ModelAndView进行处理, 转发到show.jsp -
简化流程: some.do请求 ----> DispatcherServlet ----> Controller处理器对象执行处理方法转发视
-
SpringMVC执行过程源码分析(了解):
- 一、Tomcat服务器启动都干了什么?(在init方法中创建springmvc容器)
-
web.xml中通过load-on-start标签指定1,创建DispatcherServlet对象
-
DispatcherServlet执行init初始化方法,
-
在init()方法中:
- 读取springmvc配置文件创建容器对象并创建@Controller注解所在类的对象:WebApplicationContext ctx = new ClassPathXmlApplicationContext(“springmvc.xml”);
- 把容器对象放入到ServletContext域中 底层:getServletContext().setAttribute(key, ctx);
- 二、发送请求都执行了哪些方法?
- 执行service()方法
- 执行DispatcherServlet中的doService()方法中的doDispatcher()方法
- 在doDispatcher()方法中转发执行Controller处理器对象的处理器方法
- doDispatch:springmvc中DispatcherServlet的核心方法, 所有的请求都在这个方法中完成的。
1.4、springmvc内部执行流程
-
处理器映射器(HandlerMapping):
- 概念:springmvc容器中的一种对象,实现了HandlerMapping接口的类,可以有多个。
- 作用:根据请求从springmvc容器中获取处理器对象(MyController controller = ctx.getBean(“请求路径”)),将处理器对象封装到处理器执行链的类,返回给中央调度器。
-
处理器执行链(HandlerExecutionChain):
- 处理器执行链中封装了处理器对象及拦截器。
-
处理器适配器(HandlerAdaptor:
- 概念:springmvc容器的一种对象,实现了HandlerAdaptor接口的类,可以有多个。
- 作用:通过执行链中的处理器对象执行处理器方法,返回给中央调度器
-
视图解析器:
- 概念:springmvc容器中的一种对象,实现了ViewResolver接口,可以有多个。
- 作用:组成视图完整路径,并创建View对象,View是一个接口表示视图(页面)
-
流程图
- 框架内部执行流程:
- 浏览器提交请求给中央调度器。
- 中央调度器把请求转给处理器映射器。
- 处理器映射器根据请求找到对应的处理器,并将处理器封装到处理器执行链中,返回给中央调度器。
- 中央调度器根据执行链中的处理器找到对应的适配器。
- 处理器适配器调用执行处理器。
- 处理器执行处理器方法,并返回ModelAndView给处理器适配器。
- 处理器适配器直接返回ModelAndView给中央调度器。
- 中央调度器调用视图解析器,将ModelAndView中的视图封装为视图对象(View)
- 视图解析器将视图对象返回给中央调度器。
- 中央调度器调用视图(View)对象,让视图对象进行渲染(比如:向request域中存数据),形成响应对象。
- 中央调度器响应给浏览器
二、SpringMVC注解式开发
- @Controller:创建控制器类的对象,接受请求,处理请求。
- @RequestMapping:请求映射,将请求绑定到一个方法,让这个方法去处理请求。
2.1、RequestMapping定义请求规则
2.1.1、定义模块名称
/**
* @RequestMapping(value = "/test")
* 放在类上面指定所有请求的公共部分 叫做模块名称
* value:指定所有请求的公共部分
*/
@Controller //处理器对象,放在springMVC容器中
//@RequestMapping(value = "/test")
@RequestMapping("/test") //下面方法中请求路径相同的部分可以定义到此处
public class MyController {
//因为类上面定义了uri相同的部分
///some.do","/first.do" ==> /test/some.do /test/first.do
@RequestMapping(value = {
"/some.do","/first.do"})
//@RequestMapping("/some.do")
public ModelAndView doSome(){
ModelAndView mv = new ModelAndView();
mv.addObject("msg","欢迎使用springmvc做web开发");
mv.addObject("fun","执行的dosome方法");
mv.setViewName("show");
return mv;
}
// 因为类上面定义了uri相同的部分
// "/other.do","/second.do" ==> /test/other.do /test/second.do
@RequestMapping(value = {
"/other.do","/second.do"}) // 等价==> /test/other.do
public ModelAndView doOther(){
ModelAndView mv = new ModelAndView();
mv.addObject("msg","欢迎使用springmvc做web开发");
mv.addObject("fun","执行的doother方法");
mv.setViewName("other");
return mv;
}
}
2.1.2、定义请求方式
/**
* @RequestMapping
* method属性:指定方法处理请求的方式
* RequestMethod是枚举类,里面定义了请求方式的常量,get、post、put、delete...
* GET方式:RequestMethod.GET
* POST方式:RequestMethod.POST
* 405错误:前后端请求方式不一致
*/
//指定some.do使用get请求方式才能执行此方法
@RequestMapping(value = {
"/some.do","/first.do"},method = RequestMethod.GET)
public ModelAndView doSome(){
ModelAndView mv = new ModelAndView();
mv.addObject("msg","欢迎使用springmvc做web开发");
mv.addObject("fun","执行的dosome方法");
mv.setViewName("show");
return mv;
}
//指定方法执行请求路径为/test/other.do POST请求
@RequestMapping(value = {
"/other.do","/second.do"},method = RequestMethod.POST)
public ModelAndView doOther(){
ModelAndView mv = new ModelAndView();
mv.addObject("msg","欢迎使用springmvc做web开发");
mv.addObject("fun","执行的doother方法");
mv.setViewName("other");
return mv;
}
2.2、处理器方法的参数
- 方法参数可以是HttpServletRequest、HttpServletResponse、HttpSession、请求中的参数、对象、数组、List集合、Map集合。
2.2.1、逐个参数接收
- 处理方法可以包含一下四种参数
- HttpServletRequest
- HttpServletResponse
- HttpSession
- 请求中的参数名
/**
* 逐个接受请求参数,方法中的参数顺序可以和请求中的参数顺序不一致:
* 方法的参数名必须与请求中的参数名一致
* 方法的参数类型必须与请求参数类型一致
* String name ==> 前端请求参数要求为:字符串或空值。如果前端没传值,显示空
* int age ==> 前端请求参数要求为:整型。如果前端没传值,发生400错误
* Integer age ==> 前端请求参数要求为:整型或空值。如果前端没传值,显示空
* 原理:
* request接收参数
* String name = request.getParameter("name")
* String age = request.getParameter("age")
* SpringMVC框架通过DispatcherServlet调用MyController的doSome()方法
* 按照请求中的参数与doSome()方法中的参数对应传入赋值
* 框架会提供自动类型转换功能
*/
@RequestMapping(value = {
"/some.do","/first.do"},method = RequestMethod.POST)
public ModelAndView doSome(String name,Integer age){
ModelAndView mv = new ModelAndView();
mv.addObject("myname",name);
mv.addObject("myage",age);
mv.setViewName("show");
return mv;
}
public void doSome(HttpServletRequest request, HttpServletResponse response, HttpSession httpSession){
}
2.2.2、请求参数名与形参名不一致
- @RequestParam
/**
* http://localhost:8080/ch03/test/some.do?rname=张三&rage=20
* @RequestParam注解的属性
* value:请求路径中参数的名字;rname,rage。
* required:该参数是否必须,true表示请求中必须包含该参数,false与之相反,默认是true
* 执行该方法时查找rname和rage参数的值赋给name和age
*/
@RequestMapping(value = "/some.do")
public ModelAndView doSome(@RequestParam(value = "rname",required = false) String name,
@RequestParam(value = "rage") Integer age){
ModelAndView mv = new ModelAndView();
mv.addObject("myname",name);
mv.addObject("myage",age);
mv.setViewName("show");
return mv;
}
2.2.3、对象参数接收
- 定义类及与请求参数同名的属性
public class User {
private String name;
private Integer age;
public User() {
System.out.println("构造方法");//验证框架创建对象
}
public String getName() {
return name;
}
public void setName(String name) {
System.out.println("user的setName方法");//验证框架调用setName方法
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
System.out.println("user的setAge方法");//验证框架调用setAge方法
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
/**
* 请求路径:http://localhost:8080/ch03/test/some.do?name=张三&age=123
* 处理方法形参是java对象,要求对象的属性名和请求路径的参数名一致,并且有set方法
* 因为框架会创建java对象,使用set方法给属性赋值
*/
@RequestMapping