源码解读全文
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();