Last active
December 22, 2020 10:26
-
-
Save qqyycom/1bda7c102d8c74ec58a6166208ac1975 to your computer and use it in GitHub Desktop.
springMVC中servletContext的初始化过程
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Initializer结构中最顶层的抽象类,可以直接拓展它,使用了模板方法的设计模式,每个子类都专注一个功能 | |
public abstract class AbstractAnnotationConfigDispatcherServletInitializer | |
extends AbstractDispatcherServletInitializer { | |
// 如果实现类没重写createRootApplicationContext()方法, 则在创建RootApplicationContext会被调用 | |
@Override | |
@Nullable | |
protected WebApplicationContext createRootApplicationContext() { | |
//指定rootWebApplicationContext的configClasses类,可以有多个,也可以没有; 如果有,getRootConfigClasses()需要被重写 | |
Class<?>[] configClasses = getRootConfigClasses(); | |
if (!ObjectUtils.isEmpty(configClasses)) { | |
// 初始化一个注解配置型的WebApplicationContext | |
AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext(); | |
// 把configClasses注册到context中,实际是给到rootAppContext中的一个实例属性annotatedClasses中 | |
rootAppContext.register(configClasses); | |
return rootAppContext; | |
} | |
else { | |
return null; | |
} | |
} | |
// | |
@Override | |
protected WebApplicationContext createServletApplicationContext() { | |
// 指定servletContext的配置文件 | |
AnnotationConfigWebApplicationContext servletAppContext = new AnnotationConfigWebApplicationContext(); | |
Class<?>[] configClasses = getServletConfigClasses(); | |
if (!ObjectUtils.isEmpty(configClasses)) { | |
servletAppContext.register(configClasses); | |
} | |
return servletAppContext; | |
} | |
// 指定rootWebApplicationContext(既是spring context)所需的配置文件,需要子类去实现 | |
@Nullable | |
protected abstract Class<?>[] getRootConfigClasses(); | |
// 配置dispatcherServlet 的 webApplicationContext(既是springMVC context) ,需要子类去实现 | |
@Nullable | |
protected abstract Class<?>[] getServletConfigClasses(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Initializer结构中的最底层抽象类,继承了WebApplicationInitializer接口, | |
//当Servlet容器(web容器)初始化的时候,继承了Initializer结构的子类会被调用onStartup() | |
public abstract class AbstractContextLoaderInitializer implements WebApplicationInitializer { | |
/** Logger available to subclasses */ | |
protected final Log logger = LogFactory.getLog(getClass()); | |
@Override | |
public void onStartup(ServletContext servletContext) throws ServletException { | |
// 注册ContextLoaderListener这个listener给ServletContext | |
registerContextLoaderListener(servletContext); | |
} | |
// #12调用 | |
protected void registerContextLoaderListener(ServletContext servletContext) { | |
// 创建RootApplicationContext, 如果实现类没重写createRootApplicationContext()方法, | |
// 则调用的AbstractAnnotationConfigDispatcherServletInitializer的该方法 | |
// 该方法会创建一个rootWebApplicationContext实例, | |
// 如果是AnnotationConfigWebApplicationContext,则还会把0个或多个configClass注册到实例的annotatedClass变量上 | |
// XmlWebApplicationContext, 不会出现在Initializer结构里,由ContextLoader的ContextInitialize()方法直接初始化 | |
WebApplicationContext rootAppContext = createRootApplicationContext(); | |
if (rootAppContext != null) { | |
// 创建一个ContextLoaderListener对象,并把rootAppContext直接赋值对象属性context | |
ContextLoaderListener listener = new ContextLoaderListener(rootAppContext); | |
// 这里不知道是什么作用 | |
listener.setContextInitializers(getRootApplicationContextInitializers()); | |
// 把listener添加到servletContext上 | |
servletContext.addListener(listener); | |
} | |
else { | |
logger.debug("No ContextLoaderListener registered, as " + | |
"createRootApplicationContext() did not return an application context"); | |
} | |
} | |
// #27调用 | |
/**Specify application context initializers to be applied to the root application | |
* context that the {@code ContextLoaderListener} is being created with. | |
*/ | |
@Nullable | |
protected ApplicationContextInitializer<?>[] getRootApplicationContextInitializers() { | |
return null; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Initializer结构中的抽象类,看名字知道是初始化DispatcherServlet的 | |
public abstract class AbstractDispatcherServletInitializer extends AbstractContextLoaderInitializer { | |
/** | |
* The default servlet name. Can be customized by overriding {@link #getServletName}. | |
*/ | |
public static final String DEFAULT_SERVLET_NAME = "dispatcher"; | |
// 如果继承了Initializer结构的子类不重写这个方法,则这个方法会被调用 | |
@Override | |
public void onStartup(ServletContext servletContext) throws ServletException { | |
// AbstractContextLoaderInitializer是AbstractDispatcherServletInitializer的父类,先调用父类的onStartup方法 | |
// 主要是创建一个ContextLoaderListener添加到servletContext,并给它绑定一个AnnotationConfigWebApplicationContext型的rootApplicationContext | |
super.onStartup(servletContext); | |
// 注册DispatcherServlet这个servlet,是springMVC结构的核心,前置控制器 | |
registerDispatcherServlet(servletContext); | |
} | |
protected void registerDispatcherServlet(ServletContext servletContext) { | |
// 设置servlet名字 默认dispatcher | |
String servletName = getServletName(); | |
Assert.hasLength(servletName, "getServletName() must not return empty or null"); | |
// 给servlet创建上下文 如果没被子类重写,会调用AbstractAnnotationConfigDispatcherServletInitializer类中的createServletApplicationContext() | |
// 如果有servletConfigClass,也会赋值到servletAppContext的annotatedClasses上 | |
WebApplicationContext servletAppContext = createServletApplicationContext(); | |
Assert.notNull(servletAppContext, | |
"createServletApplicationContext() did not return an application " + | |
"context for servlet [" + servletName + "]"); | |
// 创建一个dispatcherServlet,并把servletAppContext绑到上面 | |
FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext); | |
// 还不清楚要干嘛 | |
dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers()); | |
// 把dispatcherServlet添加到servletContext中servletName作为名字 | |
ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet); | |
Assert.notNull(registration, | |
"Failed to register servlet with name '" + servletName + "'." + | |
"Check if there is another servlet registered under the same name."); | |
// 设置一些dispatcherServlet初始化用到的值 | |
registration.setLoadOnStartup(1); | |
registration.addMapping(getServletMappings()); | |
registration.setAsyncSupported(isAsyncSupported()); | |
// 子类可能重写这个方法getServletFilters,用于向servletContext中添加filter | |
Filter[] filters = getServletFilters(); | |
if (!ObjectUtils.isEmpty(filters)) { | |
for (Filter filter : filters) { | |
// 注册filter | |
registerServletFilter(servletContext, filter); | |
} | |
} | |
// 自定义配置,可以配置初始化Servlet需要的参数 | |
// e.g. registration.setInitParameter("defaultHtmlEscape", "true"); | |
// registration.setInitParameter("spring.profiles.active", "default"); | |
customizeRegistration(registration); | |
} | |
// #21 调用 | |
protected String getServletName() { | |
return DEFAULT_SERVLET_NAME; // dispatcher | |
} | |
// #30 调用 | |
protected FrameworkServlet createDispatcherServlet(WebApplicationContext servletAppContext) { | |
return new DispatcherServlet(servletAppContext); | |
} | |
// #32 调用(不知道干嘛) | |
/** | |
* Specify application context initializers to be applied to the servlet-specific | |
* application context that the {@code DispatcherServlet} is being created with. | |
*/ | |
@Nullable | |
protected ApplicationContextInitializer<?>[] getServletApplicationContextInitializers() { | |
return null; | |
} | |
// #48 调用 | |
protected FilterRegistration.Dynamic registerServletFilter(ServletContext servletContext, Filter filter) { | |
// 转换filter的name | |
String filterName = Conventions.getVariableName(filter); | |
// 把filter添加到servletContext中,用刚才生成的filterName做名字 | |
Dynamic registration = servletContext.addFilter(filterName, filter); | |
// 这里不知道为什么 | |
if (registration == null) { | |
int counter = -1; | |
while (counter == -1 || registration == null) { | |
counter++; | |
registration = servletContext.addFilter(filterName + "#" + counter, filter); | |
Assert.isTrue(counter < 100, | |
"Failed to register filter '" + filter + "'." + | |
"Could the same Filter instance have been registered already?"); | |
} | |
} | |
registration.setAsyncSupported(isAsyncSupported()); | |
//指定filter所拦截的servletName和http方法 | |
registration.addMappingForServletNames(getDispatcherTypes(), false, getServletName()); | |
return registration; | |
} | |
// #97调用 | |
private EnumSet<DispatcherType> getDispatcherTypes() { | |
return (isAsyncSupported() ? | |
EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE, DispatcherType.ASYNC) : | |
EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE)); | |
} | |
/** | |
* A single place to control the {@code asyncSupported} flag for the | |
* {@code DispatcherServlet} and all filters added via {@link #getServletFilters()}. | |
* <p>The default value is "true". | |
*/ | |
protected boolean isAsyncSupported() { | |
return true; | |
} | |
// 自定义servlet初始化需要注册的东西,可以注册initparam, 子类需要重写该方法 | |
/** | |
* Optionally perform further registration customization once | |
* {@link #registerDispatcherServlet(ServletContext)} has completed. | |
* @param registration the {@code DispatcherServlet} registration to be customized | |
* @see #registerDispatcherServlet(ServletContext) | |
*/ | |
protected void customizeRegistration(ServletRegistration.Dynamic registration) { | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { | |
// servletContext只能有一个root的spring容器 | |
if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) { | |
throw new IllegalStateException( | |
"Cannot initialize context because there is already a root application context present - " + | |
"check whether you have multiple ContextLoader* definitions in your web.xml!"); | |
} | |
Log logger = LogFactory.getLog(ContextLoader.class); | |
servletContext.log("Initializing Spring root WebApplicationContext"); | |
if (logger.isInfoEnabled()) { | |
logger.info("Root WebApplicationContext: initialization started"); | |
} | |
long startTime = System.currentTimeMillis(); | |
try { | |
// 如果使用WebApplicationInitializer接口的实现类加载带注解的java类代替web.xml 配置web容器时,这里就不是null | |
// 使用web.xml配置web容器时就为null | |
if (this.context == null) { | |
//创建XmlWebApplicationContext | |
this.context = createWebApplicationContext(servletContext); | |
} | |
if (this.context instanceof ConfigurableWebApplicationContext) { | |
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context; | |
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 -> | |
// determine parent for root web application context, if any. | |
// loadParentContext(servletContext)返回null | |
ApplicationContext parent = loadParentContext(servletContext); | |
cwac.setParent(parent); | |
} | |
// 创建spring的ioc容器 | |
configureAndRefreshWebApplicationContext(cwac, servletContext); | |
} | |
} | |
// 把rootWebApplicationContext(SpringIOC容器)绑定到ServletContext上, | |
// key为org.springframework.web.context.WebApplicationContext.ROOT | |
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); | |
ClassLoader ccl = Thread.currentThread().getContextClassLoader(); | |
if (ccl == ContextLoader.class.getClassLoader()) { | |
currentContext = this.context; | |
} | |
else if (ccl != null) { | |
currentContextPerThread.put(ccl, this.context); | |
} | |
if (logger.isDebugEnabled()) { | |
logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" + | |
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]"); | |
} | |
if (logger.isInfoEnabled()) { | |
long elapsedTime = System.currentTimeMillis() - startTime; | |
logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms"); | |
} | |
return this.context; | |
} | |
catch (RuntimeException ex) { | |
logger.error("Context initialization failed", ex); | |
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex); | |
throw ex; | |
} | |
catch (Error err) { | |
logger.error("Context initialization failed", err); | |
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err); | |
throw err; | |
} | |
} | |
// 被#36调用 | |
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) { | |
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 | |
String idParam = sc.getInitParameter(CONTEXT_ID_PARAM); | |
if (idParam != null) { | |
wac.setId(idParam); | |
} | |
else { | |
// Generate default id... | |
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + | |
ObjectUtils.getDisplayString(sc.getContextPath())); | |
} | |
} | |
// 将webApplicationContext(spring容器)绑定servletContext | |
wac.setServletContext(sc); | |
// 将servletContext中的contextConfigLocation参数取出,配置给rootWebApplicationContext(spring容器),如果没有,就不配置 | |
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM); | |
if (configLocationParam != null) { | |
wac.setConfigLocation(configLocationParam); | |
} | |
// Environment接口是Spring对当前程序运行期间的环境的封装(spring),这里新建了StandardServletEnvironment对象来管理properties和profiles, | |
// 此时还没有把configClass中的properties加载进去 | |
// 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(sc, null); | |
} | |
//使用自定义rootWebApplicationContext | |
customizeContext(sc, wac); | |
//刷新容器(这里属于springIOC容器初始化,先看书),完成之后,springIOC容器创建完成,并初始化和管理了很多bean | |
wac.refresh(); | |
} | |
//#105调用 | |
protected void customizeContext(ServletContext sc, ConfigurableWebApplicationContext wac) { | |
//从ServletContext的init-param中获取globalInitializerClasses和contextInitializerClasses中指定的ApplicationContextInitializer对象 | |
List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses = | |
determineContextInitializerClasses(sc); | |
// 这里没用到,不知道干嘛的 | |
for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) { | |
Class<?> initializerContextClass = | |
GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class); | |
if (initializerContextClass != null && !initializerContextClass.isInstance(wac)) { | |
throw new ApplicationContextException(String.format( | |
"Could not apply context initializer [%s] since its generic parameter [%s] " + | |
"is not assignable from the type of application context used by this " + | |
"context loader: [%s]", initializerClass.getName(), initializerContextClass.getName(), | |
wac.getClass().getName())); | |
} | |
this.contextInitializers.add(BeanUtils.instantiateClass(initializerClass)); | |
} | |
// 根据@Order排个序 | |
AnnotationAwareOrderComparator.sort(this.contextInitializers); | |
for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : this.contextInitializers) { | |
initializer.initialize(wac); | |
} | |
} | |
// #113调用 | |
// GLOBAL_INITIALIZER_CLASSES_PARAM = globalInitializerClasses | |
// CONTEXT_INITIALIZER_CLASSES_PARAM = contextInitializerClasses | |
protected List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> | |
determineContextInitializerClasses(ServletContext servletContext) { | |
List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> classes = | |
new ArrayList<>(); | |
String globalClassNames = servletContext.getInitParameter(GLOBAL_INITIALIZER_CLASSES_PARAM); | |
if (globalClassNames != null) { | |
for (String className : StringUtils.tokenizeToStringArray(globalClassNames, INIT_PARAM_DELIMITERS)) { | |
classes.add(loadInitializerClass(className)); | |
} | |
} | |
String localClassNames = servletContext.getInitParameter(CONTEXT_INITIALIZER_CLASSES_PARAM); | |
if (localClassNames != null) { | |
for (String className : StringUtils.tokenizeToStringArray(localClassNames, INIT_PARAM_DELIMITERS)) { | |
classes.add(loadInitializerClass(className)); | |
} | |
} | |
return classes; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@SuppressWarnings("serial") | |
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware { | |
@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 { | |
// 初始化servlet的webApplicationContext(springIOC容器) | |
this.webApplicationContext = initWebApplicationContext(); | |
initFrameworkServlet(); | |
} | |
catch (ServletException ex) { | |
this.logger.error("Context initialization failed", ex); | |
throw ex; | |
} | |
catch (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"); | |
} | |
} | |
// #16 调用 | |
protected WebApplicationContext initWebApplicationContext() { | |
// 获取跟webApplicationContext | |
WebApplicationContext rootContext = | |
WebApplicationContextUtils.getWebApplicationContext(getServletContext()); | |
WebApplicationContext wac = null; | |
//如果配置过WebApplicationContext | |
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 | |
// 把rootWebApplicationContext设置为父容器 | |
cwac.setParent(rootContext); | |
} | |
// 初始化SpringIOC容器 | |
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 | |
wac = findWebApplicationContext(); | |
} | |
if (wac == null) { | |
// No context instance is defined for this servlet -> create a local one | |
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. | |
onRefresh(wac); | |
} | |
if (this.publishContext) { | |
// Publish the context as a servlet context attribute. | |
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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// DispatcherServlet结构中的一层 | |
public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware { | |
public final void init() throws ServletException { | |
if (logger.isDebugEnabled()) { | |
logger.debug("Initializing servlet '" + getServletName() + "'"); | |
} | |
// 给Servlet的初始化参数配置成property Bean | |
// Set bean properties from init parameters. | |
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); | |
if (!pvs.isEmpty()) { | |
try { | |
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); | |
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); | |
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment())); | |
initBeanWrapper(bw); | |
bw.setPropertyValues(pvs, true); | |
} | |
catch (BeansException ex) { | |
if (logger.isErrorEnabled()) { | |
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex); | |
} | |
throw ex; | |
} | |
} | |
// 调用 FrameworkServlet.initServletBean() | |
// Let subclasses do whatever initialization they like. | |
initServletBean(); | |
if (logger.isDebugEnabled()) { | |
logger.debug("Servlet '" + getServletName() + "' configured successfully"); | |
} | |
} | |
private static class ServletConfigPropertyValues extends MutablePropertyValues { | |
/** | |
* Create new ServletConfigPropertyValues. | |
* @param config ServletConfig we'll use to take PropertyValues from | |
* @param requiredProperties set of property names we need, where | |
* we can't accept default values | |
* @throws ServletException if any required properties are missing | |
*/ | |
// #10 调用 | |
public ServletConfigPropertyValues(ServletConfig config, Set<String> requiredProperties) | |
throws ServletException { | |
Set<String> missingProps = (!CollectionUtils.isEmpty(requiredProperties) ? | |
new HashSet<>(requiredProperties) : null); | |
// 检查Initparam中是否包含所有需要的参数 | |
Enumeration<String> paramNames = config.getInitParameterNames(); | |
while (paramNames.hasMoreElements()) { | |
String property = paramNames.nextElement(); | |
Object value = config.getInitParameter(property); | |
addPropertyValue(new PropertyValue(property, value)); | |
if (missingProps != null) { | |
missingProps.remove(property); | |
} | |
} | |
// Fail if we are still missing properties. | |
if (!CollectionUtils.isEmpty(missingProps)) { | |
throw new ServletException( | |
"Initialization from ServletConfig for servlet '" + config.getServletName() + | |
"' failed; the following required properties were missing: " + | |
StringUtils.collectionToDelimitedString(missingProps, ", ")); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@HandlesTypes(WebApplicationInitializer.class) | |
public class SpringServletContainerInitializer implements ServletContainerInitializer { | |
@Override | |
// 所有实现了WebApplicationInitializer接口的类,都会被web容器初始化的时候扫描到, | |
// 并在调用ServletContainerInitializer实现类的onStartup()方法时传入 | |
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) | |
throws ServletException { | |
List<WebApplicationInitializer> initializers = new LinkedList<>(); | |
if (webAppInitializerClasses != null) { | |
for (Class<?> waiClass : webAppInitializerClasses) { | |
// 只初始化实现类实现类,忽略抽象类和接口 | |
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); | |
for (WebApplicationInitializer initializer : initializers) { | |
//调用 WebApplicationInitializer实现类的onStartup方法帮助ServletContext完成初始化 | |
initializer.onStartup(servletContext); | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
SpringServletContainerInitializer实现了ServletContailerInitializer接口, | |
当servlet容器启动时,会通知实现了ServletContailerInitializer接口的类调用onStatrtup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)方法 | |
servlet容器会自动扫描classpath, 把所有实现了WebApplicationInitializer.class接口的实现类都当作参数webAppInitializerClasses传给onStartup方法 | |
onStartup方法的目的是把servletContext委托给所有WebApplicationInitializer实现类,(Delegate the ServletContext to any WebApplicationInitializer implementations present on the application classpath),去初始化各个WebApplicationInitializer实现类中来完成servletContext的初始化. | |
首先initializers.add((WebApplicationInitializer) | |
ReflectionUtils.accessibleConstructor(waiClass).newInstance()); 反射创建一个WebApplicationInitializer实现类(非抽象类,非接口)的实例 | |
然后调用WebApplicationInitializer实现类实例的onStartup()方法,目的是给servletContext继续装配filter,,servlet,listener, context-params 和 attributes. | |
这里我们创建了一个类(webAppInitializer)拓展了AbstractAnnotationConfigDispatcherServletInitializer,AbstractAnnotationConfigDispatcherServletInitializer又拓展了AbstractDispatcherServletInitializer.所以实际调用的是AbstractDispatcherServletInitializer的onStartup()方法 | |
1.调用父类AbstractContextLoaderInitializer中的onStartup(),实际调用registerContextLoaderListener()方法使用servletContext.addListener(listener);注册到servletContext | |
2.调用registerDispatcherServlet(),实例化一个DispatcherServlet,并用setContextInitializers()给Servlet配置初始化的参数,通过servletContext.addServlet()注册到servletContext | |
ContextLoaderListener继承了ContextLoader并实现了ServletContextListener接口,当web应用启动时,会通知所有实现了ServletContextListener接口的类,去调用contextInitialized(ServletContextEvent sce)方法,所以ContextLoaderListene的contextInitialized会被调用,用来初始化webApplicationContext(springIOC容器) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
厉害