Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save qqyycom/1bda7c102d8c74ec58a6166208ac1975 to your computer and use it in GitHub Desktop.
Save qqyycom/1bda7c102d8c74ec58a6166208ac1975 to your computer and use it in GitHub Desktop.
springMVC中servletContext的初始化过程
// 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();
}
//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;
}
}
// 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) {
}
}
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;
}
@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;
}
}
// 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, ", "));
}
}
}
}
@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);
}
}
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容器)
@xch900910
Copy link

厉害

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment