zoukankan      html  css  js  c++  java
  • Spring 源码分析-1-启动

    Spring 源码分析-1-启动

    在web项目中使用spring的时候,我们会在web.xml中加入如下配置:

    	<listener>
    		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    	</listener>
    

    这个配置增加了一个listener,这个ContextLoaderListener 实现了ServletContextListener 。我们在日常工作中也会定义一些listener。用于在应用启动的时候做些什么。因为我们知道servelt容器在启动的时候,Listener 类中的contextInitialized()方法将会被调用。spring中ContextLoaderListener也把这个作为起始点来初始化,contextInitialized()方法的实现如下:

    public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
      	private ContextLoader contextLoader;
    	public void contextInitialized(ServletContextEvent event) {
    		this.contextLoader = createContextLoader();//此方法直接return null了
    		if (this.contextLoader == null) {
    			this.contextLoader = this;
    		}
    		this.contextLoader.initWebApplicationContext(event.getServletContext());
    	}
    	@Deprecated
    	protected ContextLoader createContextLoader() {
    		return null;
    	}
    }
    

    根据上面的代码,在调用contextInitialized方法里边的代码很少,先是给contextLoader赋值了,然后调用了initWebApplicationContext方法。这个方法就是我们窥探的入口,它是在ContextLoader类中的,代码如下,可以先不要读这个代码,大概扫一眼,然后继续根据后面的文字描述跟踪逻辑:

    //为了方便初步的阅读,我删除了一些占用篇幅的地方
    public class ContextLoader {
      public static final String CONTEXT_CLASS_PARAM = "contextClass";
      public static final String CONTEXT_ID_PARAM = "contextId";
      public static final String CONTEXT_INITIALIZER_CLASSES_PARAM = "contextInitializerClasses";
      public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation";//web.xml里有,熟悉吧
      public static final String LOCATOR_FACTORY_SELECTOR_PARAM = "locatorFactorySelector";
      public static final String LOCATOR_FACTORY_KEY_PARAM = "parentContextKey";
      private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";
      private static final Properties defaultStrategies;
      
      	static {
          //这个静态代码块读取了ContextLoader.properties这个配置文件。在spring源码中可以找到这个配置文件
          //内容只有一行 指定WebApplicationContext的实现类为XmlWebApplicationContext
    		try {
    			ClassPathResource resource = new ClassPathResource("ContextLoader.properties",                                                                                 ContextLoader.class);
    			defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
    		}
    		catch (IOException ex) {
    			throw new IllegalStateException("Could not load 'ContextLoader.properties': "
                                                + ex.getMessage());
    		}
    	}
      	private static volatile WebApplicationContext currentContext;
      	private WebApplicationContext context;
      	private BeanFactoryReference parentContextRef;
      
    	public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
    
    		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 {
    			 
    			if (this.context == null) {
                  // 这个create方法创建了一个ConfigurableWebApplicationContext实例
    				this.context = createWebApplicationContext(servletContext);
    			}
    			if (this.context instanceof ConfigurableWebApplicationContext) {
    				ConfigurableWebApplicationContext cwac = 
                                 (ConfigurableWebApplicationContext) this.context;
    				if (!cwac.isActive()) {//isActive默认为false,所以会进入if,执行下面的代码
    					if (cwac.getParent() == null) {
    						ApplicationContext parent = loadParentContext(servletContext);
    						cwac.setParent(parent);
    					}
                      //
    					configureAndRefreshWebApplicationContext(cwac, servletContext);
    				}
    			}
    			servletContext.setAttribute("一个变量名", 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.isInfoEnabled()) {
    				long t = System.currentTimeMillis() - startTime;
    				logger.info("Root WebApplicationContext: initialization completed in"+t+" ms");
    			}
    			return this.context;
    		}
    		catch (RuntimeException ex) {
    			logger.error("Context initialization failed", ex);
    			servletContext.setAttribute("一个很长变量名", ex);
    			throw ex;
    		}
    		catch (Error err) {
    			logger.error("Context initialization failed", err);
    			servletContext.setAttribute("一个很长变量名", err);
    			throw err;
    		}
    	}  
    }
    

    在上面的贴出的的代码中,我们可以看到这个ContextLoader类有一个静态代码块,静态代码块会在累加在的时候就执行了,这个代码块执行的内容很简单,就是找到一个名为“ContextLoader.properties”的配置文件,并将这个Properties赋给defaultStrategies变量。ContextLoader.properties的内容如下:

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

    我们继续看initWebApplicationContext方法。这个方法很长,但是实际上它主要做了两件事情:

    1.创建一个ConfigurableWebApplicationContext实例。

    2.根据创建的ConfigurableWebApplicationContext实例,来配置并刷新WebApplicationContext。

    先看第一步,创建ConfigurableWebApplicationContext,方法是createWebApplicationContext()。代码如下:

    	protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
    		Class<?> contextClass = determineContextClass(sc);
    		if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
    			throw new ApplicationContextException("日志描述");
    		}
    		return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
    	}
    

    这个方法先调用determineContextClass来找到一个类,然后return语句中通过BeanUtils反射来创建这个类的实例并返回。

    determineContextClass类中,其实就是利用刚才读到的配置文件“ContextLoader.properties”,从这个文件中得到配置的类名,根据类名返回Class对象。代码简单就不贴了。

    在看第二步,刷新WebApplicationContext。即调用了configureAndRefreshWebApplicationContext(...)方法。这个方法里边做的事情很重要。先看看代码:

    protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
    		if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
                // 这里我删除了一段无关紧要的代码,起逻辑和下面的两句一样。
                String xxx= ...;
    			wac.setId(xxx)
    		}
    
    		wac.setServletContext(sc);
           // 这里得到了我们在web.xml中配置的一个值,即:contextConfigLocation的值,也就是我们的spring文件
    		String initParameter = sc.getInitParameter(CONFIG_LOCATION_PARAM);
    		if (initParameter != null) {
              // 设置spring的配置文件
    			wac.setConfigLocation(initParameter);
    		}
    		customizeContext(sc, wac);//后面分析
    		wac.refresh();//后面分析
    	}
    

    方法中,最后两行的方法调用。先看customizeContext(sc,wac)方法,方法的里边通过serveltContext 的getInitParameter方法得到contextInitializerClasses。如果没配置,就什么也不做,进入之后,可以看到头两行的内容如下:

    List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses =
    				determineContextInitializerClasses(servletContext);
    if (initializerClasses.size() == 0) {
    	// no ApplicationContextInitializers have been declared -> nothing to do
        return;
    }
    

    第一行,通过determineContextInitializerClasser方法来获取配置的contextInitializerClasses变量值,这个值是一个class类名,多个的话用逗号隔开。如果没有配置的话,代码就会执行if size=0这一句,然后return了。

    第二行wac.refresh()方法调用非常重要。这个方法实际上完成了spring 容器的初始化,代码如下:

    	public void refresh() throws BeansException, IllegalStateException {
    		synchronized (this.startupShutdownMonitor) {
    			// Prepare this context for refreshing.
    			prepareRefresh();
    			// Tell the subclass to refresh the internal bean factory.
    			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    			// Prepare the bean factory for use in this context.
    			prepareBeanFactory(beanFactory);
    			try {
    				// Allows post-processing of the bean factory in context subclasses.
    				postProcessBeanFactory(beanFactory);
    				// Invoke factory processors registered as beans in the context.
    				invokeBeanFactoryPostProcessors(beanFactory);
    				// Register bean processors that intercept bean creation.
    				registerBeanPostProcessors(beanFactory);
    				// Initialize message source for this context.
    				initMessageSource();
    				// Initialize event multicaster for this context.
    				initApplicationEventMulticaster();
    				// Initialize other special beans in specific context subclasses.
    				onRefresh();
    				// Check for listener beans and register them.
    				registerListeners();
    				// Instantiate all remaining (non-lazy-init) singletons.
    				finishBeanFactoryInitialization(beanFactory);
    				// Last step: publish corresponding event.
    				finishRefresh();
    			}
    			catch (BeansException ex) {
    				destroyBeans();cancelRefresh(ex);
    				throw ex;
    			}
    		}
    	}
    

    至此spring容器初始化完成了,这个wac.refresh()代码暂时不做深究,本篇主要讨论的是web.xml中的一行配置如何导致了spring的启动过程。本文代码是基于spring3.2.5RELASE。

    SpringMVC是怎么工作的,SpringMVC的工作原理
    spring 异常处理。结合spring源码分析400异常处理流程及解决方法
    Netty系列
    Mybatis Mapper接口是如何找到实现类的-源码分析

  • 相关阅读:
    LeetCode 109 Convert Sorted List to Binary Search Tree
    LeetCode 108 Convert Sorted Array to Binary Search Tree
    LeetCode 107. Binary Tree Level Order Traversal II
    LeetCode 106. Construct Binary Tree from Inorder and Postorder Traversal
    LeetCode 105. Construct Binary Tree from Preorder and Inorder Traversal
    LeetCode 103 Binary Tree Zigzag Level Order Traversal
    LeetCode 102. Binary Tree Level Order Traversal
    LeetCode 104. Maximum Depth of Binary Tree
    接口和多态性
    C# 编码规范
  • 原文地址:https://www.cnblogs.com/demingblog/p/7443714.html
Copyright © 2011-2022 走看看