上一篇我们讲了SpringBoot中Tomcat的启动过程,本篇我们接着讲在SpringBoot中如何向Tomcat中添加Servlet、Filter、Listener

自定义Servlet、Filter、Listener

Spring容器中声明ServletRegistrationBean、FilterRegistrationBean、ServletListenerRegistrationBean

@Bean
public ServletRegistrationBean customServlet() {
return new ServletRegistrationBean(new CustomServlet(), "/custom");
} private static class CustomServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("receive by custom servlet");
}
}

先自定义一个Servlet,重写service实现自己的业务逻辑,然后通过@Bean注解往Spring容器中注入一个ServletRegistrationBean类型的bean实例,并且实例化一个自定义的Servlet作为参数,这样就将自定义的Servlet加入Tomcat中了。

@ServletComponentScan注解和@WebServlet、@WebFilter以及@WebListener注解配合使用

@ServletComponentScan注解启用ImportServletComponentScanRegistrar类,是个ImportBeanDefinitionRegistrar接口的实现类,会被Spring容器所解析。ServletComponentScanRegistrar内部会解析@ServletComponentScan注解,然后会在Spring容器中注册ServletComponentRegisteringPostProcessor,是个BeanFactoryPostProcessor,会去解析扫描出来的类是不是有@WebServlet、@WebListener、@WebFilter这3种注解,有的话把这3种类型的类转换成ServletRegistrationBean、FilterRegistrationBean或者ServletListenerRegistrationBean,然后让Spring容器去解析:

@SpringBootApplication
@ServletComponentScan
public class EmbeddedServletApplication {
...
} @WebServlet(urlPatterns = "/simple")
public class SimpleServlet extends HttpServlet { @Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("receive by SimpleServlet");
}
}

在Spring容器中声明Servlet、Filter或者Listener

@Bean(name = "dispatcherServlet")
public DispatcherServlet myDispatcherServlet() {
return new DispatcherServlet();
}

我们发现往Tomcat中添加Servlet、Filter或者Listener还是挺容易的,大家还记得以前SpringMVC是怎么配置DispatcherServlet的吗?在web.xml中:

<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

和我们SpringBoot中配置Servlet相比是不是复杂很多,虽然SpringBoot中自定义Servlet很简单,但是其底层却不简单,下面我们来分析一下其原理

ServletRegistrationBean、FilterRegistrationBean、ServletListenerRegistrationBean

我们来看看这几个特殊的类:

ServletRegistrationBean

public class ServletRegistrationBean extends RegistrationBean {
//存放目标Servlet实例
private Servlet servlet;
//存放Servlet的urlMapping
private Set<String> urlMappings;
private boolean alwaysMapUrl;
private int loadOnStartup;
private MultipartConfigElement multipartConfig; public ServletRegistrationBean(Servlet servlet, String... urlMappings) {
this(servlet, true, urlMappings);
} public ServletRegistrationBean(Servlet servlet, boolean alwaysMapUrl, String... urlMappings) {
this.urlMappings = new LinkedHashSet();
this.alwaysMapUrl = true;
this.loadOnStartup = -1;
Assert.notNull(servlet, "Servlet must not be null");
Assert.notNull(urlMappings, "UrlMappings must not be null");
this.servlet = servlet;
this.alwaysMapUrl = alwaysMapUrl;
this.urlMappings.addAll(Arrays.asList(urlMappings));
} public void onStartup(ServletContext servletContext) throws ServletException {
Assert.notNull(this.servlet, "Servlet must not be null");
String name = this.getServletName();
if (!this.isEnabled()) {
logger.info("Servlet " + name + " was not registered (disabled)");
} else {
logger.info("Mapping servlet: '" + name + "' to " + this.urlMappings);
Dynamic added = servletContext.addServlet(name, this.servlet);
if (added == null) {
logger.info("Servlet " + name + " was not registered (possibly already registered?)");
} else {
this.configure(added);
}
}
} //略
}

在我们例子中我们通过return new ServletRegistrationBean(new CustomServlet(), "/custom");就知道,ServletRegistrationBean里会存放目标Servlet实例和urlMapping,并且继承RegistrationBean这个类

FilterRegistrationBean

