Springboot2.x之自动配置DispatcherServlet

https://www.liangzl.com/get-article-detail-133970.html

Springboot中我们引入spring-boot-starter-web依赖后,web就自动配置好了,在web.xml的年代,我们需要在web.xml中手动配置DispatcherServlet,但是Springboot中不需要,Springboot是如何替我们做好这一切的呢?

看到类DispatcherServletAutoConfiguration

DispatcherServletAutoConfiguration类主要包含了两个内部类,分别是
1、DispatcherServletConfiguration
2、DispatcherServletRegistrationConfiguration

顾名思义,前者是配置DispatcherServlet,后者是配置DispatcherServlet的注册类。什么是注册类?我们知道Servlet实例是要被添加(注册)到如tomcat这样的ServletContext里的,这样才能够提供请求服务。所以,DispatcherServletRegistrationConfiguration将生成一个Bean,负责将DispatcherServlet给注册到ServletContext中。

spring boot中注册Servlet的两种方式:一种是使用@WebServlet注解,这个是javaee的注解,是servlet3.0以后提供的。spring boot会扫描这个注解,并将这个注解注解的类注册到web容器中作为一个servlet。另一种是使用ServletRegistrationBean,这个bean是由spring boot提供专门来注册servlet的,可以象注册bean一样去配置servlet。

在内部类DispatcherServletConfiguration中:往ioc容器中注入DispatcherServlet 
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet(HttpProperties httpProperties, WebMvcProperties webMvcProperties) {
	DispatcherServlet dispatcherServlet = new DispatcherServlet();
	dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
	dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
	dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
	dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
	dispatcherServlet.setEnableLoggingRequestDetails(httpProperties.isLogRequestDetails());
	return dispatcherServlet;
}

在内部类DispatcherServletRegistrationConfiguration中:往ioc容器中注入DispatcherServletRegistrationBean 
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
		WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
	DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
			webMvcProperties.getServlet().getPath());
	registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
	registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
	multipartConfig.ifAvailable(registration::setMultipartConfig);
	return registration;
}

小结:
springboot mvc的自动配置类是DispatcherServletAutoConfigration,主要做了两件事:
1)配置DispatcherServlet
2)配置DispatcherServlet的注册BeanDispatcherServletRegistrationBean


接下来看DispatcherServletRegistrationBean

类图:
在这里插入图片描述
我们看到ServletContextInitializer接口。我们可以知道,实现该接口意味着是用来初始化ServletContext的。我们看看该接口:
ServletContextInitializer

public interface ServletContextInitializer {
    void onStartup(ServletContext servletContext) throws ServletException;
}

RegistrationBean:看看RegistrationBean是怎么实现onStartup方法的,调用了内部register方法,是一个抽象方法,留给子类实现

@Override
public final void onStartup(ServletContext servletContext) throws ServletException {
    String description = getDescription();
    if (!isEnabled()) {
        logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
        return;
    }
    register(description, servletContext);
}
protected abstract void register(String description, ServletContext servletContext);

DynamicRegistrationBean:再看DynamicRegistrationBean是怎么实现register方法的,调用了内部addRegistration方法,是一个抽象方法,留给子类实现

@Override
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);
}
protected abstract D addRegistration(String description, ServletContext servletContext);

ServletRegistrationBean:再看ServletRegistrationBean是怎么实现addRegistration方法的

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

我们看到,这里直接将DispatcherServletadd到了servletContext当中。

小结:
总的来说,其实就是触发了初始化ServletContext时候的回调接口onStartup方法,而后直接将DispatcherServlet作为一个ServletaddServletContext当中。


实现了ServletContextInitializer的实例DispatcherServletRegistrationBean,什么时候会调用onStartup方法呢?

ServletWebServerApplicationContext中,在Servlet类型的Springboot Web应用中,ApplicationContextAnnotationConfigServletWebServerApplicationContext(看过springboot嵌入式Servlet容器启动原理应该知道),而ServletWebServerApplicationContext正是其父类。

先是按照springboot嵌入式Servlet容器启动原理:启动类run–>好几个run–>refreshContext()–>refresh()–>AbstractApplicationContextrefresh()->onRefresh()转至ServletWebServerApplicationContext

ServletWebServerApplicationContextonRefresh方法覆盖了AbstractApplicationContextonRefresh方法,AbstractApplicationContext中,方法onRefresh被方法refresh调用。

