spring 的主要特性是 IoC, 实现 IoC 的关键是 bean, 而更关键的是如何 bean 的管理容器, 也就是 BeanFactory, 本文的目标是弄清楚 BeanFactory 具体是怎么样的存在.
先看下最简单的获取 bean 的案例, 代码如下:
- public static void main(String[] args){
- BeanFactory factory = new XmlBeanFactory(new ClassPathResource("spring-beans.xml"));
- User user = (User) factory.getBean("user");
- System.out.println(JSON.toJSON(user).toString());
- }
首先是读取 spring 的配置文件, 创建 BeanFactory 实例, 如何直接从 BeanFactory 实例中获取指定名称的 bean. 接下来就从这几行简单的代码入手, 分析下 BeanFactory.
BeanFactory 实例是通过 XmlBeanFactory 来创建的, 很明显 XmlBeanFactory 是 BeanFactory 的子类, 继承关系图如下:
那么先就从 XmlBeanFactory 的构造方法开始, 源码如下:
- public class XmlBeanFactory extends DefaultListableBeanFactory {
- private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
- public XmlBeanFactory(Resource resource) throws BeansException {
- this(resource, null);
- }
- public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
- super(parentBeanFactory);
- this.reader.loadBeanDefinitions(resource);
- }
- }
第 5 行的构造方法调用第 9 行的构造方法, 首先是执行父类的构造方法, 然后执行 XmlBeanFefinitionReader 的 loadBeanFefinitions(resource) 方法, 这个方法显然就是加载 bean 配置文件的方法. 接下来跟踪查看, 源码如下:
- @Override
- public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
- return loadBeanDefinitions(new EncodedResource(resource));
- }
通过 EncodeResource 来封装 Resource, 在调用重载的方法
- public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
- Assert.notNull(encodedResource, "EncodedResource must not be null");
- // 校验 Resource 参数
- if (logger.isInfoEnabled()) {
- logger.info("Loading XML bean definitions from" + encodedResource.getResource());
- }
- Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
- if (currentResources == null) {
- currentResources = new HashSet<>(4);
- this.resourcesCurrentlyBeingLoaded.set(currentResources);
- }
- if (!currentResources.add(encodedResource)) {
- throw new BeanDefinitionStoreException(
- "Detected cyclic loading of" + encodedResource + "- check your import definitions!");
- }
- try {
- // 获取 Resource 的 InputSteam 流
- InputStream inputStream = encodedResource.getResource().getInputStream();
- try {
- // 通过 InputSteam 构造 InputSource
- InputSource inputSource = new InputSource(inputStream);
- if (encodedResource.getEncoding() != null) {
- inputSource.setEncoding(encodedResource.getEncoding());
- }
- // 最终执行 doLoadBeanDefinitions 方法
- return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
- }
- finally {
- inputStream.close();
- }
- }
- catch (IOException ex) {
- throw new BeanDefinitionStoreException(
- "IOException parsing XML document from" + encodedResource.getResource(), ex);
- }
- finally {
- currentResources.remove(encodedResource);
- if (currentResources.isEmpty()) {
- this.resourcesCurrentlyBeingLoaded.remove();
- }
- }
- }
这次封装的 EncodedResource 作用主要是对 xml 配置文件的编码格式进行处理, 然后最终执行了 doLoadBeanDefinitions 方法
- protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
- throws BeanDefinitionStoreException {
- try {
- // 通过 InputSource 和 Resource 得到一个 Document 对象, 然后执行注册 bean 的方法
- Document doc = doLoadDocument(inputSource, resource);
- return registerBeanDefinitions(doc, resource);
- }
- catch (BeanDefinitionStoreException ex) {
- throw ex;
- }
- catch (SAXParseException ex) {
- throw new XmlBeanDefinitionStoreException(resource.getDescription(),
- "Line" + ex.getLineNumber() + "in XML document from" + resource + "is invalid", ex);
- }
- catch (SAXException ex) {
- throw new XmlBeanDefinitionStoreException(resource.getDescription(),
- "XML document from" + resource + "is invalid", ex);
- }
- catch (ParserConfigurationException ex) {
- throw new BeanDefinitionStoreException(resource.getDescription(),
- "Parser configuration exception parsing XML from" + resource, ex);
- }
- catch (IOException ex) {
- throw new BeanDefinitionStoreException(resource.getDescription(),
- "IOException parsing XML document from" + resource, ex);
- }
- catch (Throwable ex) {
- throw new BeanDefinitionStoreException(resource.getDescription(),
- "Unexpected exception parsing XML document from" + resource, ex);
- }
- }
该方法只有两行有效逻辑代码, 首先是加载 xml 文件得到一个 Document 对象, 然后根据 Document 对象来注册 Bean 信息, 一步步来看, 先看如何生成 Document 对象.
- private DocumentLoader documentLoader = new DefaultDocumentLoader();
- protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
- return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
- getValidationModeForResource(resource), isNamespaceAware());
- }
调用了 DocumentLoader 的实例来进行加载, DocumentLoader 接口的 loadDocument 方法
- public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
- ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
- // 这里显然是工厂模式 + 建造者模式, 先创建 DocumentBuilder 工厂, 如何得到 DocumentBuilder, 最终执行 parse 方法解析 xml 配置文件得到 Document 对象
- DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
- if (logger.isDebugEnabled()) {
- logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
- }
- DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
- return builder.parse(inputSource);
- }
- public Document parse(InputSource is) throws SAXException, IOException {
- if (is == null) {
- throw new IllegalArgumentException(
- DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN,
- "jaxp-null-input-source", null));
- }
- if (fSchemaValidator != null) {
- if (fSchemaValidationManager != null) {
- fSchemaValidationManager.reset();
- fUnparsedEntityHandler.reset();
- }
- resetSchemaValidator();
- }
- // 上面的代码都是校验, 核心是下面的三行, 通过 DomParser 来获取 Document 对象
- domParser.parse(is);
- Document doc = domParser.getDocument();
- domParser.dropDocumentReferences();
- return doc;
- }
解析配置文件得到了 Document 对象, 接下来就是通过 Document 来注册 beanl 了. 回到 XmlBeanDefinitionReader 的 RegisterBeanDefinitions 方法
- public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
- // 创建 BeanDefinitionDocumentReader 实例
- BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
- // 统计当前已经加载过的 BeanDefinition 个数
- int countBefore = getRegistry().getBeanDefinitionCount();
- // 加载及注册 bean
- documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
- // 得到本次加载的 BeanFinition 个数
- return getRegistry().getBeanDefinitionCount() - countBefore;
- }
核心是 BeanDefinitionDocumentReader 接口的 registerBeanDefinitions 方法, 代码如下:
- public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
- this.readerContext = readerContext;
- logger.debug("Loading bean definitions");
- Element root = doc.getDocumentElement();
- doRegisterBeanDefinitions(root);
- }
首先是获取 Document 的 root, 然后将 root 元素传递给下面的方法继续执行, 这里的 root 元素就是配置文件中的 < beans > 标签
- protected void doRegisterBeanDefinitions(Element root) {
- // 专门处理解析
- BeanDefinitionParserDelegate parent = this.delegate;
- this.delegate = createDelegate(getReaderContext(), root, parent);
- if (this.delegate.isDefaultNamespace(root)) {
- // 处理 < beans > 标签中的 profile 属性
- String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
- if (StringUtils.hasText(profileSpec)) {
- String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
- profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
- if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
- if (logger.isInfoEnabled()) {
- logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
- "] not matching:" + getReaderContext().getResource());
- }
- return;
- }
- }
- }
- preProcessXml(root);// 代码为空 (模板方法设计模式: 提供给子类实现, 如果需要在 bean 的解析前后做一些处理的话)
- parseBeanDefinitions(root, this.delegate);//Bean 的解析
- postProcessXml(root);// 代码为空
- this.delegate = parent;
- }
发现 bean 的注册方法应该是 parseBeanDefinitions 方法, 继续往下
- protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
- if (delegate.isDefaultNamespace(root)) {
- // 获取 < beans > 标签的子节点也就是所有的 < bean > 标签
- NodeList nl = root.getChildNodes();
- for (int i = 0; i <nl.getLength(); i++) {
- Node node = nl.item(i);
- if (node instanceof Element) {
- Element ele = (Element) node;
- if (delegate.isDefaultNamespace(ele)) {
- // 如果是默认命名空间的 bean
- parseDefaultElement(ele, delegate);
- }
- else {
- // 如果是自定义命名空间的 bean
- delegate.parseCustomElement(ele);
- }
- }
- }
- }
- else {
- // 自定义命名空间的 bean
- delegate.parseCustomElement(root);
- }
- }
这里是逻辑很清楚, 获取 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 中的标签, 按标签的类型来进行区分解析, 而两种的解析方式截然不同, 接下来就分别解析.
来源: http://www.bubuko.com/infodetail-3038429.html