public class FilterRegistrationBean extends AbstractFilterRegistrationBean {
//存放目标Filter对象
private Filter filter;
public FilterRegistrationBean() {
super(new ServletRegistrationBean[0]);
} public FilterRegistrationBean(Filter filter, ServletRegistrationBean... servletRegistrationBeans) {
super(servletRegistrationBeans);
Assert.notNull(filter, "Filter must not be null");
this.filter = filter;
} public Filter getFilter() {
return this.filter;
} public void setFilter(Filter filter) {
Assert.notNull(filter, "Filter must not be null");
this.filter = filter;
}
} abstract class AbstractFilterRegistrationBean extends RegistrationBean {
private static final EnumSet<DispatcherType> ASYNC_DISPATCHER_TYPES;
private static final EnumSet<DispatcherType> NON_ASYNC_DISPATCHER_TYPES;
private static final String[] DEFAULT_URL_MAPPINGS;
private Set<ServletRegistrationBean> servletRegistrationBeans = new LinkedHashSet();
private Set<String> servletNames = new LinkedHashSet();
private Set<String> urlPatterns = new LinkedHashSet();
//重写onStartup方法
public void onStartup(ServletContext servletContext) throws ServletException {
Filter filter = this.getFilter();
Assert.notNull(filter, "Filter must not be null");
String name = this.getOrDeduceName(filter);
if (!this.isEnabled()) {
this.logger.info("Filter " + name + " was not registered (disabled)");
} else {
Dynamic added = servletContext.addFilter(name, filter);
if (added == null) {
this.logger.info("Filter " + name + " was not registered (possibly already registered?)");
} else {
this.configure(added);
}
}
}
//略...
}

我们看到FilterRegistrationBean 中也保存了目标Filter对象,并且继承了RegistrationBean

ServletListenerRegistrationBean

public class ServletListenerRegistrationBean<T extends EventListener> extends RegistrationBean {
//存放了目标listener
private T listener; public ServletListenerRegistrationBean() {
} public ServletListenerRegistrationBean(T listener) {
Assert.notNull(listener, "Listener must not be null");
Assert.isTrue(isSupportedType(listener), "Listener is not of a supported type");
this.listener = listener;
} public void setListener(T listener) {
Assert.notNull(listener, "Listener must not be null");
Assert.isTrue(isSupportedType(listener), "Listener is not of a supported type");
this.listener = listener;
} public void onStartup(ServletContext servletContext) throws ServletException {
if (!this.isEnabled()) {
logger.info("Listener " + this.listener + " was not registered (disabled)");
} else {
try {
servletContext.addListener(this.listener);
} catch (RuntimeException var3) {
throw new IllegalStateException("Failed to add listener '" + this.listener + "' to servlet context", var3);
}
}
}
//略...
}

ServletListenerRegistrationBean也是一样,那我们来看看RegistrationBean这个类

public abstract class RegistrationBean implements ServletContextInitializer, Ordered {
...
}
public interface ServletContextInitializer {
void onStartup(ServletContext var1) throws ServletException;
}

我们发现RegistrationBean 实现了ServletContextInitializer这个接口,并且有一个onStartup方法,ServletRegistrationBean、FilterRegistrationBean、ServletListenerRegistrationBean都实现了onStartup方法。

ServletContextInitializer是 Servlet 容器初始化的时候,提供的初始化接口。所以,Servlet 容器初始化会获取并触发所有的FilterRegistrationBean、FilterRegistrationBean、ServletListenerRegistrationBean实例中onStartup方法

那到底是何时触发这些类的onStartup方法呢?

当Tomcat容器启动时,会执行callInitializers,然后获取所有的ServletContextInitializer,循环执行onStartup方法触发回调方法。那FilterRegistrationBean、FilterRegistrationBean、ServletListenerRegistrationBean实例是何时加入到Initializers集合的呢?这要回顾一下我们上一篇文章Tomcat的启动过程

Servlet容器的启动

大家可以看看我上一篇文章,我这里简单的复制一下代码

EmbeddedWebApplicationContext

 @Override
