此文为转载,地址:http://blog.csdn.net/qq924862077/
在spring Web中,需要初始化IOC容器,用于存放我们注入的各种对象。当tomcat启动时首先会初始化一个web对应的IOC容器,用于初始化和注入各种我们在web运行过程中需要的对象。当tomcat启动的时候是如何初始化IOC容器的,我们先看一下在web.xml中经常看到的配置:
- <context-param>
- <param-name>contextCon</param-name>
- <param-value>
- classpath:applicationContext.xml
- </param-value>
- </context-param>
- <listener>
- <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
- </listener>
- public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
- public ContextLoaderListener() {
- }
- public ContextLoaderListener(WebApplicationContext context) {
- super(context);
- }
- /**
- * Initialize the root web application context.
- */
- @Override
- public void contextInitialized(ServletContextEvent event) {
- //在父类ContextLoader中实现
- initWebApplicationContext(event.getServletContext());
- }
- /**
- * Close the root web application context.
- */
- @Override
- public void contextDestroyed(ServletContextEvent event) {
- closeWebApplicationContext(event.getServletContext());
- ContextCleanupListener.cleanupAttributes(event.getServletContext());
- }
- }
ContextLoaderListener的方法contextInitialized()的默认实现是在他的父类ContextLoader的initWebApplicationContext方法中实现的,意思就是初始化web应用上下文。他的主要流程就是创建一个IOC容器,并将创建的IOC容器存到servletContext中,ContextLoader的核心实现如下:
initWebApplicationContext函数:
- //初始化WebApplicationContext,IOC容器
- public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
- //判断web容器中是否已经有WebApplicationContext
- 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 {
- // Store context in local instance variable, to guarantee that
- // it is available on ServletContext shutdown.
- //创建WebApplicationContext
- 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.
- //获得父容器
- ApplicationContext parent = loadParentContext(servletContext);
- cwac.setParent(parent);
- }
- //设置并刷新WebApplicationContext容器
- configureAndRefreshWebApplicationContext(cwac, servletContext);
- }
- }
- //将初始化的WebApplicationContext设置到servletContext中
- 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");
- }
- //初始化 WebApplicationContext完成并返回
- 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;
- }
- }
springMVC父容器初始化流程图(盗图)如下:
完整的ContextLoader源码如下:
- /**
- * 用于初始化ApplicationContext
- *
- */
- public class ContextLoader {
- //WebApplicationContext id
- public static final String CONTEXT_ID_PARAM = "contextId";
- //配置文件
- public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation";
- public static final String CONTEXT_CLASS_PARAM = "contextClass";
- public static final String CONTEXT_INITIALIZER_CLASSES_PARAM = "contextInitializerClasses";
- public static final String GLOBAL_INITIALIZER_CLASSES_PARAM = "globalInitializerClasses";
- public static final String LOCATOR_FACTORY_SELECTOR_PARAM = "locatorFactorySelector";
- public static final String LOCATOR_FACTORY_KEY_PARAM = "parentContextKey";
- private static final String INIT_PARAM_DELIMITERS = ",; \t\n";
- private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";
- private static final Properties defaultStrategies;
- static {
- // Load default strategy implementations from properties file.
- // This is currently strictly internal and not meant to be customized
- // by application developers.
- try {
- ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
- defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
- }
- catch (IOException ex) {
- throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
- }
- }
- private static final Map<ClassLoader, WebApplicationContext> currentContextPerThread =
- new ConcurrentHashMap<ClassLoader, WebApplicationContext>(1);
- private static volatile WebApplicationContext currentContext;
- private WebApplicationContext context;
- private BeanFactoryReference parentContextRef;
- private final List<ApplicationContextInitializer<ConfigurableApplicationContext>> contextInitializers =
- new ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>>();
- public ContextLoader() {
- }
- public ContextLoader(WebApplicationContext context) {
- this.context = context;
- }
- @SuppressWarnings("unchecked")
- public void setContextInitializers(ApplicationContextInitializer<?>... initializers) {
- if (initializers != null) {
- for (ApplicationContextInitializer<?> initializer : initializers) {
- this.contextInitializers.add((ApplicationContextInitializer<ConfigurableApplicationContext>) initializer);
- }
- }
- }
- //初始化WebApplicationContext,IOC容器
- public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
- //判断web容器中是否已经有WebApplicationContext
- 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 {
- // Store context in local instance variable, to guarantee that
- // it is available on ServletContext shutdown.
- //创建WebApplicationContext
- 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.
- //获得父容器
- ApplicationContext parent = loadParentContext(servletContext);
- cwac.setParent(parent);
- }
- //设置并刷新WebApplicationContext容器
- configureAndRefreshWebApplicationContext(cwac, servletContext);
- }
- }
- //将初始化的WebApplicationContext设置到servletContext中
- 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");
- }
- //初始化 WebApplicationContext完成并返回
- 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;
- }
- }
- //在web容器加载的时候初始化根WebApplicationContext
- protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
- //通过反射获得上下文类
- Class<?> contextClass = determineContextClass(sc);
- if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
- throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
- "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
- }
- //初始化WebApplicationContext,是其实现类XmlWebApplicationContext
- return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
- }
- protected Class<?> determineContextClass(ServletContext servletContext) {
- //查看servlet是否已经存在应用上下文类名
- String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
- if (contextClassName != null) {
- try {
- //类名不为空则反射获得类
- return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
- }
- catch (ClassNotFoundException ex) {
- throw new ApplicationContextException(
- "Failed to load custom context class [" + contextClassName + "]", ex);
- }
- }
- else {
- //如果应用上下文类名为空,则获得默认的context上下文名
- contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
- try {
- return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
- }
- catch (ClassNotFoundException ex) {
- throw new ApplicationContextException(
- "Failed to load default context class [" + contextClassName + "]", ex);
- }
- }
- }
- 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);
- //设置应用上下文id,在web.xml中可以进行配置
- if (idParam != null) {
- wac.setId(idParam);
- }
- else {
- // Generate default id...
- //如果没有配置则设置默认名称
- wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
- ObjectUtils.getDisplayString(sc.getContextPath()));
- }
- }
- //设置ServletContext到容器中
- wac.setServletContext(sc);
- //获得web.xml中配置的contextConfigLocation的值,一般是classpath:applicationContext.xml
- String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
- if (configLocationParam != null) {
- //设置加载文件地址
- wac.setConfigLocation(configLocationParam);
- }
- // 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);
- }
- customizeContext(sc, wac);
- //这一步是特别关键的,这样application.xml中的所有配置都会被初始化到XMLWebApplication中
- wac.refresh();
- }
- protected void customizeContext(ServletContext sc, ConfigurableWebApplicationContext wac) {
- List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses =
- determineContextInitializerClasses(sc);
- for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) {
- Class<?> initializerContextClass =
- GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
- if (initializerContextClass != null) {
- Assert.isAssignable(initializerContextClass, wac.getClass(), String.format(
- "Could not add 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));
- }
- AnnotationAwareOrderComparator.sort(this.contextInitializers);
- for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : this.contextInitializers) {
- initializer.initialize(wac);
- }
- }
- protected List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>>
- determineContextInitializerClasses(ServletContext servletContext) {
- List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> classes =
- new ArrayList<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>>();
- 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("unchecked")
- private Class<ApplicationContextInitializer<ConfigurableApplicationContext>> loadInitializerClass(String className) {
- try {
- Class<?> clazz = ClassUtils.forName(className, ClassUtils.getDefaultClassLoader());
- Assert.isAssignable(ApplicationContextInitializer.class, clazz);
- return (Class<ApplicationContextInitializer<ConfigurableApplicationContext>>) clazz;
- }
- catch (ClassNotFoundException ex) {
- throw new ApplicationContextException("Failed to load context initializer class [" + className + "]", ex);
- }
- }
- //从ServletContext中获取父容器
- protected ApplicationContext loadParentContext(ServletContext servletContext) {
- ApplicationContext parentContext = null;
- String locatorFactorySelector = servletContext.getInitParameter(LOCATOR_FACTORY_SELECTOR_PARAM);
- String parentContextKey = servletContext.getInitParameter(LOCATOR_FACTORY_KEY_PARAM);
- if (parentContextKey != null) {
- // locatorFactorySelector may be null, indicating the default "classpath*:beanRefContext.xml"
- BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector);
- Log logger = LogFactory.getLog(ContextLoader.class);
- if (logger.isDebugEnabled()) {
- logger.debug("Getting parent context definition: using parent context key of '" +
- parentContextKey + "' with BeanFactoryLocator");
- }
- this.parentContextRef = locator.useBeanFactory(parentContextKey);
- parentContext = (ApplicationContext) this.parentContextRef.getFactory();
- }
- return parentContext;
- }
- //通过给定的ServletContext来关闭IOC容器
- public void closeWebApplicationContext(ServletContext servletContext) {
- servletContext.log("Closing Spring root WebApplicationContext");
- try {
- if (this.context instanceof ConfigurableWebApplicationContext) {
- ((ConfigurableWebApplicationContext) this.context).close();
- }
- }
- finally {
- ClassLoader ccl = Thread.currentThread().getContextClassLoader();
- if (ccl == ContextLoader.class.getClassLoader()) {
- currentContext = null;
- }
- else if (ccl != null) {
- currentContextPerThread.remove(ccl);
- }
- servletContext.removeAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
- if (this.parentContextRef != null) {
- this.parentContextRef.release();
- }
- }
- }
- //在当前线程中获取WebApplicationContext
- public static WebApplicationContext getCurrentWebApplicationContext() {
- ClassLoader ccl = Thread.currentThread().getContextClassLoader();
- if (ccl != null) {
- WebApplicationContext ccpt = currentContextPerThread.get(ccl);
- if (ccpt != null) {
- return ccpt;
- }
- }
- return currentContext;
- }
- }