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的注册Bean,DispatcherServletRegistrationBean
接下来看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);
}
我们看到,这里直接将DispatcherServlet给add到了servletContext当中。
小结:
总的来说,其实就是触发了初始化ServletContext时候的回调接口onStartup方法,而后直接将DispatcherServlet作为一个Servlet给add到ServletContext当中。
实现了ServletContextInitializer的实例DispatcherServletRegistrationBean,什么时候会调用onStartup方法呢?
在ServletWebServerApplicationContext中,在Servlet类型的Springboot Web应用中,ApplicationContext是AnnotationConfigServletWebServerApplicationContext(看过springboot嵌入式Servlet容器启动原理应该知道),而ServletWebServerApplicationContext正是其父类。
先是按照springboot嵌入式Servlet容器启动原理:启动类run–>好几个run–>refreshContext()–>refresh()–>AbstractApplicationContext的refresh()->onRefresh()转至ServletWebServerApplicationContext
ServletWebServerApplicationContext的onRefresh方法覆盖了AbstractApplicationContext的onRefresh方法,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类型的,还有Listener、Filter类型的,为什么呢,因为他们都需要动态添加到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);