protected void onRefresh() {
super.onRefresh();
try {
//核心方法:会获取嵌入式的Servlet容器工厂,并通过工厂来获取Servlet容器
createEmbeddedServletContainer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start embedded container", ex);
}
} private void createEmbeddedServletContainer() {
EmbeddedServletContainer localContainer = this.embeddedServletContainer;
ServletContext localServletContext = getServletContext();
if (localContainer == null && localServletContext == null) {
//先获取嵌入式Servlet容器工厂
EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();
//根据容器工厂来获取对应的嵌入式Servlet容器
this.embeddedServletContainer = containerFactory.getEmbeddedServletContainer(getSelfInitializer());
}
else if (localServletContext != null) {
try {
getSelfInitializer().onStartup(localServletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context",ex);
}
}
initPropertySources();
}

关键代码在第20行,先通过getSelfInitializer()获取到所有的Initializer,传入Servlet容器中,那核心就在getSelfInitializer()方法:

 private ServletContextInitializer getSelfInitializer() {
//只是创建了一个ServletContextInitializer实例返回
//所以Servlet容器启动的时候,会调用这个对象的onStartup方法
return new ServletContextInitializer() {
public void onStartup(ServletContext servletContext) throws ServletException {
EmbeddedWebApplicationContext.this.selfInitialize(servletContext);
}
};
}

我们看到只是创建了一个ServletContextInitializer实例返回,所以Servlet容器启动的时候,会调用这个对象的onStartup方法,那我们来分析其onStartup中的逻辑,也就是selfInitialize方法,并将Servlet上下文对象传进去了

selfInitialize

 private void selfInitialize(ServletContext servletContext) throws ServletException {
prepareWebApplicationContext(servletContext);
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
ExistingWebApplicationScopes existingScopes = new ExistingWebApplicationScopes(beanFactory);
WebApplicationContextUtils.registerWebApplicationScopes(beanFactory,getServletContext());
existingScopes.restore();
WebApplicationContextUtils.registerEnvironmentBeans(beanFactory,getServletContext());
//这里便是获取所有的 ServletContextInitializer 实现类,会获取所有的注册组件
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
//执行所有ServletContextInitializer的onStartup方法
beans.onStartup(servletContext);
}
}

关键代码在第9和第11行,先获取所有的ServletContextInitializer 实现类,然后遍历执行所有ServletContextInitializer的onStartup方法

获取所有的ServletContextInitializer

我们来看看getServletContextInitializerBeans方法

protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
return new ServletContextInitializerBeans(getBeanFactory());
}

ServletContextInitializerBeans对象是对ServletContextInitializer的一种包装:

 public class ServletContextInitializerBeans extends AbstractCollection<ServletContextInitializer> {
private final MultiValueMap<Class<?>, ServletContextInitializer> initializers = new LinkedMultiValueMap();
//存放所有的ServletContextInitializer
private List<ServletContextInitializer> sortedList;

public ServletContextInitializerBeans(ListableBeanFactory beanFactory) {
//执行addServletContextInitializerBeans
this.addServletContextInitializerBeans(beanFactory);
//执行addAdaptableBeans
this.addAdaptableBeans(beanFactory);
List<ServletContextInitializer> sortedInitializers = new ArrayList();
Iterator var3 = this.initializers.entrySet().iterator(); while(var3.hasNext()) {
Entry<?, List<ServletContextInitializer>> entry = (Entry)var3.next();
AnnotationAwareOrderComparator.sort((List)entry.getValue());
sortedInitializers.addAll((Collection)entry.getValue());
}
this.sortedList = Collections.unmodifiableList(sortedInitializers);
}

private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) {
Iterator var2 = this.getOrderedBeansOfType(beanFactory, ServletContextInitializer.class).iterator(); while(var2.hasNext()) {
Entry<String, ServletContextInitializer> initializerBean = (Entry)var2.next();
this.addServletContextInitializerBean((String)initializerBean.getKey(), (ServletContextInitializer)initializerBean.getValue(), beanFactory);
} } private void addServletContextInitializerBean(String beanName, ServletContextInitializer initializer, ListableBeanFactory beanFactory) {
if (initializer instanceof ServletRegistrationBean) {
Servlet source = ((ServletRegistrationBean)initializer).getServlet();
this.addServletContextInitializerBean(Servlet.class, beanName, initializer, beanFactory, source);
} else if (initializer instanceof FilterRegistrationBean) {
Filter source = ((FilterRegistrationBean)initializer).getFilter();
this.addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source);
} else if (initializer instanceof DelegatingFilterProxyRegistrationBean) {
String source = ((DelegatingFilterProxyRegistrationBean)initializer).getTargetBeanName();
this.addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source);
} else if (initializer instanceof ServletListenerRegistrationBean) {
EventListener source = ((ServletListenerRegistrationBean)initializer).getListener();
this.addServletContextInitializerBean(EventListener.class, beanName, initializer, beanFactory, source);
} else {
this.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);
if (source != null) {
this.seen.add(source);
} if (logger.isDebugEnabled()) {
String resourceDescription = this.getResourceDescription(beanName, beanFactory);
int order = this.getOrder(initializer);
logger.debug("Added existing " + type.getSimpleName() + " initializer bean '" + beanName + "'; order=" + order + ", resource=" + resourceDescription);
} } private void addAdaptableBeans(ListableBeanFactory beanFactory) {
MultipartConfigElement multipartConfig = this.getMultipartConfig(beanFactory);
this.addAsRegistrationBean(beanFactory, Servlet.class, new ServletContextInitializerBeans.ServletRegistrationBeanAdapter(multipartConfig));
this.addAsRegistrationBean(beanFactory, Filter.class, new ServletContextInitializerBeans.FilterRegistrationBeanAdapter(null));
Iterator var3 = ServletListenerRegistrationBean.getSupportedTypes().iterator(); while(var3.hasNext()) {
Class<?> listenerType = (Class)var3.next();
this.addAsRegistrationBean(beanFactory, EventListener.class, listenerType, new ServletContextInitializerBeans.ServletListenerRegistrationBeanAdapter(null));
} } public Iterator<ServletContextInitializer> iterator() {
//返回所有的ServletContextInitializer
return this.sortedList.iterator();
} //略...
}

