zoukankan      html  css  js  c++  java
  • Spring MVC 上下文(ApplicationContext)初始化入口

    Spring 常用上下文容器有哪些

    ApplicationContext

    1. ClassPathXmlApplicationContext

      ApplicationContext context = new ClassPathXmlApplicationContext(applicationContext.xml");
      
    2. AnnotationConfigApplicationContext

      AbstractApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
      

    应该来说是很少使用这种方法用于生产开发,常常在学习Spring做demo的时候会使用到。更有可能出现在Spring项目的代码测试,不过呢,单元测试的框架(比如 JUnit)已经提供了简单的方式,也就不建议直接实例化上下文。因为实例化一个上下文还得要做维护,再者现在常用的是基于Web的开发,也就是常用 Spring MVC。如果没有基于 Web 应用的开发,那么很可能就是一个小的程序,类似于提供给第三方使用的 SDK 代码,那么使用 Spring 感觉会太重,最重要是自己要维护一个 ApplicationContext,感觉不是那么方便

    Web ApplicationContext

    以下两个是针对 Spring MVC 的应用上下文。WebApplicationContext 实例会在应用启动之后由Spring实例化并维护,而平常在学习的时候也往往不会自己去实例化 WebApplicationContext 对象,因为将因为部署到web容器(比如 tomcat),启动之后就可以直接测试了。单元测试有专门的框架处理(比如 JUnit),可以很简单的实现测试。web项目的开发关键点在于让web容器初始化之后提醒Spring ApplicationContext 初始化,例如 tomcat 的 ServletContext 会维护一个 WebApplicationContext。

    1. XmlWebApplicationContext

      @Test
      public void handlerBeanNotFound() throws Exception {
      	MockServletContext sc = new MockServletContext("");
      	XmlWebApplicationContext root = new XmlWebApplicationContext();
      	root.setServletContext(sc);
      	root.setConfigLocations(new String[] {"/org/springframework/web/servlet/handler/map1.xml"});
      	root.refresh();
      	XmlWebApplicationContext wac = new XmlWebApplicationContext();
      	wac.setParent(root);
      	wac.setServletContext(sc);
      	wac.setNamespace("map2err");
      	wac.setConfigLocations(new String[] {"/org/springframework/web/servlet/handler/map2err.xml"});
      	try {
      		wac.refresh();
      		fail("Should have thrown NoSuchBeanDefinitionException");
      	}
      	catch (FatalBeanException ex) {
      		NoSuchBeanDefinitionException nestedEx = (NoSuchBeanDefinitionException) ex.getCause();
      		assertEquals("mainControlle", nestedEx.getBeanName());
      	}
      }
      
    2. AnnotationConfigWebApplicationContext:没找到合适的示例代码

    ApplicationContext 入口

    这种方式需要自己维护 ApplicationContext 实例,也就是开发使用的时候 new ApplicationContext,入口自己控制

    WebApplicationContext 入口

    web.xml 配置和 全注解 配置启动会有一些差别。

    web.xml 配置

    web.xml 中关于Spring的配置项,也非常常见。

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    
    	<!-- 上下文参数,在监听器中被使用,实际就是key-value,key=contextConfigLocation 写死 -->
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>
                classpath:applicationContext.xml
            </param-value>
        </context-param>
    
        <!-- 监听器配置,初始化 WebApplicationContext -->
        <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
    
        <!-- 前端控制器配置 -->
        <servlet>
            <servlet-name>dispatcher</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:applicationContext-mvc.xml</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
    
        <servlet-mapping>
            <servlet-name>dispatcher</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    
    </web-app>
    

    WebApplicationContext 的初始化调用链路:ContextLoaderListener.contextInitialized-->ContextLoader.initWebApplicationContext-->ContextLoader.createWebApplicationContext-->ContextLoader.determineContextClass-->ContextLoader.determineContextClass。

    determineContextClass 源码如下:

    protected Class<?> determineContextClass(ServletContext servletContext) {
        // 自定义的 ApplicationContext
        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 {
            // 缺省为 XmlWebApplicationContext
            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);
            }
        }
    }
    

    默认 WebApplicationContext

    根据上面的 web.xml 配置是没有指定 ApplicationContext 的实现的,所以会执行:contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());。defaultStrategies 的实现如下:

    /**
    * 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 {
        // 读取文件 ContextLoader.properties 中的配置
        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());
        }
    }
    

    根据 ContextLoader.properties 中的配置完成,里边的内容如下:

    org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
    

    所以呢,通过 web.xml 配置Spring MVC默认的上下文是: XmlWebApplicationContext

    指定 WebApplicationContext

    如果在 web.xml 中配置 contextClass 属性,例如下面的方式,摘自 StackOverflow:How to register Spring @Configuration annotated class instead of applicationContext.xml file in web.xml?

    <web-app>
      <!-- Configure ContextLoaderListener to use AnnotationConfigWebApplicationContext
           instead of the default XmlWebApplicationContext -->
      <context-param>
          <param-name>contextClass</param-name>
          <param-value>
              org.springframework.web.context.support.AnnotationConfigWebApplicationContext
          </param-value>
      </context-param>
    
      <!-- Configuration locations must consist of one or more comma- or space-delimited
           fully-qualified @Configuration classes. Fully-qualified packages may also be
           specified for component-scanning -->
      <context-param>
          <param-name>contextConfigLocation</param-name>
          <param-value>com.acme.AppConfig</param-value>
      </context-param>
    
      <!-- Bootstrap the root application context as usual using ContextLoaderListener -->
      <listener>
          <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
      </listener>
    
      <!-- Declare a Spring MVC DispatcherServlet as usual -->
      <servlet>
          <servlet-name>dispatcher</servlet-name>
          <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
          <!-- Configure DispatcherServlet to use AnnotationConfigWebApplicationContext
               instead of the default XmlWebApplicationContext -->
          <init-param>
              <param-name>contextClass</param-name>
              <param-value>
                  org.springframework.web.context.support.AnnotationConfigWebApplicationContext
              </param-value>
          </init-param>
          <!-- Again, config locations must consist of one or more comma- or space-delimited
               and fully-qualified @Configuration classes -->
          <init-param>
              <param-name>contextConfigLocation</param-name>
              <param-value>com.acme.web.MvcConfig</param-value>
          </init-param>
      </servlet>
    
      <!-- map all requests for /app/* to the dispatcher servlet -->
      <servlet-mapping>
          <servlet-name>dispatcher</servlet-name>
          <url-pattern>/app/*</url-pattern>
      </servlet-mapping>
    </web-app>
    

    感觉很神奇,但是也很麻烦的样子。我是不建议混合 web.xml 配置启动和全注解启动,乱且不好看懂。

    全注解配置

    全注解方式一般是实现WebApplicationInitializer或者通过继承AbstractAnnotationConfigDispatcherServletInitializerAbstractAnnotationConfigDispatcherServletInitializerWebApplicationInitializer的实现类。想知道全注解配置下tomcat如何Spring IOC怎样被加载,可以阅读篇文章Spring揭秘--寻找遗失的web.xml

    全注解方式配置常用类似如下的代码:

    import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
    
    /**
     * @author imssbora
     */
    public class MyWebAppInitializer extends 
       AbstractAnnotationConfigDispatcherServletInitializer{
    
       @Override
       protected Class<?>[] getRootConfigClasses() {
          return new Class[]{RootConfig.class};
       }
    
       @Override
       protected Class<?>[] getServletConfigClasses() {
          return new Class[]{WebConfig.class};
       }
    
       @Override
       protected String[] getServletMappings() {
          return new String[]{"/"};
       }
    }
    

    启动路径:SpringServletContainerInitializer.onStartup-->AbstractContextLoaderInitializer.onStartup-->AbstractContextLoaderInitializer.registerContextLoaderListener-->AbstractAnnotationConfigDispatcherServletInitializer.createRootApplicationContext。

    createRootApplicationContext 源码如下:

    protected WebApplicationContext createRootApplicationContext() {
        Class<?>[] configClasses = this.getRootConfigClasses();
        if (!ObjectUtils.isEmpty(configClasses)) {
            AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext();
            rootAppContext.register(configClasses);
            return rootAppContext;
        } else {
            return null;
        }
    }
    

    可以看出实例化了 AnnotationConfigWebApplicationContext 对象。

    参考

    关于web.xml配置启动,Spring 的加载流程网络上资料很多,所以有可能会有很多重复的,选择一遍排版不错,写得相对完整的,编写时间比较新的:Spring MVC 启动过程源码分析

    排版精美,写得很棒的文章:Spring揭秘--寻找遗失的web.xml

    从 StackOverflow 上找到的资料:How to register Spring @Configuration annotated class instead of applicationContext.xml file in web.xml?

    我的博客即将搬运同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=38db6z9qc328s

    时间:2018.9.3 16:20

  • 相关阅读:
    MHA-Atlas-MySQL高可用(上)
    MySQL数据库企业级应用实践(主从复制)
    MySQL数据库企业级应用实践(多实例源码编译)
    MySQL存储引擎
    MySQL索引与事务
    MySQL数据备份
    MySQL数据库操作
    bzoj 1038: [ZJOI2008]瞭望塔
    bzoj 2451 Uyuw's Concert
    poj 2187 Beauty Contest
  • 原文地址:https://www.cnblogs.com/xiaoheike/p/9574240.html
Copyright © 2011-2022 走看看