spring源码解析2--容器的基本实现

spring的主要特性是IOC,实现IOC的关键是bean,而更关键的是如何bean的管理容器,也就是BeanFactory,本文的目标是弄清楚BeanFactory具体是怎么样的存在。

先看下最简单的获取bean的案例,代码如下:

1 public static void main(String[] args){
2         BeanFactory factory = new XmlBeanFactory(new ClassPathResource("spring-beans.xml"));
3         User user = (User) factory.getBean("user");
4         System.out.println(JSON.toJSON(user).toString());
5     }

首先是读取spring的配置文件,创建BeanFactory实例,如何直接从BeanFactory实例中获取指定名称的bean。接下来就从这几行简单的代码入手,分析下BeanFactory。

BeanFactory实例是通过XmlBeanFactory来创建的,很明显XmlBeanFactory是BeanFactory的子类,继承关系图如下:

那么先就从XmlBeanFactory的构造方法开始,源码如下:

 1 public class XmlBeanFactory extends DefaultListableBeanFactory {
 2 
 3     private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
 4 
 5     public XmlBeanFactory(Resource resource) throws BeansException {
 6         this(resource, null);
 7     }
 8 
 9     public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
10         super(parentBeanFactory);
11         this.reader.loadBeanDefinitions(resource);
12     }
13 
14 }

第5行的构造方法调用第9行的构造方法,首先是执行父类的构造方法,然后执行XmlBeanFefinitionReader的loadBeanFefinitions(resource)方法,这个方法显然就是加载bean配置文件的方法。接下来跟踪查看,源码如下:

1     @Override
2     public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
3         return loadBeanDefinitions(new EncodedResource(resource));
4     }

通过EncodeResource来封装Resource,在调用重载的方法

 1 public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
 2         Assert.notNull(encodedResource, "EncodedResource must not be null");
 3         //校验Resource参数
 4         if (logger.isInfoEnabled()) {
 5             logger.info("Loading XML bean definitions from " + encodedResource.getResource());
 6         }
 7 
 8         Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
 9         if (currentResources == null) {
10             currentResources = new HashSet<>(4);
11             this.resourcesCurrentlyBeingLoaded.set(currentResources);
12         }
13         if (!currentResources.add(encodedResource)) {
14             throw new BeanDefinitionStoreException(
15                     "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
16         }
17         try {
18             //获取Resource的InputSteam流
19             InputStream inputStream = encodedResource.getResource().getInputStream();
20             try {
21                 //通过InputSteam构造InputSource
22                 InputSource inputSource = new InputSource(inputStream);
23                 if (encodedResource.getEncoding() != null) {
24                     inputSource.setEncoding(encodedResource.getEncoding());
25                 }
26                 //最终执行doLoadBeanDefinitions方法
27                 return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
28             }
29             finally {
30                 inputStream.close();
31             }
32         }
33         catch (IOException ex) {
34             throw new BeanDefinitionStoreException(
35                     "IOException parsing XML document from " + encodedResource.getResource(), ex);
36         }
37         finally {
38             currentResources.remove(encodedResource);
39             if (currentResources.isEmpty()) {
40                 this.resourcesCurrentlyBeingLoaded.remove();
41             }
42         }
43     }

这次封装的EncodedResource作用主要是对XML配置文件的编码格式进行处理,然后最终执行了doLoadBeanDefinitions方法

 1     protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
 2             throws BeanDefinitionStoreException {
 3         try {
 4             //通过InputSource和Resource得到一个Document对象,然后执行注册bean的方法
 5             Document doc = doLoadDocument(inputSource, resource);
 6             return registerBeanDefinitions(doc, resource);
 7         }
 8         catch (BeanDefinitionStoreException ex) {
 9             throw ex;
10         }
11         catch (SAXParseException ex) {
12             throw new XmlBeanDefinitionStoreException(resource.getDescription(),
13                     "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
14         }
15         catch (SAXException ex) {
16             throw new XmlBeanDefinitionStoreException(resource.getDescription(),
17                     "XML document from " + resource + " is invalid", ex);
18         }
19         catch (ParserConfigurationException ex) {
20             throw new BeanDefinitionStoreException(resource.getDescription(),
21                     "Parser configuration exception parsing XML from " + resource, ex);
22         }
23         catch (IOException ex) {
24             throw new BeanDefinitionStoreException(resource.getDescription(),
25                     "IOException parsing XML document from " + resource, ex);
26         }
27         catch (Throwable ex) {
28             throw new BeanDefinitionStoreException(resource.getDescription(),
29                     "Unexpected exception parsing XML document from " + resource, ex);
30         }
31     }

该方法只有两行有效逻辑代码,首先是加载XML文件得到一个Document对象,然后根据Document对象来注册Bean信息,一步步来看,先看如何生成Document对象。

1 private DocumentLoader documentLoader = new DefaultDocumentLoader();
2 
3 protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
4         return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
5                 getValidationModeForResource(resource), isNamespaceAware());
6     }