我们看到ServletContextInitializerBeans 中有一个存放所有ServletContextInitializer的集合sortedList,就是在其构造方法中获取所有的ServletContextInitializer,并放入sortedList集合中,那我们来看看其构造方法的逻辑,看到第8行先调用

addServletContextInitializerBeans方法:  

 private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) {
//从Spring容器中获取所有ServletContextInitializer.class 类型的Bean
for (Entry<String, ServletContextInitializer> initializerBean : getOrderedBeansOfType(beanFactory, ServletContextInitializer.class)) {
//添加到具体的集合中
addServletContextInitializerBean(initializerBean.getKey(),initializerBean.getValue(), beanFactory);
}
}

我们看到先从Spring容器中获取所有ServletContextInitializer.class 类型的Bean,这里我们自定义的ServletRegistrationBean、FilterRegistrationBean、ServletListenerRegistrationBean就被获取到了,然后调用addServletContextInitializerBean方法:

 private void addServletContextInitializerBean(String beanName, ServletContextInitializer initializer, ListableBeanFactory beanFactory) {
//判断ServletRegistrationBean类型
if (initializer instanceof ServletRegistrationBean) {
Servlet source = ((ServletRegistrationBean)initializer).getServlet();
//将ServletRegistrationBean加入到集合中
this.addServletContextInitializerBean(Servlet.class, beanName, initializer, beanFactory, source);
//判断FilterRegistrationBean类型
} else if (initializer instanceof FilterRegistrationBean) {
Filter source = ((FilterRegistrationBean)initializer).getFilter();
//将ServletRegistrationBean加入到集合中
this.addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source);
} else if (initializer instanceof DelegatingFilterProxyRegistrationBean) {
String source = ((DelegatingFilterProxyRegistrationBean)initializer).getTargetBeanName();
this.addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source);
} else if (initializer instanceof ServletListenerRegistrationBean) {
EventListener source = ((ServletListenerRegistrationBean)initializer).getListener();
this.addServletContextInitializerBean(EventListener.class, beanName, initializer, beanFactory, source);
} else {
this.addServletContextInitializerBean(ServletContextInitializer.class, beanName, initializer, beanFactory, initializer);
} } private void addServletContextInitializerBean(Class<?> type, String beanName,
ServletContextInitializer initializer, ListableBeanFactory beanFactory, Object source) {
//加入到initializers中
this.initializers.add(type, initializer);
}

