SpringMVC源码分析之Servlet容器启动和Servlet组件注册
1.自动配置类 DispatcherServletAutoConfiguration
这个配置类中定义了两个bean
- DispatcherServlet 会拦截所有的请求,并调用相应的控制器处理
- DispatcherServletRegistrationBean 这个类的主要作用是想Servlet容器中注册Servlet组件
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());
return dispatcherServlet;
}
@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;
}
2.自动配置类 ServletWebServerFactoryAutoConfiguration
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class, //引入Tomcat容器配置
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,//引入Jetty容器配置
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
}
static class EmbeddedTomcat {
//创建Tomcat工厂
@Bean
TomcatServletWebServerFactory tomcatServletWebServerFactory(
ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
ObjectProvider<TomcatContextCustomizer> contextCustomizers,
ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
factory.getTomcatConnectorCustomizers()
.addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));
factory.getTomcatContextCustomizers()
.addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));
factory.getTomcatProtocolHandlerCustomizers()
.addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
return factory;
}
}
3.启动Tomcat容器
ioc容器创建时会调refresh方法,调用流程如下
AbstractApplicationContext#refresh()
=> ServletWebServerApplicationContext#onfrefresh()
=> createWebServer
WebServer创建流程:
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
ServletWebServerFactory factory = getWebServerFactory();
createWebServer.tag("factory", factory.getClass().toString());
//WebServer工厂创建WebServer
this.webServer = factory.getWebServer(getSelfInitializer());
createWebServer.end();
getBeanFactory().registerSingleton("webServerGracefulShutdown",
new WebServerGracefulShutdownLifecycle(this.webServer));
getBeanFactory().registerSingleton("webServerStartStop",
new WebServerStartStopLifecycle(this, this.webServer));
}
else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context", ex);
}
}
initPropertySources();
}
//向 Tomcat 中注册 Servlet Filter
//DispatcherServlet 和 CharacterEncodingFilter都是在这里注册到Servlet容器中的
private void selfInitialize(ServletContext servletContext) throws ServletException {
prepareWebApplicationContext(servletContext);
registerApplicationScope(servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);
}
}
TomcatServletWebServerFactory创建Tomcat服务流程
public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {
Registry.disableRegistry();
}
//创建Tomcat
Tomcat tomcat = new Tomcat();
File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
connector.setThrowOnFailure(true);
tomcat.getService().addConnector(connector);
customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
configureEngine(tomcat.getEngine());
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
prepareContext(tomcat.getHost(), initializers);
//启动Tomcat
return getTomcatWebServer(tomcat);
4.向Tomcat中注册Servlet、Filter
首先自定义一个Filter 和Servlet
public class MyFilter1 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("MyFilter1的初试化方法");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("MyFilter1的doFilter方法");
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
System.out.println("MyFilter1的destroy方法");
}
}
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("自定义Servlet doGet请求");
resp.setContentType("text/html");
ServletOutputStream outputStream = resp.getOutputStream();
outputStream.write("自定义Servlet doGet请求".getBytes(StandardCharsets.UTF_8));
outputStream.flush();
outputStream.close();
}
}
方式一:使用注解
启动类:
@ServletComponentScan
自定义Servlet
@WebServlet(name = "myServlet",urlPatterns = "/myServlet")
public class MyServlet extends HttpServlet {
自定义Filter
@WebFilter(filterName = "myFilter1",urlPatterns = "/myServlet")
public class MyFilter1 implements Filter {
原理分析:
方式二:使用RegistrationBean进行注册
//注册Filter
@Bean
public FilterRegistrationBean<MyFilter1> myFilter(){
FilterRegistrationBean<MyFilter1> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new MyFilter1()); //设置自定义Filter
filterRegistrationBean.setUrlPatterns(Arrays.asList("/myServlet")); //设置自定义Filter 匹配路径
return filterRegistrationBean;
}
@Bean
public ServletRegistrationBean<MyServlet> myServletRegistration() {
//设置定义Servlet
ServletRegistrationBean<MyServlet> registration = new ServletRegistrationBean<>(new MyServlet(),"/myServlet");
registration.setName("myServlet");
return registration;
}
注册原理分析:
1.从IOC容器中获取全部的ServletContextInitializer接口的实例对象
RegistrationBean 类实现了ServletContextInitializer接口,它的作用是向Servlet容器注册Servlet 、Filter和Listener组件。ServletContextInitializer 接口中有一个onStartup 方法,Servlet容器启动后会回调这个方法,使用这个回调方法就可以将Servlet 和Filter 组件注册到Servlet容器中。
FilterRegistrationBean 注册Filter DispatcherServletRegistrationBean 注册DispatcherServlet ServletRegistrationBean 注册Servlet ServletListenerRegistrationBean 注册Listener
Springboot创建tomcat容器是在ServletWebServerApplicationContext#createWebServer方法中进行,通过调用getSelfInitializer方法来获取对应的ServletContextInitializer实例对象:
private void selfInitialize(ServletContext servletContext) throws ServletException {
prepareWebApplicationContext(servletContext);
registerApplicationScope(servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
//调用 ServletContextInitializer 的onStartup方法
beans.onStartup(servletContext);
}
}
ServletWebServerApplicationContext#getServletContextInitializerBeans
protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
return new ServletContextInitializerBeans(getBeanFactory());
}
ServletContextInitializerBeans是一个从容器ListableBeanFactory中获取的ServletContextInitializer实例对象集合,包含所有的ServletContextInitializer beans对象,Servlet、Filter、EventListener监听器bean。
ServletContextInitializerBeans#ServletContextInitializerBeans
public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
Class<? extends ServletContextInitializer>... initializerTypes) {
this.initializers = new LinkedMultiValueMap<>();
this.initializerTypes = (initializerTypes.length != 0) ? Arrays.asList(initializerTypes)
: Collections.singletonList(ServletContextInitializer.class);
//将容器中的ServletContextInitializer实例对象按照类别加入到集合
addServletContextInitializerBeans(beanFactory);
//将容器中的Servlet、Filter、EventListenr实例对象加入集合
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);
}
@SuppressWarnings("unchecked")
protected void addAdaptableBeans(ListableBeanFactory beanFactory) {
MultipartConfigElement multipartConfig = getMultipartConfig(beanFactory);
//将容器中的Servlet实例对象转换为RegistrationBean实例对象加入ServletContextInitializer集合
addAsRegistrationBean(beanFactory, Servlet.class, new ServletRegistrationBeanAdapter(multipartConfig));
//将容器中的Filter实例对象转换为RegistrationBean实例对象加入ServletContextInitializer集合
addAsRegistrationBean(beanFactory, Filter.class, new FilterRegistrationBeanAdapter());
for (Class<?> listenerType : ServletListenerRegistrationBean.getSupportedTypes()) {
//将容器中的EventListener实例对象转换为RegistrationBean实例对象加入ServletContextInitializer集合
addAsRegistrationBean(beanFactory, EventListener.class, (Class<EventListener>) listenerType,
new ServletListenerRegistrationBeanAdapter());
}
}
//将ServletContextInitializer、Filter、Servlet、EventListener类的实例对象加入到集合中
private void addServletContextInitializerBean(String beanName, ServletContextInitializer initializer,
ListableBeanFactory beanFactory) {
if (initializer instanceof ServletRegistrationBean) {
Servlet source = ((ServletRegistrationBean<?>) initializer).getServlet();
//将Servlet加入集合
addServletContextInitializerBean(Servlet.class, beanName, initializer, beanFactory, source);
}
else if (initializer instanceof FilterRegistrationBean) {
Filter source = ((FilterRegistrationBean<?>) initializer).getFilter();
//将Filter加入集合
addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source);
}
else if (initializer instanceof DelegatingFilterProxyRegistrationBean) {
String source = ((DelegatingFilterProxyRegistrationBean) initializer).getTargetBeanName();
//将Servlet加入集合
addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source);
}
else if (initializer instanceof ServletListenerRegistrationBean) {
EventListener source = ((ServletListenerRegistrationBean<?>) initializer).getListener();
//将EventListener加入集合
addServletContextInitializerBean(EventListener.class, beanName, initializer, beanFactory, source);
}
else {
//将ServletContextInitializer加入集合
addServletContextInitializerBean(ServletContextInitializer.class, beanName, initializer, beanFactory,
initializer);
}
}
2.注册组件
遍历所有的ServletContextInitializer 接口的实例对象,并调用onStartup 方法。
RegistrationBean#onStartup
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);
}
DynamicRegistrationBean#register
protected final void register(String description, ServletContext servletContext) {
//将组件添加到ServletContext 中
D registration = addRegistration(description, servletContext);
if (registration == null) {
logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");
return;
}
//配置请求路径
// DispatcherServlet 和 CharacterEncodingFilter 请求路径都是在这里拦截的
configure(registration);
}
@Override
protected void configure(ServletRegistration.Dynamic registration) {
super.configure(registration);
String[] urlMapping = StringUtils.toStringArray(this.urlMappings);
if (urlMapping.length == 0 && this.alwaysMapUrl) {
urlMapping = DEFAULT_MAPPINGS;
}
if (!ObjectUtils.isEmpty(urlMapping)) {
registration.addMapping(urlMapping);
}
registration.setLoadOnStartup(this.loadOnStartup);
if (this.multipartConfig != null) {
registration.setMultipartConfig(this.multipartConfig);
}
}