学习学习容器初始化,若有不对的地方,请指出更正,大家共同学习学习。
此篇幅主要围绕着 ContextLoaderListener加载容器,理解其中的原理。
ContextLoaderListener的作用就是启动Web容器时,自动装配ApplicationContext的配置信息。
因为它实现了ServletContextListener这个接口,在web.xml配置这个监听器,启动容器时,就会默认执行它实现的方法。
ContextLoaderListener启动的上下文为根上下文,DispatcherServlet所创建的上下文的的父上下文即为此根上下文,可在FrameworkServlet中的initWebApplicationContext中看出。
通常在web.xml中如下配置:
- <context-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>classpath*:server_spring.xml
- </param-value>
- </context-param>
- <listener>
- <listener-class>org.springframework.web.context.ContextLoaderListener
- </listener-class>
- </listener>
层次结构
- /**
- * Receives notification that the web application initialization
- * process is starting.
- *
- * <p>All ServletContextListeners are notified of context
- * initialization before any filters or servlets in the web
- * application are initialized.
- *
- * @param sce the ServletContextEvent containing the ServletContext
- * that is being initialized
- */
- public void contextInitialized(ServletContextEvent sce);
- /**
- * Receives notification that the ServletContext is about to be
- * shut down.
- *
- * <p>All servlets and filters will have been destroyed before any
- * ServletContextListeners are notified of context
- * destruction.
- *
- * @param sce the ServletContextEvent containing the ServletContext
- * that is being destroyed
- */
- public void contextDestroyed(ServletContextEvent sce);
- /**
- * Initialize the root web application context.
- */
- @Override
- public void contextInitialized(ServletContextEvent event) {
- initWebApplicationContext(event.getServletContext());
- }
初始化root跟web上下文,initWebApplicationContext方法在其父类ContextLoader中提供实现。
- /**
- * Initialize Spring's web application context for the given servlet context,
- * using the application context provided at construction time, or creating a new one
- * according to the "{@link #CONTEXT_CLASS_PARAM contextClass}" and
- * "{@link #CONFIG_LOCATION_PARAM contextConfigLocation}" context-params.
- * @param servletContext current servlet context
- * @return the new WebApplicationContext
- * @see #ContextLoader(WebApplicationContext)
- * @see #CONTEXT_CLASS_PARAM
- * @see #CONFIG_LOCATION_PARAM
- */
- public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
- //这里判断是否在ServletContext中存在上下文,如果有,说明已载入过或配置文件出错,可以从错误信息中看出
- if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != ) {
- 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.
- if (this.context == ) {
- 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() == ) {
- // 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);
- }
- //这里从web.xml中取得相关的初始化参数,对WebApplicationContext进行初始化
- configureAndRefreshWebApplicationContext(cwac, 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 != ) {
- 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;
- }
- }
根据提供的servlet上下文去初始化Spring的web应用上下文,在构造时使用当前应用上下文或者在web.xml中配置参数contextClass和contextConfigLocation去创建新的上下文。
- /**
- * Instantiate the root WebApplicationContext for this loader, either the
- * default context class or a custom context class if specified.
- * <p>This implementation expects custom contexts to implement the
- * {@link ConfigurableWebApplicationContext} interface.
- * Can be overridden in subclasses.
- * <p>In addition, {@link #customizeContext} gets called prior to refreshing the
- * context, allowing subclasses to perform custom modifications to the context.
- * @param sc current servlet context
- * @return the root WebApplicationContext
- * @see ConfigurableWebApplicationContext
- */
- protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
- //这里需要确定我们载入的根WebApplication的类型,
- //由在web.xml中配置的contextClass中配置的参数, 如果没有使用默认的。
- Class<?> contextClass = determineContextClass(sc);
- //contextClass必须实现ConfigurableWebApplicationContext,否则抛异常
- if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
- throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
- "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
- }
- //初始化WebApplication,强转成ConfigurableWebApplicationContext
- return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
- }
初始化根上下文,
- /**
- * Return the WebApplicationContext implementation class to use, either the
- * default XmlWebApplicationContext or a custom context class if specified.
- * @param servletContext current servlet context
- * @return the WebApplicationContext implementation class to use
- * @see #CONTEXT_CLASS_PARAM
- * @see org.springframework.web.context.support.XmlWebApplicationContext
- */
- protected Class<?> determineContextClass(ServletContext servletContext) {
- String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
- if (contextClassName != ) {
- try {
- return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
- }
- catch (ClassNotFoundException ex) {
- throw new ApplicationContextException(
- "Failed to load custom context class [" + contextClassName + "]", ex);
- }
- }
- else {
- 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);
- }
- }
- }
- /**
- * Name of the class path resource (relative to the ContextLoader class)
- * that defines ContextLoader's default strategy names.
- */
- 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());
- }
- }
- # Default WebApplicationContext implementation class for ContextLoader.
- # Used as fallback when no explicit context implementation has been specified as context-param.
- # Not meant to be customized by application developers.
- org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
- /**
- * Template method with default implementation (which may be overridden by a
- * subclass), to load or obtain an ApplicationContext instance which will be
- * used as the parent context of the root WebApplicationContext. If the
- * return value from the method is null, no parent context is set.
- * <p>The main reason to load a parent context here is to allow multiple root
- * web application contexts to all be children of a shared EAR context, or
- * alternately to also share the same parent context that is visible to
- * EJBs. For pure web applications, there is usually no need to worry about
- * having a parent context to the root web application context.
- * <p>The default implementation uses
- * {@link org.springframework.context.access.ContextSingletonBeanFactoryLocator},
- * configured via {@link #LOCATOR_FACTORY_SELECTOR_PARAM} and
- * {@link #LOCATOR_FACTORY_KEY_PARAM}, to load a parent context
- * which will be shared by all other users of ContextsingletonBeanFactoryLocator
- * which also use the same configuration parameters.
- * @param servletContext current servlet context
- * @return the parent application context, or {@code null} if none
- * @see org.springframework.context.access.ContextSingletonBeanFactoryLocator
- */
- protected ApplicationContext loadParentContext(ServletContext servletContext) {
- ApplicationContext parentContext = ;
- String locatorFactorySelector = servletContext.getInitParameter(LOCATOR_FACTORY_SELECTOR_PARAM);
- String parentContextKey = servletContext.getInitParameter(LOCATOR_FACTORY_KEY_PARAM);
- if (parentContextKey != ) {
- // 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;
- }
根据在web.xml中配置的locatorFactorySelector和parentContextKey来给根web应用上下设置父上下文,如果没配置的话,父上下文为空。
1.WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();
当前应用的WebApplicationContext就保存在 ContextLoader的currentContextPerThread属性当中
2.基于ServletContext上下文获取的方式
ServletContext sc = request.getSession().getServletContext();
ApplicationContext ac1 = WebApplicationContextUtils.getRequiredWebApplicationContext(sc);
ApplicationContext ac2 = WebApplicationContextUtils.getWebApplicationContext(sc);
WebApplicationContext wac1 = (WebApplicationContext) sc.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
3.还有一些更合适的,基于Spring提供的抽象类或者接口,在初始化Bean时注入ApplicationContext
3.1:继承自抽象类ApplicationObjectSupport
说明:抽象类ApplicationObjectSupport提供getApplicationContext()方法,可以方便的获取到ApplicationContext。
Spring初始化时,会通过该抽象类的setApplicationContext(ApplicationContext context)方法将ApplicationContext 对象注入。
3.2:继承自抽象类WebApplicationObjectSupport
说明:类似上面方法,调用getWebApplicationContext()获取WebApplicationContext
3.3:实现接口ApplicationContextAware
说明:实现该接口的setApplicationContext(ApplicationContext context)方法,并保存ApplicationContext 对象。