很明显,判断从Spring容器中获取的ServletContextInitializer类型,如ServletRegistrationBean、FilterRegistrationBean、ServletListenerRegistrationBean,并加入到initializers集合中去,我们再来看构造器中的另外一个方法addAdaptableBeans(beanFactory):

 private void addAdaptableBeans(ListableBeanFactory beanFactory) {
//从beanFactory获取所有Servlet.class和Filter.class类型的Bean,并封装成RegistrationBean对象,加入到集合中
this.addAsRegistrationBean(beanFactory, Servlet.class, new ServletContextInitializerBeans.ServletRegistrationBeanAdapter(multipartConfig));
this.addAsRegistrationBean(beanFactory, Filter.class, new ServletContextInitializerBeans.FilterRegistrationBeanAdapter(null));
} private <T, B extends T> void addAsRegistrationBean(ListableBeanFactory beanFactory, Class<T> type, Class<B> beanType, ServletContextInitializerBeans.RegistrationBeanAdapter<T> adapter) {
//从Spring容器中获取所有的Servlet.class和Filter.class类型的Bean
List<Entry<String, B>> beans = this.getOrderedBeansOfType(beanFactory, beanType, this.seen);
Iterator var6 = beans.iterator(); while(var6.hasNext()) {
Entry<String, B> bean = (Entry)var6.next();
if (this.seen.add(bean.getValue())) {
int order = this.getOrder(bean.getValue());
String beanName = (String)bean.getKey();
//创建Servlet.class和Filter.class包装成RegistrationBean对象
RegistrationBean registration = adapter.createRegistrationBean(beanName, bean.getValue(), beans.size());
registration.setName(beanName);
registration.setOrder(order);
this.initializers.add(type, registration);
if (logger.isDebugEnabled()) {
logger.debug("Created " + type.getSimpleName() + " initializer for bean '" + beanName + "'; order=" + order + ", resource=" + this.getResourceDescription(beanName, beanFactory));
}
}
} }

我们看到先从beanFactory获取所有Servlet.class和Filter.class类型的Bean,然后通过ServletRegistrationBeanAdapter和FilterRegistrationBeanAdapter两个适配器将Servlet.class和Filter.class封装成RegistrationBean

private static class ServletRegistrationBeanAdapter implements ServletContextInitializerBeans.RegistrationBeanAdapter<Servlet> {
private final MultipartConfigElement multipartConfig; ServletRegistrationBeanAdapter(MultipartConfigElement multipartConfig) {
this.multipartConfig = multipartConfig;
} public RegistrationBean createRegistrationBean(String name, Servlet source, int totalNumberOfSourceBeans) {
String url = totalNumberOfSourceBeans == 1 ? "/" : "/" + name + "/";
if (name.equals("dispatcherServlet")) {
url = "/";
}
//还是将Servlet.class实例封装成ServletRegistrationBean对象
//这和我们自己创建ServletRegistrationBean对象是一模一样的
ServletRegistrationBean bean = new ServletRegistrationBean(source, new String[]{url});
bean.setMultipartConfig(this.multipartConfig);
return bean;
}
} private static class FilterRegistrationBeanAdapter implements ServletContextInitializerBeans.RegistrationBeanAdapter<Filter> {
private FilterRegistrationBeanAdapter() {
} public RegistrationBean createRegistrationBean(String name, Filter source, int totalNumberOfSourceBeans) {
//Filter.class实例封装成FilterRegistrationBean对象
return new FilterRegistrationBean(source, new ServletRegistrationBean[0
]);
}
}

代码中注释很清楚了还是将Servlet.class实例封装成ServletRegistrationBean对象,将Filter.class实例封装成FilterRegistrationBean对象,这和我们自己定义ServletRegistrationBean对象是一模一样的,现在所有的ServletRegistrationBean、FilterRegistrationBean

Servlet.class、Filter.class都添加到List<ServletContextInitializer> sortedList这个集合中去了,接着就是遍历这个集合,执行其onStartup方法了

ServletContextInitializer的onStartup方法

ServletRegistrationBean

