资料
1、javaWeb 链接:https://pan.baidu.com/s/1T3ouoZuZCMCAwPRv1Tahdg?pwd=5u25 提取码:5u25
2、主流框架 链接:https://pan.baidu.com/s/10HGe7wP1aed2HUCihc3-yQ?pwd=afjd 提取码:afjd
3、微服务架构 链接:https://pan.baidu.com/s/14RCkZXWyRP5hlOpDpp_ESQ?pwd=g0at 提取码:g0at
4、互联网生态 链接:https://pan.baidu.com/s/1IlM4LAU2gQqUMeN_B48t8w?pwd=egl7 提取码:egl7
6、架构师必会 链接:https://pan.baidu.com/s/10fPzIzSskuecnSxs4F4FRQ?pwd=s8tg 提取码:s8tg
SpringMVC 概述
- SpringMVC 也叫 Spring Web MVC
- SpringMVC 是基于 spring 的一个框架, 实际上就是 spring 的一个模块, 是专门做 web 开发的,SpringMVC 也可以理解为是 servlet 的升级版
- web 开发的底层是 servlet,框架是在 servlet 的基础上加入了一些功能,让你做 web 开发更加方便。
- SpringMVC其实就是Spring,只不过用在web开发方面
- SpringMVC基于MVC架构
- 由于 SpringMVC 是基于 Spring 的,所以SpringMVC也可以认为是一个Spring,由于 Spring 是一个容器,可以通过
<bean>
、@Component
、@Repository
、@Service
、@Controller
创建对象,并且可以通过 IoC 来管理对象,因此SpringMVC也能够创建对象,并且将对象放在容器中(SpringMVC的容器),在SpringMVC容器中放的是控制器对象
- 在SpringMVC中使用
@Controller
来标注创建控制器对象,并将控制器对象放在SpringMVC容器中,这些对象作为控制器使用,这些对象能够接收用户的请求、显示处理的结果(当做 servlet 使用,但不是 servlet)。 - 使用
@Controller
标注创建的对象就是一个普通的Java对象,不是servlet,但是SpringMVC赋予了这些对象额外的功能,使其能够像servlet一样接收处理用户的请求,具有控制器的能力。
- 由于使用
@Controller
标注创建的对象是一个普通的对象,所以不能直接接收用户的请求,但是SpringMVC中有一个对象DispatherServlet(中央调度器),该对象是一个Servlet,该对象负责接收用户的所有请求,然后将用户的请求转发给相应的使用@Controller
标注创建的对象,由使用@Controller
标注创建的对象来处理用户的请求。
SpringMVC 处理用户请求的过程
- 用户发送请求
- 由DispatherServlet(中央调度器)接收所有的用户请求
- DispatherServlet将用户请求转发/分配给相应的控制器对象(@Controller标注的对象)
- 控制器对象处理用户请求
- 用户请求处理结果返回给DispatherServlet
- DispatherServlet再将用户请求的处理结果返回给用户
第一个 SpringMVC 程序
创建项目
- 添加缺少的目录
- 将java目录标记为源代码的根目录
引入依赖
- 引入 spring-webmvc 依赖,由于 springmvc 是基于 spring 的,所以引入 spring-webmvc 依赖,会简洁引入 spring 的其他依赖
- servlet 依赖,因为底层还是 servlet
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.cw</groupId> <artifactId>springmvc-001</artifactId> <version>1.0-SNAPSHOT</version> <!-- web 项目的打包方式 --> <packaging>war</packaging> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <!-- JDK 17 --> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> </properties> <dependencies> <!-- springmvc 依赖 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.5.RELEASE</version> </dependency> <!-- servlet 依赖 --> <!-- 我使用的 tomcat 为 8.x --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> </dependencies> <build> </build> </project>
注册 DispatherServlet 对象
- 修改 web.xml 的版本为 4.0
- 在 web.xml 文件中注册 springmvc 的核心对象 DispatherServlet(中央调度器)
- DispatherServlet 本身是一个 Servlet,它的父类继承 HttpServlet
- DispatherServlet 也叫前端控制器(front controller),负责接收用户提交的请求,调用其他控制器对象,并把请求的处理结果返回给用户
- springmvc 的项目必须有 DispatherServlet 对象
<?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 的核心对象 DispatherServlet --> <!-- 需要在tomcat服务器启动后,创建DispatcherServlet对象的实例。 --> <!-- 为什么要创建DispatcherServlet对象的实例呢? --> <!-- 因为DispatcherServlet在他的创建过程中, 会同时创建springmvc容器对象, 读取springmvc的配置文件,把这个配置文件中的对象都创建好, 当用户发起 请求时就可以直接使用对象了。 --> <!-- servlet的初始化会执行init()方法。 DispatcherServlet在init()中会执行 { //创建springmvc容器,读取配置文件 WebApplicationContext ctx = new ClassPathXmlApplicationContext("springmvc配置文件.xml"); //把容器对象放入到ServletContext中 getServletContext().setAttribute(key, ctx); } --> <servlet> <!-- 启动tomcat时实例化DispatcherServlet对象,会读取springmvc的配置文件 springmvc的配置文件路径默认为:/WEB-INF/<servlet-name>-servlet.xml 对于springmvc的配置文件路径我们也可以进行自定义 --> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 自定义springmvc读取配置文件路径 --> <init-param> <!-- contextConfigLocation:指定springmvc配置文件位置的属性 --> <param-name>contextConfigLocation</param-name> <!-- 指定springmvc配置文件的路径 --> <!-- 类路径/springmvc.xml --> <param-value>classpath:springmvc.xml</param-value> </init-param> <!-- 在tomcat启动后,创建Servlet对象 load-on-startup: 表示tomcat启动后创建对象的顺序。 它的值是整数,数值越小,tomcat创建对象的时间越早。 大于等于0的整数。 --> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <!-- 使用框架的时候, url-pattern可以使用两种值 1. 使用扩展名方式, 语法 *.xxxx , xxxx是自定义的扩展名。 常用的方式 *.do, *.action, *.mvc等等 不能使用 *.jsp http://localhost:8080/myweb/some.do http://localhost:8080/myweb/other.do 表示以 .do 结尾的请求都交给 springmvc 这个 servlet 处理 2.使用斜杠 "/" --> <url-pattern>*.do</url-pattern> </servlet-mapping> </web-app>
创建发起请求的页面 index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <p><a href="test.do">发起请求</a></p> </body> </html>
创建控制器类
- 对于控制器类,需要使用
@Controller
注解标注,会创建控制器对象,并放到 springmvc 容器中 - 在类中的方法上添加
@RequestMapping
注解
- 使用该注解修饰的方法叫做处理器方法或控制器方法
- 使用该注解修饰的方法可以处理请求,类似servlet中的doGet、doPost方法
- 声明视图解析器,用于帮助处理视图
package cw.springmvc.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; /** * ClassName: MyController * Package: cw.springmvc.controller * Description: * * @Author tcw * @Create 2023-05-14 21:40 * @Version 1.0 */ // 能处理请求的都是控制器(处理器),MyController能处理请求 // 是控制器(处理器),也叫后端控制器(back controller) @Controller public class MyController { /** * springmvc中使用方法来处理用户提交的请求 * 方法是自定义的,可以有多种返回值、多种参数,方法名自定义 * 使用该方法处理 test.do 的请求 * 处理请求的方法,使用 @RequestMapping 标注, * 作用:把请求地址和方法绑定,一个请求指定一个方法处理 * * @return ModelAndView:本次请求处理的结果 * Model:请求处理完成后,要显示给用户的数据 * View:视图 * @RequestMapping: 属性: * value:String[] 类型,请求的uri地址,value值必须唯一,推荐以“/”开头 * 使用位置: * 1. 方法上(常用) * 2. 类上 */ @RequestMapping({"/test.do"}) // / 表示该web项目的根地址 public ModelAndView doSome() { ModelAndView modelAndView = new ModelAndView(); // 添加数据, 框架在请求的最后把数据放入到request作用域。 // request.setAttribute("msg","欢迎使用springmvc做web开发"); modelAndView.addObject("msg", "hello world"); // 指定视图, 指定视图的完整路径 // 后面框架会对视图执行的forward操作, request.getRequestDispather("/show.jsp").forward(...) modelAndView.setViewName("/show.jsp"); return modelAndView; } }
创建显示结果的页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <h1>show.jsp从request作用域中获取数据</h1> <h2>${msg}</h2> </body> </html>
创建 springmvc 的配置文件
- springmvc 的配置文件和 spring 的配置文件一样
- 声明组件扫描器,指定
@Controller
注解所在的包名
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" 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/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="cw.springmvc.controller"/> </beans>
请求处理过程分析
- 用户发起请求 test.do
- tomcat 接收用户的请求
- tomcat 读取web项目的xml文件,通过servlet配置的url-pattern得知 *.do 的请求要交给DispatcherServlet对象处理
- DispatcherServlet对象接收到tomcat转发过来的请求
- DispatcherServlet读取springmvc.xml配置文件,得知 test.do 请求由MyController中的doSome方法进行处理
- DispatcherServlet对象将请求交给MyController的doSome方法
- doSome方法处理请求,并将处理结果放在ModelAndView对象上,然后将ModelAndView对象返回
- doSome方法将请求以及请求处理结果转发给 show.jsp
- show.jsp 将请求处理结果返回展示给用户
DispatcherServlet 源码分析
init()
- DispatcherServlet 的父类为 HttpServlet,所以 DispatcherServlet 是一个 Servlet,创建 DispatcherServlet 对象时会执行 init() 方法。
init() { // 创建springmvc容器,读取配置文件 // 读取springmvc配置文件时会进行对象的创建,并将创建出来的对象放到springmvc容器中 WebApplicationContext ctx = new ClassPathXmlApplicationContext("springmvc配置文件.xml"); // 把容器对象放入到ServletContext中 getServletContext().setAttribute(key, ctx); }
- 创建 DispatcherServlet 对象时,执行的 init() 方法在 DispatcherServlet 的父类 FrameworkServlet 的父类 HttpServletBean 中
public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware { public final void init() throws ServletException { ...... // HttpServletBean 的子类 FrameworkServlet 对该方法有重写, // DispatcherServlet 没有对该方法进行重写 // 所以调用的为 FrameworkServlet 的 initServletBean() this.initServletBean(); } protected void initServletBean() throws ServletException { } }
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware { protected final void initServletBean() throws ServletException { ...... try { // 创建springmvc容器 this.webApplicationContext = this.initWebApplicationContext(); this.initFrameworkServlet(); } catch (RuntimeException | ServletException var4) { this.logger.error("Context initialization failed", var4); throw var4; } ...... } // 创建springmvc容器 protected WebApplicationContext initWebApplicationContext() { WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext()); WebApplicationContext wac = null; // springmvc容器对象变量 // springmvc容器是否已经存在 if (this.webApplicationContext != null) { wac = this.webApplicationContext; ... } if (wac == null) { // 找springmvc容器 wac = this.findWebApplicationContext(); } if (wac == null) { // 找不到springmvc容器就创建springmvc容器 // 创建springmvc容器过程中会读取springmvc的配置文件 wac = this.createWebApplicationContext(rootContext); } ... if (this.publishContext) { String attrName = this.getServletContextAttributeName(); // 将springmvc容器放到web应用级作用域中(本web项目的全局作用域) this.getServletContext().setAttribute(attrName, wac); } // 返回springmvc容器 return wac; } }
doDispath()
- DispatcherServlet 对象是一个 Servlet,当用户发送请求并且该请求后面将会交给 DispatcherServlet 对象处理,tomcate 会调用 DispatcherServlet 对象的 service 方法接收处理请求
- 在 DispatcherServlet 中没有重写 service 方法,在 HttpServletBean 中也没有,service 方法在 FrameworkServlet 中有进行重写,所以会调用 FrameworkServlet 中的 service 方法
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware { ...... protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpMethod httpMethod = HttpMethod.resolve(request.getMethod()); if (httpMethod != HttpMethod.PATCH && httpMethod != null) { super.service(request, response); } else { // 执行该方法,处理请求 this.processRequest(request, response); } } protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ... try { // 执行该方法 this.doService(request, response); } catch (IOException | ServletException var16) { ... } catch (Throwable var17) { ... } finally { ... } } // 由于 FrameworkServlet 中的 doService 方法为抽象方法 // 所以会调用子类 DispatcherServlet 中重写实现的 doService 方法 protected abstract void doService(HttpServletRequest var1, HttpServletResponse var2) throws Exception; ...... }
public class DispatcherServlet extends FrameworkServlet { ...... protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { ... try { // 调用执行该方法 this.doDispatch(request, response); } finally { ... } } // DispatcherServlet 处理请求的核心方法 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { try { // 模型和视图 ModelAndView mv = null; ... // 会根据请求调用相应的处理器 Controller 对象中相应的用于处理该请求的方法 /* 如果用户发送 test.do 请求 @RequestMapping({"/test.do"}) public ModelAndView doSome() { ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("msg", "hello world"); modelAndView.setViewName("/show.jsp"); return modelAndView; } */ // mv 接收返回的 ModelAndView 对象 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); ... } catch (Exception var22) { this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22); } catch (Throwable var23) { this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23)); } } finally { ... } } ...... }
配置视图解析器
- 对于不想让用户可以直接通过浏览器的url地址访问的页面(视图),可以将其放到 WEB-INF 目录下,让其受到保护,不能通过浏览器的url直接访问,此时要让视图使用时使用的路径和原来一样(即不用写
/WEB-INF/views/...
),就需要配置视图解析器 - 视图解析器的配置写在springmvc的配置文件中
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" 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/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 组件扫描器 --> <context:component-scan base-package="cw.springmvc.controller"/> <!-- 声明springmvc框架中的视图解析器,帮助设置视图文件的路径 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- 前缀:视图文件所在的目录 --> <!-- InternalResourceViewResolver 的 prefix 属性为String类型 --> <!-- 最前面的 / 表示本web资源项目的根路径,即webapp --> <!-- 最后的 / 表示路径 --> <property name="prefix" value="/WEB-INF/views/"/> <!-- 后缀:视图文件的扩展名 --> <!-- InternalResourceViewResolver 的 suffix 属性为String类型 --> <property name="suffix" value=".jsp"/> </bean> </beans>
package cw.springmvc.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; /** * ClassName: MyController * Package: cw.springmvc.controller * Description: * * @Author tcw * @Create 2023-05-14 21:40 * @Version 1.0 */ @Controller public class MyController { @RequestMapping({"/test.do"}) public ModelAndView doSome() { ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("msg", "hello world"); // 配置完后使用视图文件只需指定文件名即可 // 框架会使用视图解析器中配置的 前缀 + 文件名 + 后缀 组成完整的视图文件路径 modelAndView.setViewName("show"); return modelAndView; } }
类上的 RequestMapping 注解
- 将 RequestMapping 注解写到类上,可以把请求uri中的公共前缀部分进行抽取,相当于对路由进行分组
package cw.springmvc.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; /** * ClassName: MyController * Package: cw.springmvc.controller * Description: * * @Author tcw * @Create 2023-05-14 21:40 * @Version 1.0 */ @Controller @RequestMapping({"/study"}) // 请求uri前缀为/study匹配到该控制类 public class MyController { @RequestMapping({"/test.do"}) // 除去前缀后的uri为/test.do匹配到该方法处理请求 public ModelAndView doSome() { ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("msg", "hello world"); modelAndView.setViewName("show"); return modelAndView; } }
设置方法处理请求的类型
- RequestMapping 注解中具有 method 属性,该属性可以用于指定 RequestMapping 标注的方法用于处理什么类型的请求,该属性的值为 RequestMethod 枚举类的枚举值
- 如果没有指定方法处理的请求类型,则该方法可以处理任何类型的请求
@Controller @RequestMapping({"/study"}) public class MyController { // 该方法用于处理请求地址为/study/test.do的GET请求 @RequestMapping(value = {"/test.do"}, method = RequestMethod.GET) public ModelAndView doSome() { ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("msg", "hello world"); modelAndView.setViewName("show"); return modelAndView; } }
@Controller @RequestMapping({"/study"}) public class MyController { // 该方法用于处理请求地址为/study/test.do的POST请求 @RequestMapping(value = {"/test.do"}, method = RequestMethod.POST) public ModelAndView doSome() { ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("msg", "hello world"); modelAndView.setViewName("show"); return modelAndView; } }
处理器方法形参
- 处理器方法可以包含以下四类参数,这些参数会在系统调用时由系统自动赋值,即程序员可在方法内直接使用。
- HttpServletRequest
- HttpServletResponse
- HttpSession
- 请求中所携带的请求参数
HttpServletRequest
@Controller @RequestMapping({"/study"}) public class MyController { @RequestMapping(value = {"/test.do"}) public ModelAndView doSome(HttpServletRequest request, HttpServletResponse response, HttpSession session) { ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("msg", "name = " + request.getParameter("name")); modelAndView.setViewName("show"); return modelAndView; } }