源码解读全文
SPI
- 规范,
Service Provider Interface
- 接口工程,提供接口;多个实现工程,实现接口
- 配置
META-INF/services,文件名为接口名,文件内容为实现类ServiceLoader进行加载,指定一个接口,加载当前系统中的所有该接口的指定实现类
Setvlet3.0是JavaEE的Web规范标准- Tomcat是
Setvlet3.0的一个实现,可以运行Setvlet3.0的程序,是Setvlet3.0的容器
Servlet规范
- 创建对象
- 调用
init初始化 - 每次请求调用
service进行处理 Tomcat停止,调用destory进行销毁
ServletContainerInitializer
Tomcat启动后,context.start过程中,发送事件调用webConfig(),扫描web路径下META-INF/services扫描ServletContainerInitializer,加载SpringServletContainerInitializer- 根据
SpringServletContainerInitializer的@HandlesTypes,在Tomcat启动期间Webconfig()中,加载WebApplicationInitializer
注解了
@HandlesTypes(WebApplicationInitializer.class)标明感兴趣的类,找到所有实现WebApplicationInitializer的类,最终也找到自定义的AppStarter
自定义注解启动类
public class AppStrater implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(AppConfig.class);
//请求都交给 DispatcherServlet
DispatcherServlet dispatcherServlet = new DispatcherServlet(context);
ServletRegistration.Dynamic app = servletContext.addServlet("app", dispatcherServlet);
app.setLoadOnStartup(1);
app.addMapping("/");
}
}
onStarup
SpringServletContainerInitializer.onStarup遍历每一个WebApplicationInitializer- 创建为实例,添加到
initializers集合中,initializers.add((WebApplicationInitializer) ReflectionUtils.accessibleConstructor(waiClass).newInstance());
非接口,不是抽象,是
WebApplicationInitializer
- 观察者模式,调用所有
onStartup - 调用自定义的
onStartup,准备空的容器,注册Spring配置类,但是没有刷新,还未启动容器,利用servlet的初始化机制 - 注册
DispatcherServlet,设置IOC容器,并注册到Tomcat的servletContext中
Tomcat会为每一个Servlet创建一个对象,保证为单实例DispatcherServlet需要初始化,那么就需要通过init- 在初始化
DispatcherServlet,启动IOC容器
DispatcherServlet
HttpServletBean重写了init,留下了initServletBean,给子类重写FrameworkServlet中重写了initServletBean,留下了initFrameworkServlet给子类重写
最终通过这里,初始化子容器,
容器启动12大步
initServletBean
- 初始化
web容器,this.webApplicationContext = initWebApplicationContext();
父子容器
xml配置
web.xml配置context-parm的contextConfigLocation,指定Spring配置文件,为父容器,进行包扫描,并且保存所有组件web.xml配置DispatcherServlet的contextConfigLocation,指定SpringMVC配置文件,只负责Web组件- 产生父子容器
- 尝试获取之前的
webApplicationContext,构建父子容器,产生容器隔离效果
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
- 生成父子容器
// 当前容器
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
//类型转换
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent -> set
// the root application context (if any; may be null) as the parent
//设置父容器
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
通过AbstractAnnotationConfigDispatcherServletInitializer启动SpringMVC
- 其中
AbstractContextLoaderInitializer的registerContextLoaderListener注册了监听器,通过监听器原理,进行初始化
ContextLoaderListener在Tomcat启动后,调用contextInitialized,其中initWebApplicationContext(event.getServletContext());初始化IOC容器
registerContextLoaderListener
protected void registerContextLoaderListener(ServletContext servletContext) {
//创建一个根容器,方法留给子类实现
WebApplicationContext rootAppContext = createRootApplicationContext();
if (rootAppContext != null) {
ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
listener.setContextInitializers(getRootApplicationContextInitializers());
servletContext.addListener(listener);
}
else {
logger.debug("No ContextLoaderListener registered, as " +
"createRootApplicationContext() did not return an application context");
}
}
createRootApplicationContext
AbstractAnnotationConfigDispatcherServletInitializer,重写了该方法
protected WebApplicationContext createRootApplicationContext() {
//获取根配置类,留给子类实现
Class<?>[] configClasses = getRootConfigClasses();
if (!ObjectUtils.isEmpty(configClasses)) {
//配置类非空,创建容器类,并注册配置类
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(configClasses);
return context;
}
else {
return null;
}
}
实现
- 产生父子容器隔离的效果,子容器可以引用父容器的内容
启动类
public class QuickAppStarter extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
//Spring配置类
return new Class[]{SpringConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
//SpringMVC配置类
return new Class[]{SpringMVCConfig.class};
}
@Override
protected String[] getServletMappings() {
//映射
return new String[]{"/"};
}
}
配置类
- 父容器
Spring配置类,扫描除web相关的组件
@ComponentScan(value = "com.java", excludeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, value = Controller.class)
})
@Configuration
public class SpringConfig {
}
- 子容器不能有
@Configuration,否则会被扫描到
@ComponentScan(value = "com.java", includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, value = Controller.class)
}, useDefaultFilters = false) //禁用本身
public class SpringMVCConfig {
}
SpringMVC父子容器启动
- Tomcat启动,
StandardContext.startInternal(),SPI机制加载SpringServletContainerInitializer,根据@HandlerTypes扫描所有WebApplicationInitializer,在启动过程中,调用其中遍历SpringServletContainerInitializer调用onStartup() - 遍历所有
WebApplicationInitializer的实现类,开始创建父子容器 - 父容器,
tomcat启动过程中,context.start触发监听器回调进行启动 - 子容器,在
tomcat启动过程中,wrapper.allocate()中调用dispatcherServlet.init进行初始化
SPI加载WebApplicationInitializer
Tomcat,在创建context.start()中webConfig()进行加载WebApplicationInitializer- 第三步,
processServletContainerInitializers进行ServletContainerInitializer加载 - 第五步,匹配
HandlerTypes,加载WebApplicationInitializer - 第11步,将
ServletContainerInitializer作为key,WebApplicationInitializer集合作为value,进行保存,当调用SpringServletContainerInitializer时进行遍历
ServletContainerInitializer实际上为SpringServletContainerInitializer
processServletContainerInitializers
- 加载
ServletContainerInitializer load方法META-INF/services路径
创建父容器
- 调用
SpringServletContainerInitializer的onStartup(),调用父类AbstractContextLoaderInitializer的onStartup(),注册监听器registerContextLoaderListener(servletContext);,其中调用WebApplicationContext rootAppContext = createRootApplicationContext();
创建
AnnotationConfigWebApplicationContext父容器,配置类由子类重写getRootConfigClasses提供,此时还没有刷新容器
- 准备监听器,保存了父容器
ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
监听器,在Tomcat加载结束后,调用
contextInitialized,其中会初始化父容器
创建子容器
AbstractDispatcherServletInitializer的onStartup()中继续创建web子容器registerDispatcherServlet(servletContext);
同样是
AnnotationConfigWebApplicationContext,配置类由子类重写getServletConfigClasses()提供,此时还没有刷新容器
- 创建前端控制器
dispatcherServlet保存子容器,FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext); - 将
servlet注册到上下文,ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet); - 设置启动优先级
registration.setLoadOnStartup(1);
- 正数,越小优先级越高
- 负数,容器在该
servlet被选择时才加载
- 设置映射路径,有子类重写
registration.addMapping(getServletMappings());
初始化父容器
Tomcat启动context,其中listenerStart()- 遍历所有监听器,并调用
contextInitialized - 调用
ContextLoaderListener的contextInitialized,其中initWebApplicationContext(event.getServletContext());初始化父容器 - 获取当前容器
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context; - 执行初始化逻辑
configureAndRefreshWebApplicationContext(cwac, servletContext);
其中设置必要的参数,刷新容器
wac.refresh();
- 将当前容器设置给上下文(
application作用域)servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
初始化子容器
Tomcat.StandardContext进行启动过程中,调用loadOnStartup,加载启动时创建的servlet,最终调用servlet.init(facade);- 初始化
DispatcherServlet,最终调用HttpServletBean的init()方法,调用FrameworkServlet中的initServletBean,其中this.webApplicationContext = initWebApplicationContext();,初始化子容器 - 尝试获得根容器
WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); - 将父容器设置给子容器
cwac.setParent(rootContext); - 初始化子容器
configureAndRefreshWebApplicationContext(cwac);
设置信息,并刷新容器
wac.refresh();
父子容器组件搜索
BeanFactoryUtils的beanNamesForTypeIncludingAncestors会在父容器中,进行查找
public static String[] beanNamesForTypeIncludingAncestors(ListableBeanFactory lbf, Class<?> type) {
Assert.notNull(lbf, "ListableBeanFactory must not be null");
String[] result = lbf.getBeanNamesForType(type);
if (lbf instanceof HierarchicalBeanFactory) {
HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;
//在父容器中找,递归找
if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
String[] parentResult = beanNamesForTypeIncludingAncestors(
(ListableBeanFactory) hbf.getParentBeanFactory(), type);
//合并结果
result = mergeNamesWithParent(result, parentResult, hbf);
}
}
return result;
}
Tomcat.StandardContext对SpringMVC启动的流程影响
startInternal.fireLifecycleEvent
- 发送配置初始化事件
fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);
SPI机制加载ServletContainerInitializer,实际上为SpringServletContainerInitializer- 根据
SpringServletContainerInitializer上的@HandlerTypes,加载WebApplicationInitializer
- 创建父子容器
SpringServletContainerInitializer.onStartup() - 初始化父容器
listenerStart()
startInternal.loadOnStartup
- 加载并初始化所有启动时加载servlet,
loadOnStartup(findChildren()) - 调用
wrapper.load(); - 此时这个
StandardWrapper已经包括在初始化子容器时创建的DipatcherServlet - 调用
initServlet(instance);,最终调用servlet.init(facade);,进入MVC的初始化子容器
以下是错误理解,不舍得删
- 初始化子容器
- 初始化
servlet,wrapper.getPipeline().getFirst().invoke(request, response); - 其实调用
StandardWrapperValve.invoke
servlet = wrapper.allocate();instance = loadServlet();servlet = (Servlet) instanceManager.newInstance(servletClass);
- 最终调用
initServlet(servlet);,其中servlet.init(facade);
通过
DispatcherServlet调用FrameworkServlet中的initServletBean,调用this.webApplicationContext = initWebApplicationContext();