Spring IOC 源码阅读之资源定位加载

来源:互联网 发布:杰西平克曼 知乎 编辑:程序博客网 时间:2024/05/20 06:37

关于spring容器的启动的主要的入口是AbstractApplicationContext的refresh()方法,这个方法非常重要;

[html] view plain copy
  1. public void refresh() throws BeansException, IllegalStateException {  
  2.     synchronized (this.startupShutdownMonitor) {  
  3.         // Prepare this context for refreshing.  
  4.         prepareRefresh();  
  5.   
  6.         // Tell the subclass to refresh the internal bean factory.  
  7.         ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();  
  8.   
  9.         // Prepare the bean factory for use in this context.  
  10.         prepareBeanFactory(beanFactory);  
  11.   
  12.         try {  
  13.             // Allows post-processing of the bean factory in context subclasses.  
  14.             postProcessBeanFactory(beanFactory);  
  15.   
  16.             // Invoke factory processors registered as beans in the context.  
  17.             invokeBeanFactoryPostProcessors(beanFactory);  
  18.   
  19.             // Register bean processors that intercept bean creation.  
  20.             registerBeanPostProcessors(beanFactory);  
  21.   
  22.             // Initialize message source for this context.  
  23.             initMessageSource();  
  24.   
  25.             // Initialize event multicaster for this context.  
  26.             initApplicationEventMulticaster();  
  27.   
  28.             // Initialize other special beans in specific context subclasses.  
  29.             onRefresh();  
  30.   
  31.             // Check for listener beans and register them.  
  32.             registerListeners();  
  33.   
  34.             // Instantiate all remaining (non-lazy-init) singletons.  
  35.             finishBeanFactoryInitialization(beanFactory);  
  36.   
  37.             // Last step: publish corresponding event.  
  38.             finishRefresh();  
  39.         }  
  40.   
  41.         catch (BeansException ex) {  
  42.             // Destroy already created singletons to avoid dangling resources.  
  43.             destroyBeans();  
  44.   
  45.             // Reset 'active' flag.  
  46.             cancelRefresh(ex);  
  47.   
  48.             // Propagate exception to caller.  
  49.             throw ex;  
  50.         }  
  51.     }  
  52. }  
      此方法中用obtainFreshBeanFactory方法间接的调用了子类(如AbstractRefreshableApplicationContext)实现的refreshBeanFctory()方法。
[java] view plain copy
  1. /** 
  2.      * This implementation performs an actual refresh of this context's underlying 
  3.      * bean factory, shutting down the previous bean factory (if any) and 
  4.      * initializing a fresh bean factory for the next phase of the context's lifecycle. 
  5.      */  
  6.     @Override  
  7.     protected final void refreshBeanFactory() throws BeansException {  
  8.         if (hasBeanFactory()) {  
  9.             destroyBeans();  
  10.             closeBeanFactory();  
  11.         }  
  12.         try {  
  13.             DefaultListableBeanFactory beanFactory = createBeanFactory();  
  14.             beanFactory.setSerializationId(getId());  
  15.             customizeBeanFactory(beanFactory);  
  16.             loadBeanDefinitions(beanFactory);  
  17.             synchronized (this.beanFactoryMonitor) {  
  18.                 this.beanFactory = beanFactory;  
  19.             }  
  20.         }  
  21.         catch (IOException ex) {  
  22.             throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);  
  23.         }  
  24.     }  

     refreshBeanFctory()首先实例化一个BeanFactory(DefaultListableBeanFactory),然后调用抽象方法loadBeanDifinition(DefaultListableBeanFactory beanFactory),此方法由其子类实现,比如AbstractXmlApplicationContext、XmlWebApplicationContext等。
[java] view plain copy
  1. protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {  
  2.     // Create a new XmlBeanDefinitionReader for the given BeanFactory.  
  3.     XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);  
  4.   
  5.     // Configure the bean definition reader with this context's  
  6.     // resource loading environment.  
  7.     beanDefinitionReader.setEnvironment(this.getEnvironment());  
  8.     beanDefinitionReader.setResourceLoader(this);  
  9.     beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));  
  10.   
  11.     // Allow a subclass to provide custom initialization of the reader,  
  12.     // then proceed with actually loading the bean definitions.  
  13.     initBeanDefinitionReader(beanDefinitionReader);  
  14.     loadBeanDefinitions(beanDefinitionReader);  
  15. }  