调用了DocumentLoader的实例来进行加载,DocumentLoader接口的loadDocument方法

 1 public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
 2                                  ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
 3 
 4         //这里显然是工厂模式+建造者模式,先创建DocumentBuilder工厂,如何得到DocumentBuilder,最终执行parse方法解析xml配置文件得到Document对象
 5         DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
 6         if (logger.isDebugEnabled()) {
 7             logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
 8         }
 9         DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
10         return builder.parse(inputSource);
11     }
 1 public Document parse(InputSource is) throws SAXException, IOException {
 2         if (is == null) {
 3             throw new IllegalArgumentException(
 4                     DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN,
 5                             "jaxp-null-input-source", null));
 6         }
 7         if (fSchemaValidator != null) {
 8             if (fSchemaValidationManager != null) {
 9                 fSchemaValidationManager.reset();
10                 fUnparsedEntityHandler.reset();
11             }
12             resetSchemaValidator();
13         }
14         //上面的代码都是校验,核心是下面的三行,通过DomParser来获取Document对象
15         domParser.parse(is);
16         Document doc = domParser.getDocument();
17         domParser.dropDocumentReferences();
18         return doc;
19     }

解析配置文件得到了Document对象,接下来就是通过Document来注册beanl了。回到XmlBeanDefinitionReader的RegisterBeanDefinitions方法

 1 public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
 2         //创建BeanDefinitionDocumentReader实例
 3         BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
 4         //统计当前已经加载过的BeanDefinition个数
 5         int countBefore = getRegistry().getBeanDefinitionCount();
 6         //加载及注册bean
 7         documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
 8         //得到本次加载的BeanFinition个数
 9         return getRegistry().getBeanDefinitionCount() - countBefore;
10     }

核心是BeanDefinitionDocumentReader接口的registerBeanDefinitions方法,代码如下:

1 public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
2         this.readerContext = readerContext;
3         logger.debug("Loading bean definitions");
4         Element root = doc.getDocumentElement();
5         doRegisterBeanDefinitions(root);
6     }

首先是获取Document的root,然后将root元素传递给下面的方法继续执行,这里的root元素就是配置文件中的<beans>标签

 1 protected void doRegisterBeanDefinitions(Element root) {
 2         //专门处理解析
 3         BeanDefinitionParserDelegate parent = this.delegate;
 4         this.delegate = createDelegate(getReaderContext(), root, parent);
 5 
 6         if (this.delegate.isDefaultNamespace(root)) {
 7             //处理<beans>标签中的profile属性
 8             String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
 9             if (StringUtils.hasText(profileSpec)) {
10                 String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
11                         profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
12                 if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
13                     if (logger.isInfoEnabled()) {
14                         logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
15                                 "] not matching: " + getReaderContext().getResource());
16                     }
17                     return;
18                 }
19             }
20         }
21 
22         preProcessXml(root);//代码为空(模板方法设计模式:提供给子类实现,如果需要在bean的解析前后做一些处理的话)
23         parseBeanDefinitions(root, this.delegate);//Bean的解析
24         postProcessXml(root);//代码为空
25 
26         this.delegate = parent;
27     }

发现bean的注册方法应该是parseBeanDefinitions方法,继续往下

 1  protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
 2         if (delegate.isDefaultNamespace(root)) {
 3             //获取<beans>标签的子节点也就是所有的<bean>标签
 4             NodeList nl = root.getChildNodes();
 5             for (int i = 0; i < nl.getLength(); i++) {
 6                 Node node = nl.item(i);
 7                 if (node instanceof Element) {
 8                     Element ele = (Element) node;
 9                     if (delegate.isDefaultNamespace(ele)) {
10                         //如果是默认命名空间的bean
11                         parseDefaultElement(ele, delegate);
12                     }
13                     else {
14                         //如果是自定义命名空间的bean
15                         delegate.parseCustomElement(ele);
16                     }
17                 }
18             }
19         }
20         else {
21             //自定义命名空间的bean
22             delegate.parseCustomElement(root);
23         }
24     }

这里是逻辑很清楚,获取root下的子节点遍历,判断是否是默认命名空间的元素来分别处理。判断是否是默认命名空间的方式是取Node的namespanceUrl来和默认命名空间的地址进行比较,默认地址为:

public static final String BEANS_NAMESPACE_URI = "http://www.springframework.org/schema/beans";

像<bean id="userService" class="com.test.UserService">这种写法就是默认标签,而如<tx:annotation-driven>这种就是自定义的写法。

总结:本文从BeanFactory的初始化开始,解析XML配置文件,生成Docuemnt对象,解析Document中的标签,按标签的类型来进行区分解析,而两种的解析方式截然不同,接下来就分别解析。

原文地址:https://www.cnblogs.com/jackion5/p/10780568.html