但是在 SpringBoot 中,我们没有了 web.xml 文件,我们如何去配置一个 dispatcherservlet 呢? 其实 Servlet3.0 规范中规定,要添加一个 Servlet ,除了采用 xml 配置的方式,还有一种通过代码的 方式,伪代码如下 :
servletContext . addServlet ( name , this . servlet );那么也就是说,如果我们能动态往 web 容器中添加一个我们构造好的 dispatcherServlet 对象, 是不是就实现自动装配 SpringMVC 了
一. 自动配置dispatcherServlet和dispatcherServletRegistry
springboot 的自动配置基于 SPI 机制,实现自动配置的核心要点就是添加一个自动配置的类, SpringBoot MVC 的自动配置自然也是相同原理。 所以,先找到 springmvc 对应的自动配置类。
org.springframework.boot.autoconfigure.web.servlet.dispatcherServletAutoConf iguration
(一)dispatcherServletAutoConfiguration自动配置类
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(dispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class dispatcherServletAutoConfiguration {
1
、首先注意到,
@Configuration
表名这是一个配置类,将会被
spring
给解析。
2
、
@ConditionalOnWebApplication
意味着当时一个
web
项目,且是
Servlet
项目的时候才会被解
析。
3
、
@ConditionalOnClass
指明
dispatcherServlet
这个核心类必须存在才解析该类。
4
、
@AutoConfigureAfter
指明在
ServletWebServerFactoryAutoConfiguration
这个类之后再解
析,设定了一个顺序。
总的来说,这些注解表明了该自动配置类的会解析的前置条件需要满足。
其次,
dispatcherServletAutoConfiguration
类主要包含了两个内部类,分别是
1
、
dispatcherServletConfiguration
2
、
dispatcherServletRegistrationConfiguration
顾名思义,前者是配置
dispatcherServlet
,后者是配置
dispatcherServlet
的注册类。什么是注册
类?我们知道
Servlet
实例是要被添加(注册)到如
tomcat
这样的
ServletContext
里的,这样才能
够提供请求服务。所以,
dispatcherServletRegistrationConfiguration
将生成一个
Bean
,负责将
dispatcherServlet
给注册到
ServletContext
中。
(二)配置dispatcherServletConfiguration
我们先看看 dispatcherServletConfiguration 这个配置类@Configuration(proxyBeanMethods = false)
@Conditional(DefaultdispatcherServletCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties({ HttpProperties.class, WebMvcProperties.class })
protected static class dispatcherServletConfiguration {
@Conditional
指明了一个前置条件判断,由
DefaultdispatcherServletCondition
实现。主要是判
断了是否已经存在
dispatcherServlet
,如果没有才会触发解析。
@ConditionalOnClass
指明了当
ServletRegistration
这个类存在的时候才会触发解析,生成的
dispatcherServlet
才能注册到
ServletContext
中。
最后,
@EnableConfigrationProperties
将会从
application.properties
这样的配置文件中读取
spring.http
和
spring.mvc
前缀的属性生成配置对象
HttpProperties
和
WebMvcProperties
。
再看
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;
}
@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;
}
这个两个方法我们比较熟悉了,就是生成了
Bean
。
dispatcherServlet
方法将生成一个
dispatcherServlet
的
Bean
对象。比较简单,就是获取一个实
例,然后添加一些属性设置。
multipartResolver
方法主要是把你配置的
MultipartResolver
的
Bean
给重命名一下,防止你不是用
multipartResolver
这个名字作为
Bean
的名字。
(三)配置dispatcherServletRegistrationConfiguration
再看注册类的 Bean 配置@Configuration(proxyBeanMethods = false)
@Conditional(dispatcherServletRegistrationCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
@Import(dispatcherServletConfiguration.class)
protected static class dispatcherServletRegistrationConfiguration {
同样的,
@Conditional
有一个前置判断,
dispatcherServletRegistrationCondition
主要判断了该
注册类的
Bean
是否存在。
@ConditionOnClass
也判断了
ServletRegistration
是否存在
@EnableConfigurationProperties
生成了
WebMvcProperties
的属性对象
@Import
导入了
dispatcherServletConfiguration
,也就是我们上面的配置对象。
再看
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;
}
内部只有一个方法,生成了
dispatcherServletRegistrationBean
。核心逻辑就是实例化了一个
Bean
,设置了一些参数,如
dispatcherServlet
、
loadOnStartup
等
总结
springboot mvc
的自动配置类是
dispatcherServletAutoConfigration
,主要做了两件事:
1
)配置
dispatcherServlet
2
)配置
dispatcherServlet
的注册
Bean(dispatcherServletRegistrationBean)
二. 注册dispatcherServlet到ServletContext
在上一小节的源码翻阅中,我们看到了 dispatcherServlet 和 dispatcherServletRegistrationBean 这两个 Bean 的自动配置。 dispatcherServlet 我们很熟悉, dispatcherServletRegistrationBean 负 责将 dispatcherServlet 注册到 ServletContext 当中(一)dispatcherServletRegistrationBean的类图
既然该类的职责是负责注册 dispatcherServlet ,那么我们得知道什么时候触发注册操作。为此, 我们先看看 dispatcherServletRegistrationBean 这个类的类图(二)注册dispatcherServlet流程
1. ServletContextinitializer
我们看到,最上面是一个 ServletContextinitializer 接口。我们可以知道,实现该接口意味着是用 来初始化 ServletContext 的。我们看看该接口public interface ServletContextinitializer {
void onStartup(ServletContext servletContext) throws servletexception;
}
2. RegistrationBean
看看RegistrationBean是怎么实现onStartup方法的
@Override
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
方法,跟进它
3. DynamicRegistrationBean
再看 DynamicRegistrationBean 是怎么实现 register 方法的@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);
}
跟进
addRegistration
方法
protected abstract D addRegistration(String description, ServletContext servletContext);
4.ServletRegistrationBean
再看 ServletRegistrationBean 是怎么实现 addRegistration 方法的@Override
protected ServletRegistration.Dynamic addRegistration(String description, ServletContext servletContext) {
String name = getServletName();
//负责将dispatcherServlet注册到servletContext中
return servletContext.addServlet(name, this.servlet);
}
我们看到,这里直接将
dispatcherServlet
给
add
到了
servletContext
当中。
(三)SpringBoot启动流程中具体体现
这段代码其实就是去加载 SpringMVC ,那么他是如何做到的呢? getSelfInitializer() 最终会 去调用到 ServletWebServerApplicationContext 的 selfInitialize 方法,该方法代码如下getSelfInitializer().onStartup(servletContext);
private void selfInitialize(ServletContext servletContext) throws servletexception {
prepareWebApplicationContext(servletContext);
registerapplicationScope(servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(getbeanfactory(), servletContext);
for (ServletContextinitializer beans : getServletContextinitializerBeans()) {
beans.onStartup(servletContext);
}
}
我们通过调试,知道
getServletContextinitializerBeans()
返回的是一个
ServletContextinitializer
集合,集合中有以下几个对象
然后依次去调用对象的 onStartup 方法,那么对于上图标红的对象来说,就是会调用到 dispatcherServletRegistrationBean 的 onStartup 方法,这个类并没有这个方法,所以最终 会调用到父类 RegistrationBean 的 onStartup 方法,该方法代码如下
@Override
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)
;
会调用到
DynamicRegistrationBean
的
register
方法,代码如下:
@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);
}
addRegistration(description, servletContext)
又会调用到
ServletRegistrationBean
中的
addRegistration
方法,代码如下 :
@Override
protected ServletRegistration.Dynamic addRegistration(String description, ServletContext servletContext) {
String name = getServletName();
//负责将dispatcherServlet注册到servletContext中
return servletContext.addServlet(name, this.servlet);
}
看到了关键的
servletContext.addServlet
代码了,我们通过调试,即可知到
this.servlet
就
是
dispatcherServlet
总结 SpringBoot 自动装配 SpringMvc 其实就是往 ServletContext 中加入了一个 dispatcherservlet 。 Servlet3.0 规范中有这个说明,除了可以动态加 Servlet, 还可以动态加 Listener , Filter
- addServlet
- addListener
- addFilter
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。