/** * 这里是设置默认BeanDefinition的地方,如果不特殊指定其他文件,IoC容器会从/WEB-INF/applicationContext.xml文件 * 读取BeanDefinition来初始化IoC容器。 * */ public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml"; /** Default prefix for building a config location for a namespace */ public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/"; /** Default suffix for building a config location for a namespace */ public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml"; /** * 这里是取得Resource位置的地方,使用默认的配置位/WEB-INF/applicationContext.xml。 */ @Override protected String[] getDefaultConfigLocations() { if (getNamespace() != null) { return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX}; } else { return new String[] {DEFAULT_CONFIG_LOCATION}; } } /** * 这个加载过程在容器refresh()时启动 */ @Override protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // Create a new XmlBeanDefinitionReader for the given BeanFactory. // 对于XmlWebApplicationContext,当然是使用XmlBeanDefinitionReader来对BeanDefinition信息进行解析。 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // 因为XmlWebApplicationContext是DefaultResourceLoader的子类, // 所以这里同样使用DefaultResourceLoader来定位BeanDefinition。 beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // 钩子,允许子类自定义XmlBeanDefinitionReader的属性配置 initBeanDefinitionReader(beanDefinitionReader); //这里使用定义好的XmlBeanDefinitionReader来载入BeanDefinition loadBeanDefinitions(beanDefinitionReader); } /** * 如果有多个BeanDefinition文件的定义,需逐个载入,都是通过reader来完成的 * 这个初始化过程是由refreshBeanFactory方法来完成的,这里只负责载入BeanDefinition。 */ protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException { String[] configLocations = getConfigLocations(); if (configLocations != null) { for (String configLocation : configLocations) { reader.loadBeanDefinitions(configLocation); } } }
public interface ServletContextListener extends EventListener { //服务器启动的时候初始化入口 public void contextInitialized ( ServletContextEvent sce ); //服务器销毁的时候销毁入口 public void contextDestroyed ( ServletContextEvent sce ); }
/** * Initialize the root web application context. */ public void contextInitialized(ServletContextEvent event) { //这个方法实现的本意是提供一个占位符方法createContextLoader()给子类机会创建客户化的环境加载, //但是,后来这个证明不是非常有用的,已经鼓励不再使用了,事实上,子类可以通过重写本方法达到同样的目的 this.contextLoader = createContextLoader(); //实际上是为了使用超类的默认实现 if (this.contextLoader == null) { this.contextLoader = this; } //调用超类的加载根共享Web应用程序环境的默认实现 this.contextLoader.initWebApplicationContext(event.getServletContext()); }
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { //如果已经存在了根共享Web应用程序环境,则抛出异常提示客户 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"); } //记录创建根Web应用程序环境的开始时间 long startTime = System.currentTimeMillis(); try { //决定根Web应用程序环境是否存在父应用程序环境,一般是返回null ApplicationContext parent = loadParentContext(servletContext); //创建根Web应用程序环境,如果父环境存在则引用父环境,通常情况下父环境是不存在的 即parent=null // 这是一个比较重要的方法,在第三步说明 this.context = createWebApplicationContext(servletContext, parent); //把创建的根Web应用程序环境保存到Servlet环境中,每个派遣器Servlet加载的子环境会应用这个环境作为父环境 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) { //如果线程和本类拥有不同的类加载器,则使用线程的类加载器作为关键在保存在一个映射对象里,保证析构时能拿到Web应用程序环境进行关闭操作 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); //如果产生任何异常,则保存异常对象到Servlet环境里 servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex); throw ex; } catch (Error err) { logger.error("Context initialization failed", err); //如果产生任何错误,则保存错误对象到Servlet环境里 servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err); throw err; } }
protected WebApplicationContext createWebApplicationContext(ServletContext sc, ApplicationContext parent) { //检测是否有配置的Web应用程序环境类(即存放bean的容器类),如果没有配置,则使用缺省的类XmlWebApplicationContext // 例如在web.xml中配置 // <context-param> // <param-name>contextClass</param-name> // <param-value>org.springframework.web.context.support.GenericWebApplicationContext</param-value> // </context-param> Class<?> contextClass = determineContextClass(sc); //如果配置的Web应用程序环境类不是可配置的Web应用程序环境的子类,则抛出异常,停止初始化 //若按上面的配置,这里会抛出异常,因为不是ConfigurableWebApplicationContext的子类 if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]"); } //实例化Web应用程序环境类 ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); //设置Web应用程序环境的ID if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) { //如果 Servlet规范 <= 2.4,则使用web.xml里定义的应用程序名字定义Web应用程序名 String servletContextName = sc.getServletContextName(); wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(servletContextName)); } else { // 如果Servlet规范是 2.5, 则使用配置的ContextPath定义Web应用程序名 try { String contextPath = (String) ServletContext.class.getMethod("getContextPath").invoke(sc); wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(contextPath)); } catch (Exception ex) { throw new IllegalStateException("Failed to invoke Servlet 2.5 getContextPath method", ex); } } //如果父环境存在,则引用使用父环境 wac.setParent(parent); //保存Servlet环境 wac.setServletContext(sc); //设置根环境XML文件存放的位置 //这里如果以下配置容器启动不会报错,但若不配置,则会去寻找/WEB-INF/applicationContext.xml,若该文件不存在就则抛出FileNotFound异常 //<context-param> // <param-name>contextConfigLocation</param-name> // <param-value></param-value> //</context-param> wac.setConfigLocation(sc.getInitParameter(CONFIG_LOCATION_PARAM)); //提供子类可互换Web应用程序环境的机会 占位符方法 customizeContext(sc, wac); //刷新Web应用程序环境以加载Bean定义,这里才是把我们XML里定义的bean放入容器的时候 wac.refresh(); return wac; }