文章目录??
目录
文章目录??
前言
一、ServletContainerInitializer 接口是什么?
二、@HandlesTypes注解是什么
三、SpringServletContainerInitializer 是什么
四、WebApplicationInitializer 接口
总结
前言
本文不是SpringBoot的启动流程,是基于Tomcat,Spring,SpringMVC整合的启动流程,在我们启动Tomcat的时候Spring和SpringMvc是怎么加载启动的呢?ServletContainerInitializer 离不开这个关键的接口,围绕此接口在下文详细叙述。
一、ServletContainerInitializer 接口是什么?
在Tomcat容器启动的时候会扫描所有jar包中 META-INF/services 目录下存不存在一个名为javax.servlet.ServletContainerInitializer 的文件,此文件的内容是实现了ServletContainerInitializer 接口的实现类的全类名, 若存在则tomcat容器会将该实现类加载和实例化并调用该类的onStartup方法
public interface ServletContainerInitializer {
void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException;
}
可以看到onStartup方法有两个参数?
- ?Set<Class<?>> webAppInitializerClasses?是一个Class对象的Set集合,Set集合中的Class对象是从何而来呢?请带着问题向下接着看.
-
ServletContext servletContex
解答刚才提出的疑问,??参数一?webAppInitializerClasses?从何而来@HandlesTypes(WebApplicationInitializer.class), tomcat容器会扫描所有实现了注解标注的WebApplicationInitializer.class这个接口的实现类, 并把所有实现类的Class对象在调用onStartup方法时以参数的形式传递过来
二、@HandlesTypes注解是什么
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface HandlesTypes {
Class<?>[] value();
}
?这个注解标注接受一个Class的数组,这个数组会被当作参数传递给onStartup方法, 对应此方法的第一个参数, 这就解答了上面的问题,Set集合中的Class对象是从何而来的
三、SpringServletContainerInitializer 是什么
??在Spring中 (Spring的spi文件)org.springframework.web.SpringServletContainerInitializer 类就是实现了ServletContainerInitializer 接口并重写了onStartup方法的实现类 ?这时,当tomcat启动时就会调用SpringServletContainerInitializer 类重写的onStartup方法, Spring重写的onStartup方法就做了三件事
- 见图片2号方框判断条件, 找出所有非接口, 非抽象类 且是WebApplicationInitializer.class的子类的Class对象,
- 见图片3号方框, 把满足条件的所有Class生成的对象, 添加到List集合存储起来
- 见图片4号方框,?遍历第二步的集合中的对象 调用对象的onStartup方法,注意此处的onStartup方法是WebApplicationInitializer.class 这个接口定义的
public interface WebApplicationInitializer {
void onStartup(ServletContext servletContext) throws ServletException;
}
四、WebApplicationInitializer 接口
方便以编程方式配置ServletContext, Spring和SpringMvc容器的启动也依赖此接口及其子类, 主要依赖的子类有三个
-
AbstractContextLoaderInitializer -
AbstractDispatcherServletInitializer -
AbstractAnnotationConfigDispatcherServletInitializer
类的继承结构如下:
?当调用onStartup方法时由其子类AbstractDispatcherServletInitializer#onStartup方法实现
//此处代码在AbstractDispatcherServletInitializer类中
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
//此处调用父类的AbstractContextLoaderInitializer#onStartup方法
super.onStartup(servletContext);//第一步
registerDispatcherServlet(servletContext);//第二步
}
?第一步先调用了AbstractContextLoaderInitializer#onStartup方法
此处代码在AbstractContextLoaderInitializer类中
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
registerContextLoaderListener(servletContext);
}
protected void registerContextLoaderListener(ServletContext servletContext) {
WebApplicationContext rootAppContext = createRootApplicationContext();
if (rootAppContext != null) {
ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
listener.setContextInitializers(getRootApplicationContextInitializers());
servletContext.addListener(listener);
}
}
?第二步紧接着调用AbstractDispatcherServletInitializer#registerDispatcherServlet
//此处代码在AbstractDispatcherServletInitializer类中
protected void registerDispatcherServlet(ServletContext servletContext) {
String servletName = getServletName();
WebApplicationContext servletAppContext = createServletApplicationContext();
FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());
ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
registration.setLoadOnStartup(1);
registration.addMapping(getServletMappings());
registration.setAsyncSupported(isAsyncSupported());
Filter[] filters = getServletFilters();
if (!ObjectUtils.isEmpty(filters)) {
for (Filter filter : filters) {
registerServletFilter(servletContext, filter);
}
}
customizeRegistration(registration);
}
总结
|