ServletWebServerApplicationContext中,方法onRefresh()–>createWebServer()–>getWebServer()(创建了servlet容器对象new Tomcat())->getSelfInitializer()–>selfInitialize()
重点看selfInitialize方法:通过方法getServletContextInitializerBeans获取所有的ServletContextInitializer 并分别执行它们的onStartup方法。

private void selfInitialize(ServletContext servletContext) throws ServletException {
	prepareWebApplicationContext(servletContext);
	registerApplicationScope(servletContext);
	WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
	for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
		beans.onStartup(servletContext);
	}
}
protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
	return new ServletContextInitializerBeans(getBeanFactory());
}

小结:
实现了ServletContextInitializer的实例DispatcherServletRegistrationBean,会在创建servlet容器之前调用onStartup方法,不止是DispatcherServletRegistrationBean,所有实现了ServletContextInitializer的实例,都会在这里执行onStartup方法。

这个ServletContextInitializerBeans是个啥呢?


ServletContextInitializerBeans类

public class ServletContextInitializerBeans extends AbstractCollection<ServletContextInitializer> 

可以看到ServletContextInitializerBeans其实是一个类型为ServletContextInitializer的集合。它实例化的时候会从ioc容器中获取所有的ServletContextInitializer的集合。在这个类可以看到,实现了ServletContextInitializer的不止是Servlet类型的,还有ListenerFilter类型的,为什么呢,因为他们都需要动态添加到web容器中,即需要ServletContext。
构造方法:

public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
			Class<? extends ServletContextInitializer>... initializerTypes) {
		this.initializers = new LinkedMultiValueMap<>();
		this.initializerTypes = (initializerTypes.length != 0) ? Arrays.asList(initializerTypes)
				: Collections.singletonList(ServletContextInitializer.class);
		// 完全缺省配置的Springboot Web应用,其实只会有一个 : 
		// 注册 dispatcherServlet的dispatcherServletRegistration
		addServletContextInitializerBeans(beanFactory);
		addAdaptableBeans(beanFactory);
		List<ServletContextInitializer> sortedInitializers = this.initializers.values().stream()
				.flatMap((value) -> value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE))
				.collect(Collectors.toList());
		this.sortedList = Collections.unmodifiableList(sortedInitializers);
		logMappings(this.initializers);
	}
private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) {
	for (Class<? extends ServletContextInitializer> initializerType : this.initializerTypes) {
		for (Entry<String, ? extends ServletContextInitializer> initializerBean : getOrderedBeansOfType(beanFactory,
				initializerType)) {
			//将ServletContextInitializer添加到属性initializers(是一个map)中
			addServletContextInitializerBean(initializerBean.getKey(), initializerBean.getValue(), beanFactory);
		}
	}
}
//从容器中获取所有的ServletContextInitializer
private <T> List<Entry<String, T>> getOrderedBeansOfType(ListableBeanFactory beanFactory, Class<T> type) {
	return getOrderedBeansOfType(beanFactory, type, Collections.emptySet());
}
	private void addServletContextInitializerBean(String beanName, ServletContextInitializer initializer,
			ListableBeanFactory beanFactory) {
		if (initializer instanceof ServletRegistrationBean) {
			Servlet source = ((ServletRegistrationBean<?>) initializer).getServlet();
			addServletContextInitializerBean(Servlet.class, beanName, initializer, beanFactory, source);
		}
		else if (initializer instanceof FilterRegistrationBean) {
			Filter source = ((FilterRegistrationBean<?>) initializer).getFilter();
			addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source);
		}
		else if (initializer instanceof DelegatingFilterProxyRegistrationBean) {
			String source = ((DelegatingFilterProxyRegistrationBean) initializer).getTargetBeanName();
			addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source);
		}
		else if (initializer instanceof ServletListenerRegistrationBean) {
			EventListener source = ((ServletListenerRegistrationBean<?>) initializer).getListener();
			addServletContextInitializerBean(EventListener.class, beanName, initializer, beanFactory, source);
		}
		else {
			addServletContextInitializerBean(ServletContextInitializer.class, beanName, initializer, beanFactory,
					initializer);
		}
	}
-----------------------------------------------------------------------------------
	private void addServletContextInitializerBean(Class<?> type, String beanName, ServletContextInitializer initializer,
			ListableBeanFactory beanFactory, Object source) {
		this.initializers.add(type, initializer);

版权声明:本文为weixin_42412601原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。