在这个方法中,做了以下事情:
1、首先创建了一个BeanDefinitionReader的实例(XmlBeanDefinitionReader), 
[java] view plain copy
  1. XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);  
2、设置ResorceLoader为ApplicationContext实例本身(实际上是继承了DefaultResourceLoader,所以其实这里的ResourceLoader是DefaultResourceLoader),
[java] view plain copy
  1. beanDefinitionReader.setResourceLoader(this);  
  3、调用loadBeanDefinitions(XmlBeanDefinitionReader reader)方法。
 
[java] view plain copy
  1.    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {  
  2. Resource[] configResources = getConfigResources();  
  3. if (configResources != null) {  
  4.     reader.loadBeanDefinitions(configResources);  
  5. }  
  6. String[] configLocations = getConfigLocations();  
  7. if (configLocations != null) {  
  8.     reader.loadBeanDefinitions(configLocations);  
  9. }  

这个方法调用父类AbstractRefreshableConfigApplication的getConfigLocations方法,获取资源定义的路径,如果不是通过构造器传入的路径,则调用getDefaultConfigLocations的子类实现来获取资源定义路径(XmlWebApplicationContext)。
然后调用BeanDefinitionReader的loadBeanDefinitions(String location)方法加载资源。
[java] view plain copy
  1. public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {  
  2.     ResourceLoader resourceLoader = getResourceLoader();  
  3.     if (resourceLoader == null) {  
  4.         throw new BeanDefinitionStoreException(  
  5.                 "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");  
  6.     }  
  7.   
  8.     if (resourceLoader instanceof ResourcePatternResolver) {  
  9.         // Resource pattern matching available.  
  10.         try {  
  11.             Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);  
  12.             int loadCount = loadBeanDefinitions(resources);  
  13.             if (actualResources != null) {  
  14.                 for (Resource resource : resources) {  
  15.                     actualResources.add(resource);  
  16.                 }  
  17.             }  
  18.             if (logger.isDebugEnabled()) {  
  19.                 logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");  
  20.             }  
  21.             return loadCount;  
  22.         }  
  23.         catch (IOException ex) {  
  24.             throw new BeanDefinitionStoreException(  
  25.                     "Could not resolve bean definition resource pattern [" + location + "]", ex);  
  26.         }  
  27.     }  
  28.     else {  
  29.         // Can only load single resources by absolute URL.只能加载一个绝对路径的URL资源  
  30.         Resource resource = resourceLoader.getResource(location);  
  31.         int loadCount = loadBeanDefinitions(resource);  
  32.         if (actualResources != null) {  
  33.             actualResources.add(resource);  
  34.         }  
  35.         if (logger.isDebugEnabled()) {  
  36.             logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");  
  37.         }  
  38.         return loadCount;  
  39.     }  
  40. }  

在BeanDefinitionReader的loadBeanDefinitions的方法中,首先获取前面setter的ResourceLoader对象,调用ResourseLoader对象的
getResources(location)方法(这个方法会根据location判断是否是classpathResource或者URL,如果都不是调用getResourceByPath(由子类FileSystemXmlApplicationContext或AbstractRefreshableWebApplicationContext等实现返回FileSystemResource或者ServletContextResource))获取具体的资源Resource。
[java] view plain copy
  1. public Resource getResource(String location) {  
  2.     Assert.notNull(location, "Location must not be null");  
  3.     if (location.startsWith(CLASSPATH_URL_PREFIX)) {  
  4.         return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());  
  5.     }  
  6.     else {  
  7.         try {  
  8.             // Try to parse the location as a URL...  
  9.             URL url = new URL(location);  
  10.             return new UrlResource(url);  
  11.         }  
  12.         catch (MalformedURLException ex) {  
  13.             // No URL -> resolve as resource path.  
  14.             return getResourceByPath(location);  
  15.         }  
  16.     }  
  17. }  
FileSystemXmlApplication:
[java] view plain copy
  1.        protected Resource getResourceByPath(String path) {  
  2. if (path != null && path.startsWith("/")) {  
  3.     path = path.substring(1);  
  4. }  
  5. return new FileSystemResource(path);  
AbstractRefreshableWebApplicationContext:
[java] view plain copy
  1. protected Resource getResourceByPath(String path) {  
  2.         return new ServletContextResource(this.servletContext, path);  
  3.     } 
0 0
原创粉丝点击