承接上文。
在上一篇文章中,我们介绍到了SpringBoot是如何整合Tomcat的。我们知道在SpringBoot项目中可以使用SpringMVC的注解,比如@Controller和@RequestMapping注解。既然我们没有配置SpringMVC那么mvc的功能又是怎么起作用呢?
自动配置(一) 自动配置DispacherServlet和DispacherServletRegistration
首先找到SpringMVC的自动配置类
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration
DispatcherServletAutoConfiguration.class
首先解释一下该类上的注解:根据行号来解释
1.@AutoConfigurationOrder:自动加载时配置该类的顺序
2.@Configuration:表示这是一个配置类
3.@ConditionalOnWebApplication:自动配置该类需要满足当前上下文环境是SERVLET环境
4.@ConditionalOnClass:classpath路径下需要存在DispacherServlet类
5.@AutoConfigureAfter: 在ServletWebServerFactoryAutoConfiguration后配置,这个ServletWebServerFactoryAutoConfiguation就是我们前面文章说的Tomcat的自动配置类
DispatcherServletAutoConfiguration类中有两个内部类分别是:DispatcherServletConfiguration、DispatcherServletRegistrationConfiguration
DispacherServletConfiguration.class
@Configuration(proxyBeanMethods = false)
@Conditional(DefaultDispatcherServletCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties({ HttpProperties.class, WebMvcProperties.class })
protected static class DispatcherServletConfiguration {
@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;
}
// 这个类的主要作用是把用户自己配置的MultipartResolver的名称给配置一下
// 因为用户自己配置的类名称可能不叫multipartResolver
// 简单来说就是为了统一MultipartResolver在IOC容器中的bean名称叫 multipartResolver
@Bean
@ConditionalOnBean(MultipartResolver.class)
@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
public MultipartResolver multipartResolver(MultipartResolver resolver) {
// Detect if the user has created a MultipartResolver but named it incorrectly
return resolver;
}
1.@Conditional指明了一个前置条件判断,由DefaultDispatcherServletCondition实现。主要是判
断了是否已经存在DispatcherServlet,如果没有才会触发解析。
2.@ConditionalOnClass指明了当ServletRegistration这个类存在的时候才会触发解析,生成的
DispatcherServlet才能注册到ServletContext中。
3.最后,@EnableConfigrationProperties将会从application.properties这样的配置文件中读取
spring.http和spring.mvc前缀的属性生成配置对象HttpProperties和WebMvcProperties。
由此可见DispatcherServletConfiguration .class 主要作用就是向容器中注册一个DispacherServlet
DispatcherServletRegistrationConfiguration
@Configuration(proxyBeanMethods = false)
@Conditional(DispatcherServletRegistrationCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
@Import(DispatcherServletConfiguration.class)
protected static class DispatcherServletRegistrationConfiguration {
@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;
}
}
同样的,@Conditional有一个前置判断,DispatcherServletRegistrationCondition主要判断了该
注册类的Bean是否存在。
@ConditionOnClass也判断了ServletRegistration是否存在
@EnableConfigurationProperties生成了WebMvcProperties的属性对象
@Import导入了DispatcherServletConfiguration,也就是我们上面的配置对象。
内部只有一个方法,生成了DispatcherServletRegistrationBean对象。
DispatcherServletConfiguration是一个注册类,所以DispatcherServletConfiguration的作用就是生成一个DispatcherServletRegistrationBean并向SevletContext中注册DispacherServlet
小结
springboot mvc的自动配置类是DispatcherServletAutoConfigration,主要做了两件事:
1)配置DispatcherServlet
2)配置DispatcherServlet的注册Bean(DispatcherServletRegistrationBean)
自动配置(二)注册DispatcherServlet到ServletContext
上面我们已经看到DispacherServlet和DispacherServletRegistrationBean注册到了IOC容器中,并在BeanFactoryPostProcessor中进行加载注册。那么现在的疑问是,DispacherServlet是在哪里被注册到ServletContext中的?下面我们就来看DispacherServlet在哪里注册的?
DispacherServletRegistrationBean的类图
DispacherServlet注册流程
根据上一篇文章我们知道注册Tomcat的位置。
现在我们来到**ServletWevServerApplicationContext#createWebServer()**方法中
// 方法1
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
ServletWebServerFactory factory = getWebServerFactory();
// 1. getWebServer()的参数有一个getSelfInitializer()
this.webServer = factory.getWebServer(getSelfInitializer());
}
else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context", ex);
}
}
initPropertySources();
}
进入到**getSelfInitializer()**方法里面
// 方法2
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
return this::selfInitialize;
}
// 上面的方法又调用了下面的这个方法
// 方法3
private void selfInitialize(ServletContext servletContext) throws ServletException {
prepareWebApplicationContext(servletContext);
registerApplicationScope(servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
// 2. 这里调用了getServletContextInitializerBeans(),继续进入
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);
}
}
// 继续进入
// 方法4
protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
return new ServletContextInitializerBeans(getBeanFactory());
}
// 方法5
public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
Class<? extends ServletContextInitializer>... initializerTypes) {
this.initializers = new LinkedMultiValueMap<>();
this.initializerTypes = (initializerTypes.length != 0) ? Arrays.asList(initializerTypes)
: Collections.singletonList(ServletContextInitializer.class);
// 3. 点击继续这个方法里面
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);
}
// 方法6
private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) {
for (Class<? extends ServletContextInitializer> initializerType : this.initializerTypes) {
for (Entry<String, ? extends ServletContextInitializer> initializerBean : getOrderedBeansOfType(beanFactory,
initializerType)) {
addServletContextInitializerBean(initializerBean.getKey(), initializerBean.getValue(), beanFactory);
}
}
}
进入方法的调用流程已经标了注释
断点调试放在方法6处:
可以看到当前initializerBean的类型就是我们上面说自动配置时注册的DispacherServletRegistrationBean
回到方法3处:
他在for循环中调用了onStartUp方法,我们进去看一下。
根据上面的ServletContextInitializer类图进入RegistrationBean
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方法
register(description, servletContext);
}
register方法又是一个模板方法,找到他的实现类DynamicRegistrationBean,进入
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
我们看到了servletContext.addServlet(name, this.servlet),这就是真正的将DispacherServlet注册进ServletContext中的方法。this.servlet就是我们获取到的DispacherServlet。
小结
SpringBoot自动装配SpringMvc其实就是往ServletContext中加入了一个 Dispatcherservlet 。
Servlet3.0规范中有这个说明,除了可以动态加Servlet,还可以动态加Listener,Filter
addServlet
addListener
addFilter
谢谢!!!!