public class ServletRegistrationBean extends RegistrationBean {
private static final Log logger = LogFactory.getLog(ServletRegistrationBean.class);
private static final String[] DEFAULT_MAPPINGS = new String[]{"/*"};
private Servlet servlet; public void onStartup(ServletContext servletContext) throws ServletException {
Assert.notNull(this.servlet, "Servlet must not be null");
String name = this.getServletName();
//调用ServletContext的addServlet
Dynamic added = servletContext.addServlet(name, this.servlet);
} //略...
} private javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, String servletClass, Servlet servlet, Map<String, String> initParams) throws IllegalStateException {
if (servletName != null && !servletName.equals("")) {
if (!this.context.getState().equals(LifecycleState.STARTING_PREP)) {
throw new IllegalStateException(sm.getString("applicationContext.addServlet.ise", new Object[]{this.getContextPath()}));
} else {
Wrapper wrapper = (Wrapper)this.context.findChild(servletName);
if (wrapper == null) {
wrapper = this.context.createWrapper();
wrapper.setName(servletName);
this.context.addChild(wrapper);
} else if (wrapper.getName() != null && wrapper.getServletClass() != null) {
if (!wrapper.isOverridable()) {
return null;
} wrapper.setOverridable(false);
} if (servlet == null) {
wrapper.setServletClass(servletClass);
} else {
wrapper.setServletClass(servlet.getClass().getName());
wrapper.setServlet(servlet);
} if (initParams != null) {
Iterator i$ = initParams.entrySet().iterator(); while(i$.hasNext()) {
Entry<String, String> initParam = (Entry)i$.next();
wrapper.addInitParameter((String)initParam.getKey(), (String)initParam.getValue());
}
} return this.context.dynamicServletAdded(wrapper);
}
} else {
throw new IllegalArgumentException(sm.getString("applicationContext.invalidServletName", new Object[]{servletName}));
}
}

看到没,ServletRegistrationBean 中的 onStartup先获取Servlet的name,然后调用ServletContext的addServlet将Servlet加入到Tomcat中,这样我们就能发请求给这个Servlet了。

AbstractFilterRegistrationBean

public void onStartup(ServletContext servletContext) throws ServletException {
Filter filter = this.getFilter();
Assert.notNull(filter, "Filter must not be null");
String name = this.getOrDeduceName(filter);
//调用ServletContext的addFilter
Dynamic added =
servletContext.addFilter(name, filter);
}

AbstractFilterRegistrationBean也是同样的原理,先获取目标Filter,然后调用ServletContext的addFilter将Filter加入到Tomcat中,这样Filter就能拦截我们请求了。

DispatcherServletAutoConfiguration

最熟悉的莫过于,在Spring Boot在自动配置SpringMVC的时候,会自动注册SpringMVC前端控制器:DispatcherServlet,该控制器主要在DispatcherServletAutoConfiguration自动配置类中进行注册的。DispatcherServlet是SpringMVC中的核心分发器。DispatcherServletAutoConfiguration也在spring.factories中配置了

DispatcherServletConfiguration

 @Configuration
@ConditionalOnWebApplication
// 先看下ClassPath下是否有DispatcherServlet.class字节码
// 我们引入了spring-boot-starter-web,同时引入了tomcat和SpringMvc,肯定会存在DispatcherServlet.class字节码
@ConditionalOnClass({DispatcherServlet.class})
// 这个配置类的执行要在EmbeddedServletContainerAutoConfiguration配置类生效之后执行
// 毕竟要等Tomcat启动后才能往其中注入DispatcherServlet
@AutoConfigureAfter({EmbeddedServletContainerAutoConfiguration.class})
protected static class DispatcherServletConfiguration {
public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";
public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";
@Autowired
private ServerProperties server; @Autowired
private WebMvcProperties webMvcProperties; @Autowired(required = false)
private MultipartConfigElement multipartConfig; // Spring容器注册DispatcherServlet
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet() {
// 直接构造DispatcherServlet,并设置WebMvcProperties中的一些配置
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setDispatchOptionsRequest(this.webMvcProperties.isDispatchOptionsRequest());
dispatcherServlet.setDispatchTraceRequest(this.webMvcProperties.isDispatchTraceRequest());
dispatcherServlet.setThrowExceptionIfNoHandlerFound(this.webMvcProperties.isThrowExceptionIfNoHandlerFound());
return dispatcherServlet;
} @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
public ServletRegistrationBean dispatcherServletRegistration() {
// 直接使用DispatcherServlet和server配置中的servletPath路径构造ServletRegistrationBean
// ServletRegistrationBean实现了ServletContextInitializer接口,在onStartup方法中对应的Servlet注册到Servlet容器中
// 所以这里DispatcherServlet会被注册到Servlet容器中,对应的urlMapping为server.servletPath配置
ServletRegistrationBean registration = new ServletRegistrationBean(dispatcherServlet(), this.server.getServletMapping());
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
if (this.multipartConfig != null) {
registration.setMultipartConfig(this.multipartConfig);
}
return registration;
} @Bean // 构造文件上传相关的bean
@ConditionalOnBean(MultipartResolver.class)
@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
public MultipartResolver multipartResolver(MultipartResolver resolver) {
return resolver;
} }

