面试官:说一下Spring MVC的启动流程呗!

在这里插入图片描述

Servlet规范和容器

我们常用的Spring MVC是基于Servlet规范实现的,所以我们先来回顾一下Servlet相关的内容。

在这里插入图片描述

如果我们直接用Servlet来开发web应用,只需要继承HttpServlet,实现service方法即可,HttpServlet继承自Servlet,Servlet中常用的方法如下

public interface Servlet {
    
    

	// 初始化,只会被调用一次,在service方法调用之前完成
    void init(ServletConfig config) throws ServletException;
    
    ServletConfig getServletConfig();
    
    // 处理请求
    void service(ServletRequest req, ServletResponse res)throws ServletException, IOException;
    
    String getServletInfo();
    
    // 销毁
    void destroy();
}

每个Servlet有一个ServletConfig,用来保存和Servlet相关的配置
每个Web应用有一个ServletContext,用来保存和容器相关的配置

考虑到很多小伙伴可能对Servlet的很多用法不熟悉了,简单介绍一下,就用xml配置了,当然你可以用JavaConfig的方式改一下

项目结构如下
在这里插入图片描述

web.xml

<?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_3_1.xsd"
         version="3.1">

  <context-param>
    <param-name>configLocation</param-name>
    <param-value>test</param-value>
  </context-param>

  <servlet>
    <servlet-name>userServlet</servlet-name>
    <servlet-class>com.javashitang.controller.UserServlet</servlet-class>
    <init-param>
      <param-name>helloWord</param-name>
      <param-value>hello sir</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>userServlet</servlet-name>
    <url-pattern>/user.do</url-pattern>
  </servlet-mapping>

  <listener>
    <listener-class>com.javashitang.listener.MyServletContextListener</listener-class>
  </listener>

</web-app>
public class UserServlet extends HttpServlet {
    
    

    private String helloWord;

    @Override
    public void init(ServletConfig config) throws ServletException {
    
    
        this.helloWord = config.getInitParameter("helloWord");
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        resp.setContentType("text/html");
        PrintWriter out = resp.getWriter();
        String userId = req.getParameter("userId");
        out.println(helloWord + " " + userId);
    }
}

xml配置文件中可以用init-param标签给Servlet设置一些配置,然后在init方法中通过ServletConfig来获取这些配置,做初始化

访问
http://localhost:8080/user.do?userId=1
返回
hello sir 1

可以看到我们针对这个servlet还配置了load-on-startup这个标签,那么这个标签有什么用呢?

load-on-startup表示当容器启动时就初始化这个Servlet,数组越小,启动优先级越啊高。当不配置这个标签的时候则在第一次请求到达的时候才会初始化这个Servlet

context-param标签是容器的初始化配置,可以调用容器的getInitParameter方法获取属性值

Listener是一种扩展机制,当Web应用启动或者停止时会发送各种事件,我们可以用Listener来监听这些事件,做一些初始化工作。如监听启动事件,来初始化数据库连接等。

我这个demo只是获取了一下配置文件的位置,并打印出来。

public class MyServletContextListener implements ServletContextListener {
    
    

	// 容器启动
    public void contextInitialized(ServletContextEvent sce) {
    
    
        ServletContext sc = sce.getServletContext();
        String location = sc.getInitParameter("configLocation");
        System.out.println(location);
    }

	// 容器销毁
    public void contextDestroyed(ServletContextEvent sce) {
    
    

    }
}

写一个Spring MVC应用

我们基于xml方式写一个spring mvc应用,基于这个应用来分析,项目结构如下
在这里插入图片描述
web.xml

<?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_3_1.xsd"
		 version="3.1">
		 
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:spring-context.xml</param-value>
	</context-param>

	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

	<servlet>
		<servlet-name>dispatcher</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>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<servlet-mapping>
		<servlet-name>dispatcher</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>

</web-app>

spring-context.xml(配置service,dao层)

<?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 http://www.springframework.org/schema/context/spring-context.xsd">

	<context:component-scan base-package="com.javashitang.service"/>

</beans>

spring-mvc.xml(配置和spring mvc相关的配置)

<?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"
	   xmlns:mvc="http://www.springframework.org/schema/mvc"
	   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 http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">

	<context:component-scan base-package="com.javashitang.controller"/>
	<mvc:annotation-driven/>

</beans>
@RestController
public class UserController implements ApplicationContextAware {
    
    

	@Resource
	private UserService userService;
	private ApplicationContext context;

	@RequestMapping("user")
	public String index(@RequestParam("userId") String userId) {
    
    
		return userService.getUsername(userId);
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    
    
		this.context = applicationContext;
		System.out.println("UserController " + context.getId());
	}
}
public interface UserService {
    
    

	String getUsername(String userId);
}
@Service
public class UserServiceImpl implements UserService, ApplicationContextAware {
    
    

	private ApplicationContext context;

	@Override
	public String getUsername(String userId) {
    
    
		return userId;
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    
    
		this.context = applicationContext;
		System.out.println("UserServiceImpl " + context.getId());
	}
}

我们之所以要用2个配置文件,是因为在Spring MVC中有2个容器

父容器由ContextLoaderListener来初始化,一般用来存放一些dao层和service层的Bean
子容器由DispatcherServlet来初始化,一般用来存放controller层的Bean

项目启动后从打印出的值就可以看出来,Service和Controller是从2个容器获取的

UserServiceImpl org.springframework.web.context.WebApplicationContext:
UserController org.springframework.web.context.WebApplicationContext:/dispatcher

子容器可以访问父容器中的Bean,父容器不能访问子容器中的Bean。当从子容器找不到对应的Bean时,会从父容器中找

父容器启动

父容器由ContextLoaderListener来初始化,当tomcat启动的时候,发布启动事件,调用ContextLoaderListener#contextInitialized方法,接着调用initWebApplicationContext方法

在这里插入图片描述

子容器启动

子容器的启动在DispatcherServlet#init方法中

DispatcherServlet中并没有重写init方法,那就实在父类中了,HttpServletBean重写了init方法
在这里插入图片描述
用流程图总结一下过程

在这里插入图片描述
如果你觉得父容器没啥作用的话,可以把所有的Bean都放在子容器中

参考博客

猜你喜欢

转载自blog.csdn.net/zzti_erlie/article/details/115190719