有彩蛋哦!!!(或者公众号内点击网赚获取彩蛋)
HttpServletBean完成的工作非常简单,FrameworkServlet完成的工作也不复杂,主要就是创建SpringMVC的IOC容器。但是debug 没有配置文件的springboot项目发现,最要的工作还是调用了DispatcherServlet重写的onRefresh方法
Spring MVC(二)-----HttpServletBean
https://blog.csdn.net/yueloveme/article/details/89643980
FrameworkServlet类的设计目的就是用来建立一个和Servlet关联的Spring容器上下文并将其注册到ServletContext中。
debug过程中没有遇到需要FrameworkServlet来创建容器的情况,在AbstractApplicationContext中refresh方法中创建Spring的IOC容器是在SpringMVC初始化之前进行的,而且并不是项目启动就会初始化SpringMVC而是第一次调用的时候会进行初始化,这是目前观察到的如有不正确欢迎指正
/**
* Overridden method of {@link HttpServletBean}, invoked after any bean properties
* have been set. Creates this servlet's WebApplicationContext.
*/
@Override
protected final void initServletBean() throws ServletException {
getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
if (this.logger.isInfoEnabled()) {
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
}
long startTime = System.currentTimeMillis();
try {
this.webApplicationContext = initWebApplicationContext();
无具体实现
initFrameworkServlet();
}
catch (ServletException | RuntimeException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}
if (this.logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
elapsedTime + " ms");
}
}
/**
* Initialize and publish the WebApplicationContext for this servlet.
* Delegates to {@link #createWebApplicationContext} for actual creation
* of the context. Can be overridden in subclasses.
* @return the WebApplicationContext instance
* @see #FrameworkServlet(WebApplicationContext)
* @see #setContextClass
* @see #setContextConfigLocation
*/
初始化和发布web应用上下文
protected WebApplicationContext initWebApplicationContext() {
获取Spring的IOC容器 SpringIOC容器是先于SpringMVC容器创建的,要把rootContext
当做父容器
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
如果webApplicationContext已经不为空,表示这个Servlet类是通过编程式注册到容器中的
(Servlet 3.0 +中的ServletContext.addServlet()),上下文也由编程式传入。若这个传
入的上下文还没有被初始化,将rootContext上下文设置为它的父上下文,然后将其初始化,否
则直接使用。
if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
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);
}
}
}
if (wac == null) {
// No context instance was injected at construction time -> see if one
// has been registered in the servlet context. If one exists, it is assumed
// that the parent context (if any) has already been set and that the
// user has performed any initialization such as setting the context id
如果为空,说明该Servlet不是由编程式注册到容器中的,在ServletContext中
查找上下文,查找得到
说明上下文已经以别的方式初始化并注册在contextAttribute下,直接使用
wac = findWebApplicationContext();
}
if (wac == null) {
// No context instance is defined for this servlet -> create a local one
此时还为空,调用以下方法创建一个全新的以rootContext为父上下文的上下
文,作为SpringMVC配置元素的上下文
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
// Either the context is not a ConfigurableApplicationContext with refresh
// support or the context injected at construction time had already been
// refreshed -> trigger initial onRefresh manually here.
DispatcherServlet具体实现,初始化一系列属性
onRefresh(wac);
}
if (this.publishContext) {
// Publish the context as a servlet context attribute.
将这个上下文发布到ServletContext中
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
"' as ServletContext attribute with name [" + attrName + "]");
}
}
return wac;
}
从当前容器中查找SpringMVC容器
/**
* Retrieve a {@code WebApplicationContext} from the {@code ServletContext}
* attribute with the {@link #setContextAttribute configured name}. The
* {@code WebApplicationContext} must have already been loaded and stored in the
* {@code ServletContext} before this servlet gets initialized (or invoked).
* Subclasses may override this method to provide a different
* {@code WebApplicationContext} retrieval strategy.
* @return the WebApplicationContext for this servlet, or {@code null} if not found
* @see #getContextAttribute()
*/
@Nullable
protected WebApplicationContext findWebApplicationContext() {
String attrName = getContextAttribute();
if (attrName == null) {
return null;
}
WebApplicationContext wac =
WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName);
if (wac == null) {
throw new IllegalStateException("No WebApplicationContext found: initializer not registered?");
}
return wac;
}
创建SpringMVC容器
/**
* Instantiate the WebApplicationContext for this servlet, either a default
* {@link org.springframework.web.context.support.XmlWebApplicationContext}
* or a {@link #setContextClass custom context class}, if set.
* This implementation expects custom contexts to implement the
* {@link org.springframework.web.context.ConfigurableWebApplicationContext}
* interface. Can be overridden in subclasses.
*
Do not forget to register this servlet instance as application listener on the
* created context (for triggering its {@link #onRefresh callback}, and to call
* {@link org.springframework.context.ConfigurableApplicationContext#refresh()}
* before returning the context instance.
* @param parent the parent ApplicationContext to use, or {@code null} if none
* @return the WebApplicationContext for this servlet
* @see org.springframework.web.context.support.XmlWebApplicationContext
*/
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
Class<?> contextClass = getContextClass();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Servlet with name '" + getServletName() +
"' will try to create custom WebApplicationContext context of class '" +
contextClass.getName() + "'" + ", using parent context [" + parent + "]");
}
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException(
"Fatal initialization error in servlet with name '" + getServletName() +
"': custom WebApplicationContext class [" + contextClass.getName() +
"] is not of type ConfigurableWebApplicationContext");
}
实例化IOC容器
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
设置IOC容器相关属性
wac.setEnvironment(getEnvironment());
设置parent
wac.setParent(parent);
String configLocation = getContextConfigLocation();
if (configLocation != null) {
wac.setConfigLocation(configLocation);
}
初始化springMVC各种的相关配置,各种注入的Controller,配置文件
configureAndRefreshWebApplicationContext(wac);
return wac;
}
配置或者刷新应用上下文
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
// The application context id is still set to its original default value
// -> assign a more useful id based on available information
if (this.contextId != null) {
wac.setId(this.contextId);
}
else {
// Generate default id...
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
}
}
设置SpringMVC 属性
wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
// The wac environment's #initPropertySources will be called in any case when the context
// is refreshed; do it eagerly here to ensure servlet property sources are in place for
// use in any post-processing or initialization that occurs below prior to #refresh
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
}
postProcessWebApplicationContext(wac);
applyInitializers(wac);
初始化springMVC各种的相关配置,各种注入的Controller,配置文件
wac.refresh();
}