先看下ClassPath下是否有DispatcherServlet.class字节码, 我们引入了spring-boot-starter-web,同时引入了tomcat和SpringMvc,肯定会存在DispatcherServlet.class字节码,如果没有导入spring-boot-starter-web,则这个配置类将不会生效

然后往Spring容器中注册DispatcherServlet实例,接着又加入ServletRegistrationBean实例,并把DispatcherServlet实例作为参数,上面我们已经学过了ServletRegistrationBean的逻辑,在Tomcat启动的时候,会获取所有的ServletRegistrationBean,并执行其中的onstartup方法,将DispatcherServlet注册到Servlet容器中,这样就类似原来的web.xml中配置的dispatcherServlet。

<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

所以只要导入了spring-boot-starter-web这个starter,SpringBoot就有了Tomcat容器,并且往Tomcat容器中注册了DispatcherServlet对象,这样就能接收到我们的请求了

 
 

SpringBoot 源码解析 (七)----- Spring Boot的核心能力 - 自定义Servlet、Filter、Listener是如何注册到Tomcat容器中的?(SpringBoot实现SpringMvc的原理)的更多相关文章

  1. springboot源码解析-管中窥豹系列之web服务器(七)

    一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...

  2. springboot源码解析-管中窥豹系列之自动装配(九)

    一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...

  3. springboot源码解析-管中窥豹系列之BeanDefine如何加载(十三)

    一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...

  4. springboot源码解析-管中窥豹系列之总体结构(一)

    一.简介 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...

  5. springboot源码解析-管中窥豹系列之项目类型(二)

    一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...

  6. springboot源码解析-管中窥豹系列之Runner(三)

    一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...

  7. springboot源码解析-管中窥豹系列之Initializer(四)

    一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...

  8. springboot源码解析-管中窥豹系列之BeanPostProcessor(十二)

    一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...

  9. springboot源码解析-管中窥豹系列之bean如何生成?(十四)

    一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...

随机推荐

  1. ArrayList源码解析(二)

    欢迎转载,转载烦请注明出处,谢谢. https://www.cnblogs.com/sx-wuyj/p/11177257.html 自己学习ArrayList源码的一些心得记录. 继续上一篇,Arra ...

  2. g_pLog

     g_pLog = new CLog("log");  g_pLog->Enable();  g_pScrLog = new CLog("data"); ...

  3. unittest断言

    assertEquals(expected,actual,msg=msg)    # 判断 expected,actual是否一致,msg类似备注,可以为空

  4. spring security原理-学习笔记1-整体概览

    整体概述 运行时环境 Spring Security 3.0需要Java 5.0 Runtime Environment或更高版本. 核心组件 SecurityContextHolder,Securi ...

  5. 说说hashCode() 和 equals() 之间的关系?

    上一篇关于介绍Object类下的几种方法时面试题时,提到equals()和hashCode()方法可能引出关于“hashCode() 和 equals() 之间的关系?”的面试题,本篇来解析一下这道基 ...

  6. 基于 HTML5 + WebGL 的 3D 可视化挖掘机

    前言 在工业互联网以及物联网的影响下,人们对于机械的管理,机械的可视化,机械的操作可视化提出了更高的要求.如何在一个系统中完整的显示机械的运行情况,机械的运行轨迹,或者机械的机械动作显得尤为的重要,因 ...

  7. SQL挑战一 : 查找最晚入职员工的所有信息

    以上数据库表: CREATE TABLE `employees` ( `emp_no` int(11) NOT NULL, `birth_date` date NOT NULL, `first_nam ...

  8. JS移动端适配(自适应)

    var html = document.querySelector('html'); changeRem(); window.addEventListener('resize', changeRem) ...

  9. 解决 Mybatis报错org.apache.ibatis.ognl.NoSuchPropertyException: XXXCriteria$Criterion.noValue

    问题 这个noValue一定存在,但是报错. 场景就是存在并发的情况下,尤其是在服务刚刚启动的时候,就会发生这个异常. 但是很不幸,mybatis 3.4.1之前,用的 OGNL都是由这个问题. 分析 ...

  10. 小白学 Python(13):基础数据结构(字典)(下)

    人生苦短,我选Python 前文传送门 小白学 Python(1):开篇 小白学 Python(2):基础数据类型(上) 小白学 Python(3):基础数据类型(下) 小白学 Python(4):变 ...