Tomcat 如何加载Spring?
1. Tomcat 提供的SCI 接口
SCIs 通过SPI 方式被注册和使用,tomcat要求所有MVC 框架必须包含 META-INF/services/javax.servlet.ServletContainerInitializer 文件,并且在里面指定自己的实现类。
public interface ServletContainerInitializer {
/**
* Receives notification during startup of a web application of the classes
* within the web application that matched the criteria defined via the
* {@link javax.servlet.annotation.HandlesTypes} annotation.
*
* @param c The (possibly null) set of classes that met the specified
* criteria
* @param ctx The ServletContext of the web application in which the
* classes were discovered
*
* @throws ServletException If an error occurs
*/
void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException;
}
2. spring-mvc中SCIs 的实现类 (SpringServletContainerInitializer)
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
List<WebApplicationInitializer> initializers = Collections.emptyList();
if (webAppInitializerClasses != null) {
initializers = new ArrayList<>(webAppInitializerClasses.size());
for (Class<?> waiClass : webAppInitializerClasses) {
// Be defensive: Some servlet containers provide us with invalid classes,
// no matter what @HandlesTypes says...
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
initializers.add((WebApplicationInitializer)
ReflectionUtils.accessibleConstructor(waiClass).newInstance());
}
catch (Throwable ex) {
throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
}
}
}
}
if (initializers.isEmpty()) {
servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
return;
}
servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
AnnotationAwareOrderComparator.sort(initializers);
// 执行所有 WebApplicationInitializer 的 onStartup 方法,完成应用的初始化
for (WebApplicationInitializer initializer : initializers) {
initializer.onStartup(servletContext);
}
}
3. tomcat 启动SCIs (StandardContext.java)
protected synchronized void startInternal() throws LifecycleException {
....
// 调用所有的 ServletContainerInitializers 实现类
for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry :
initializers.entrySet()) {
try {
entry.getKey().onStartup(entry.getValue(),
getServletContext());
} catch (ServletException e) {
log.error(sm.getString("standardContext.sciFail"), e);
ok = false;
break;
}
}
// 初始化并加载所有的 "load-on-startup" servlets
if (ok) {
if (!loadOnStartup(findChildren())){
log.error(sm.getString("standardContext.servletFail"));
ok = false;
}
}
....
}
如果 DispatcherServlet 的 load-on-startup 小于0,会在首次处理请求时初始化,具体见第4步。
4. tomcat 初始化并加载 DispatcherServlet (StandardWrapper.java)
StandardWrapper.allocate() -> loadServlet()
public synchronized Servlet loadServlet() throws ServletException {
Servlet servlet;
try {
long t1=System.currentTimeMillis();
// Complain if no servlet class has been specified
if (servletClass == null) {
unavailable(null);
throw new ServletException
(sm.getString("standardWrapper.notClass", getName()));
}
InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager();
try {
// 创建 DispatcherServlet 实例
servlet = (Servlet) instanceManager.newInstance(servletClass);
} catch (ClassCastException e) {
throw new ServletException
(sm.getString("standardWrapper.notServlet", servletClass), e);
} catch (Throwable e) {
e = ExceptionUtils.unwrapInvocationTargetException(e);
throw new ServletException
(sm.getString("standardWrapper.instantiate", servletClass), e);
}
initServlet(servlet);
fireContainerEvent("load", this);
loadTime=System.currentTimeMillis() -t1;
} finally {
}
return servlet;
}
如果觉得还不错的话,关注、分享、在看(关注不失联~), 原创不易,且看且珍惜~