Spring源码学习笔记(4)

Spring源码学习笔记(四)  

  前言--

    最近花了些时间看了《Spring源码深度解析》这本书,算是入门了Spring的源码吧。打算写下系列文章,回忆一下书的内容,总结代码的运行流程。推荐那些和我一样没接触过SSH框架源码又想学习的,阅读郝佳编著的《Spring源码深度解析》这本书,会是个很好的入门。

 


    开头写句话, 不会太尴尬 --   前三篇在源码的角度梳理了 SpringMVC 的基本基本流程, 在接下来的内容回归 Spring 框架本身。 回顾 Spring 框架的功能的实现。 接下来进入正文。  o(* ̄︶ ̄*)o


Spring 容器

       加载 Bean 的类:

        通过 IntelliJ 的 show Diagram 功能获取到  DefaultListableBeanFactoryXmlBeanFactory 结构图。

    

    ------  DefaultListableBeanFactory 是 bean 加载的核心, 上图中的类都有其各自对应的功能和作用, 在接下来的探索中, 我们将会逐渐看到他们各自的身影。

    读取 XML 配置文件的类:

    无论是 Spring ,还是 Hibernate, Struts 框架, 其实现都是通过读取 XML 配置文件的信息来进行各种任务的执行。  在 Spring 框架中, 从 XmlBeanDefinitionReader  中瞄下读取资源文件的功能。首先, 还是看下 XmlBeanDefinitionReader 的结构图。

    

 


 

     上面介绍了 Spring 的功能实现的两个重要的类, 接下来从代码的角度分析 Spring 从资源文件读取到加载 Bean 的逻辑。


 

资源文件的读取

     在使用 Spring 容器加载 Bean 之前, 我们通常会通过指定 XML 配置文件告诉 Spring 容器在哪加载我们定义的 Bean, 即:

 1   BeanFactory bf = new XmlBeanFactory(new ClassPathResource("***.xml"));                                                                                                              

     可以看到, Spirng 通过 ClassPathResource 类封装了资源配置文件。  查看 ClassPathResource 的结构

    

    在其父接口 Resource 中, 定义了许多有关读取资源文件的方法:

 1 public interface Resource extends InputStreamSource {
 2     boolean exists();
 3 
 4     boolean isReadable();
 5 
 6     boolean isOpen();
 7 
 8     URL getURL() throws IOException;
 9 
10     URI getURI() throws IOException;
11 
12     File getFile() throws IOException;
13 
14     long contentLength() throws IOException;
15 
16     long lastModified() throws IOException;
17 
18     Resource createRelative(String var1) throws IOException;
19 
20     String getFilename();
21 
22     String getDescription();
23 }

 

     需要我们关注的是在 Resource 接口的父接口 InputStreamSource 接口中的一个方法:

1 public interface InputStreamSource {  
2     //第一步:  定义了流读取资源文件的方法
3     InputStream getInputStream() throws IOException;  
4 }          

 

    了解了资源文件封装的类后, 再来看看 XmlBeanFactory 的初始化:

 1 public class XmlBeanFactory extends DefaultListableBeanFactory {
 2     private final XmlBeanDefinitionReader reader;
 3 
 4     public XmlBeanFactory(Resource resource) throws BeansException {
 5         this(resource, (BeanFactory)null);
 6     }
 7    // 第一步:  传入 Resource 
 8     public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
 9         super(parentBeanFactory);    //第二步:  调用父类的构造函数
10         this.reader = new XmlBeanDefinitionReader(this);
11         //第三步;  加载配置文件的方法
12         this.reader.loadBeanDefinitions(resource);
13     }
14 }

 

      在 XmlBeanDefinition 的构造函数中,第二步调用父类构造函数的初始化方法:

1     public AbstractAutowireCapableBeanFactory() {
2         //第一步:  忽略实现指定接口的类
3         this.ignoreDependencyInterface(BeanNameAware.class);
4         this.ignoreDependencyInterface(BeanFactoryAware.class);
5         this.ignoreDependencyInterface(BeanClassLoaderAware.class);
6     }

 

    在 XmlBeanDefinition 的构造函数中, 第三步加载配置文件的 loadBeanDefinitions() 真正实现了资源的加载:

1     public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
2         //第一步:  XmlBeanDefinitionReader 中的 loadBeanDefinitions方法 , 封装了 EncodedResource
3         return this.loadBeanDefinitions(new EncodedResource(resource));
4     }

 

    在 loadBeanDefinitions() 方法中, 封装的 EncodedResource 处理了编码问题:

1     public Reader getReader() throws IOException {
2         return this.charset != null?new InputStreamReader(this.resource.getInputStream(), this.charset):(this.encoding != null?new InputStreamReader(this.resource.getInputStream(), this.encoding):new InputStreamReader(this.resource.getInputStream()));
3     }

 

    然后进入 loadBeanDefinitions() 真正的实现逻辑:

 1     public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
 2         //第一步:  记录正在加载的资源
 3         Set<EncodedResource> currentResources = (Set)this.resourcesCurrentlyBeingLoaded.get();
 4         if(currentResources == null) {
 5             currentResources = new HashSet(4);
 6             this.resourcesCurrentlyBeingLoaded.set(currentResources);
 7         }
 8 
 9         if(!((Set)currentResources).add(encodedResource)) {
10             throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
11         } else {
12             int var5;
13             try {
14                 //第二步: 从 EncodedResource 获取 Resource 再 获取 InputStream
15                 InputStream inputStream = encodedResource.getResource().getInputStream();
16 
17                 try {
18                     InputSource inputSource = new InputSource(inputStream);
19                     if(encodedResource.getEncoding() != null) {
20                         inputSource.setEncoding(encodedResource.getEncoding());
21                     }
22                     //第三步:  进入核心逻辑
23                     var5 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());
24                 } finally {
25                     //第四步:  关闭流资源
26                     inputStream.close();
27                 }
28             } catch (IOException var15) {
29                 throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), var15);
30             } finally {
31                 ((Set)currentResources).remove(encodedResource);
32                 if(((Set)currentResources).isEmpty()) {
33                     this.resourcesCurrentlyBeingLoaded.remove();
34                 }
35 
36             }
37 
38             return var5;
39         }
40     }

 

     在 loadBeanDefinitions() 方法 的第三步中, doLoadBeanDefinitions()  实现了加载 BeanDefinition 的逻辑:

1     protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
2         try {
3             //第一步:  加载文档, 验证文档的工作
4             Document doc = this.doLoadDocument(inputSource, resource);
5             //第二步:  注册加载的 BeanDefinition
6             return this.registerBeanDefinitions(doc, resource);
7         } 
8     }

 

    在 doLoadBeanDefinitions() 方法中, 第一步 doLoadDocument() 方法的实现:

1     protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
2         //第一步:  对 XML 文件的验证, 获取文档并返回
3         return this.documentLoader.loadDocument(inputSource, this.getEntityResolver(), this.errorHandler, this.getValidationModeForResource(resource), this.isNamespaceAware());
4     }

 

    在 doLoadDocument() 方法中, 第一步中  DocumentLoader 对象处理加载 Document 的逻辑:

1     public Document loadDocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
2         //第一步:  创建 DocumentBuilderFactory 
3         DocumentBuilderFactory factory = this.createDocumentBuilderFactory(validationMode, namespaceAware);
4         //第二步:  根据 DocumentBuilderFactory  创建 DocumentBuilder , 进而解析 Document
5         DocumentBuilder builder = this.createDocumentBuilder(factory, entityResolver, errorHandler);
6         return builder.parse(inputSource);
7     }

 

    在  doLoadBeanDefinitions() 方法中,  第二步  registerBeanDefinitions() 实现注册 BeanDefinition 的逻辑:(在 XmlBeanDefinitionReader 中)

 1     public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
 2         //第一步:  创建 Reader 对象
 3         BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader();
 4         documentReader.setEnvironment(this.getEnvironment());
 5         //第二步:  记录加载的个数
 6         int countBefore = this.getRegistry().getBeanDefinitionCount();
 7         //第三步:  加载以及注册 BeanDefinition
 8         documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource));
 9         return this.getRegistry().getBeanDefinitionCount() - countBefore;
10     }

 

     在第一步 createBeanDefinitionDocumentReader() 方法中, 实例化 Reader 对象:

1     protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
2         //第一步:  创建 Reader 对象
3         return (BeanDefinitionDocumentReader)BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));
4     }

 

    发现 this.documentReaderClass 属性:

  1   private Class<?> documentReaderClass = DefaultBeanDefinitionDocumentReader.class;                                                                                               

    因此, 在 registerBeanDefinitions() 方法中, 第二步 registerBeanDefinitions() 方法的实现逻辑是在 DefaultBeanDefinitionDocumentReader 类中:

1     public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
2         this.readerContext = readerContext;
3         this.logger.debug("Loading bean definitions");
4         Element root = doc.getDocumentElement();
5         //第一步: 真正的处理逻辑
6         this.doRegisterBeanDefinitions(root);
7     }

 

 1     protected void doRegisterBeanDefinitions(Element root) {
 2         //第一步:  处理 profile 属性 (是 “dev” 或者 “production”)
 3         String profileSpec = root.getAttribute("profile");
 4         if(StringUtils.hasText(profileSpec)) {
 5             Assert.state(this.environment != null, "Environment must be set for evaluating profiles");
 6             String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, ",; ");
 7             if(!this.environment.acceptsProfiles(specifiedProfiles)) {
 8                 return;
 9             }
10         }
11 
12         BeanDefinitionParserDelegate parent = this.delegate;
13         this.delegate = this.createDelegate(this.readerContext, root, parent);
14         //第二步:  解析前处理,子类实现
15         this.preProcessXml(root);
16         //第三步:  真正解析逻辑
17         this.parseBeanDefinitions(root, this.delegate);
18         //第四步: 解析后处理, 子类实现
19         this.postProcessXml(root);
20         this.delegate = parent;
21     }

 

    在 doRegisterBeanDefinitions() 方法中, 第三步 parseBeanDefinition() 方法处理了 bean :

 1     protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
 2         //第一步:  默认标签的处理
 3         if(delegate.isDefaultNamespace(root)) {
 4             NodeList nl = root.getChildNodes();
 5 
 6             for(int i = 0; i < nl.getLength(); ++i) {
 7                 Node node = nl.item(i);
 8                 if(node instanceof Element) {
 9                     Element ele = (Element)node;
10                     if(delegate.isDefaultNamespace(ele)) {
11                         //第二步:  处理默认标签
12                         this.parseDefaultElement(ele, delegate);
13                     } else {
14                         //第三步:  处理自定义标签
15                 delegate.parseCustomElement(ele);
16                     }
17                 }
18             }
19         } else {
20             //第四步:  处理自定义标签
21             delegate.parseCustomElement(root);
22         }
23 
24     }

 

     到此 Spring 容器读取资源文件, 并开始 解析默认的便签, 也就是 parseDefaultElement()  以及  parseCustomElement() 方法, 将在下一篇继续深入。  

    PERFECT EVERYTHING IN YOUR LIFE!!!  KEEP ON GRINDING!!     o(* ̄︶ ̄*)o

 

posted @ 2017-04-07 14:52  VimKeyLin  阅读(280)  评论(0编辑  收藏  举报