【SpringBoot源码解析】第四章:SpringBoot是如何自动装配SpringMvc的

时间:2022-07-23
本文章向大家介绍【SpringBoot源码解析】第四章:SpringBoot是如何自动装配SpringMvc的,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

缘起

在上一章中,我们介绍了SpringBoot是如何启动一个内置tomcat的。我们知道我们在SpringBoot项目里面是可以直接使用诸如@RequestMapping这类的SpringMVC的注解,那么你会不会奇怪,这是为什么?我明明没有配置SpringMVC为什么就可以使用呢?

SpringBoot是如何自动装配SpringMVC的

你还记得我们如何在一个普通的WEB项目中使用SpringMVC吗?我们首先就是要在web.xml中配置如下配置

<servlet>
    <description>spring mvc servlet</description>
    <servlet-name>springMvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>springMvc</servlet-name>
    <url-pattern>*.do</url-pattern>
</servlet-mapping>

你有没有想过配置的这个东西到底是在干嘛呢?其实要说清楚这个是在干嘛,涉及到SpringMVC的底层原理,我们这里可以理解为这就是在配置一个Dispatcherservlet,这个Dispatcherservlet是可以帮我妈转发请求到其他的Servlet,理解不了没关系,我们只要知道在SpringMVC中,必须需要先配置一个Dispatcherservlet,而在SpringBoot中,我们没有了web.xml文件,我们如何去配置一个Dispatcherservlet呢?其实Serclet3.0规范中规定,要添加一个Servlet,除了采用xml配置的方式,还有一种通过代码的方式,伪代码如下

servletContext.addServlet(name, this.servlet);

那么也就是说,如果我们能动态往web容器中添加一个我们构造好的DispatcherServlet对象,是不是就能实现自动装配SpringMVC了?那么SpringBoot又是什么时候把这个DispatcherServlet装载进容器的呢?下面来说

不知道在前面讲解【SpringBoot源码解析】第二章:SpringBoot是如何通过内置Tomcat启动的你有没有注意到有这样一段代码

getSelfInitializer().onStartup(servletContext);

这段代码其实就是去加载SpringMVC,那么他是如何做到的呢?getSelfInitializer()最终会去调用到ServletWebServerApplicationContextselfInitialize方法,该方法代码如下

private void selfInitialize(ServletContext servletContext) throws ServletException {
	prepareWebApplicationContext(servletContext);
	ConfigurableListableBeanFactory beanFactory = getBeanFactory();
	ExistingWebApplicationScopes existingScopes = new ExistingWebApplicationScopes(
			beanFactory);
	WebApplicationContextUtils.registerWebApplicationScopes(beanFactory,
			getServletContext());
	existingScopes.restore();
	WebApplicationContextUtils.registerEnvironmentBeans(beanFactory,
			getServletContext());
	for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
		beans.onStartup(servletContext);
	}
}

我们通过调试,知道getServletContextInitializerBeans()返回的是一个ServletContextInitializer集合,集合中有以下几个对象

image.png

然后依次去调用对象的onStartup方法,那么对于上图标红的对象来说,就是会调用到DispatcherServletRegistrationBeanonStartup方法,这个类并没有这个方法,所以最终会调用到父类RegistrationBeanonStartup方法,该方法代码如下

public final void onStartup(ServletContext servletContext) throws ServletException {
	//获取当前环境到底是一个filter 还是一个servlet 还是一个listener
	String description = getDescription();
	if (!isEnabled()) {
		logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
		return;
	}
	register(description, servletContext);
}

这边register(description, servletContext);会调用到DynamicRegistrationBeanregister方法,代码如下

protected final void register(String description, ServletContext servletContext) {
	D registration = addRegistration(description, servletContext);
	if (registration == null) {
		logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");
		return;
	}
	configure(registration);
}

addRegistration(description, servletContext)又会调用到ServletRegistrationBean中的addRegistration方法,代码如下

protected ServletRegistration.Dynamic addRegistration(String description, ServletContext servletContext) {
  String name = getServletName();
  return servletContext.addServlet(name, this.servlet);
}

看到了关键的servletContext.addServlet代码了把,我们通过调试,即可知到this.servlet就是dispatcherServlet

image.png

总结

SpringBoot自动装配SpringMvc其实就是往ServletContext中加入了一个Dispatcherservlet

Serclet3.0规范中有这个说明,除了可以动态加Servlet,还可以动态加Listener,Filter

  • addServlet
  • addListener
  • addFilter

到此为止,还有一个问题,那便是springboot是如何加装其他应用的呢?我们知道,比如我们要在项目里面使用Feign,我们只需要使用这样一个注解@EnableFeignClients.那么这是如何做到的呢?

其实这涉及到如何在springboot中自定义starter