最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring中BeanFactory解析bean詳解

 更新時間:2017年04月05日 11:57:53   作者:weknow619  
本篇文章主要介紹了Spring中BeanFactory解析bean詳解 ,詳細的介紹了使用BeanFactory對bean進行解析的實例,有興趣的可以了解一下。

在該文中來講講Spring框架中BeanFactory解析bean的過程,該文之前在小編原文中有發(fā)表過,先來看一個在Spring中一個基本的bean定義與使用。

package bean;
public class TestBean {
  private String beanName = "beanName";
  public String getBeanName() {
    return beanName;
  }
  public void setBeanName(String beanName) {
    this.beanName = beanName;
  }
}

Spring配置文件root.xml定義如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xsi:schemaLocation="http://www.springframework.org/schema/beans 
   http://www.springframework.org/schema/beans/spring-beans-4.1.xsd">

  <bean id="testBean" class="bean.TestBean">
</beans>

下面使用XmlBeanFactory來獲取該bean:

public class BeanTest {

  private static final java.util.logging.Logger logger = LoggerFactory.getLogger(BeanTest.class);

  @Test
  public void getBeanTest() {
    BeanFactory factory = new XmlBeanFactory(new ClassPathResource("root.xml"));
    TestBean bean = factory.getBean("testBean");
    logger.info(bean.getBeanName);
  }
}

這個單元測試運行結果就是輸出beanName,上面就是Spring最基本的bean的獲取操作,這里我用BeanFactory作為容器來獲取bean的操作并不多見,在企業(yè)開發(fā)中一般是使用功能更完善的ApplicationContext,這里先不討論這個,下面重點講解使用BeanFactory獲取bean的過程。

現(xiàn)在就來分析下上面的測試代碼,看看Spring到底為我們做了什么工作,上面代碼完成功能的流程不外乎如此:

1. 讀取Spring配置文件root.xml;

2. 根據(jù)root.xml中的bean配置找到對應的類的配置,并實例化;

3. 調用實例化后的對象輸出結果。

先來看看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);
  }
}

從上面可以看出XmlBeanFactory繼承了DefaultListableBeanFactory,DefaultListableBeanFactory是Spring注冊加載bean的默認實現(xiàn),它是整個bean加載的核心部分,XmlBeanFactory與它的不同點就是XmlBeanFactory使用了自定義的XML讀取器XmlBeanDefinitionReader,實現(xiàn)了自己的BeanDefinitionReader讀取。

XmlBeanFactory加載bean的關鍵就在于XmlBeanDefinitionReader,下面看看XmlBeanDefinitionReader的源碼(只列出部分):

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {

  private Class<?> documentReaderClass = DefaultBeanDefinitionDocumentReader.class;

  private ProblemReporter problemReporter = new FailFastProblemReporter();

  private ReaderEventListener eventListener = new EmptyReaderEventListener();

  private SourceExtractor sourceExtractor = new NullSourceExtractor();

  private NamespaceHandlerResolver namespaceHandlerResolver;

  private DocumentLoader documentLoader = new DefaultDocumentLoader();

  private EntityResolver entityResolver;

  private ErrorHandler errorHandler = new SimpleSaxErrorHandler(logger);
}

XmlBeanDefinitionReader繼承自AbstractBeanDefinitionReader,下面是AbstractBeanDefinitionReader的源碼(只列出部分):

public abstract class AbstractBeanDefinitionReader implements EnvironmentCapable, BeanDefinitionReader {

  protected final Log logger = LogFactory.getLog(getClass());

  private final BeanDefinitionRegistry registry;

  private ResourceLoader resourceLoader;

  private ClassLoader beanClassLoader;

  private Environment environment;

  private BeanNameGenerator beanNameGenerator = new DefaultBeanNameGenerator();
}

XmlBeanDefinitionReader主要通過以下三步來加載Spring配置文件中的bean:

1. 通過繼承自AbstractBeanDefinitionReader中的方法,使用ResourLoader將資源文件(root.xml)路徑轉換為對應的Resource文件;

2. 通過DocumentLoader對Resource文件進行轉換,將Resource文件轉換為Ducument文件;

3. 通過DefaultBeanDefinitionDocumentReader類對Document進行解析,最后再對解析后的Element進行解析。

了解以上基礎后,接下來詳細分析下一開始例子中的代碼:

BeanFactory factory = new XmlBeanFactory(new ClassPathResource("root.xml"));

先看看下面XmlBeanFactory初始化的時序圖來進一步了解這段代碼的執(zhí)行,

在這里可以看出BeanTest測試類通過向ClassPathResource的構造方法傳入spring的配置文件構造一個Resource資源文件的實例對象,再通過這個Resource資源文件來構造我們想要的XmlBeanFactory實例。在前面XmlBeanFactory源碼中的構造方法可以看出,

public XmlBeanFactory(Resource resource) throws BeansException {
   this(resource, null);
}

public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
   super(parentBeanFactory);
   this.reader.loadBeanDefinitions(resource);
}

this.reader.loadBeanDefinition(resource)就是資源加載真正的實現(xiàn),時序圖中XmlBeanDefinitionReader加載數(shù)據(jù)就是在這里完成的。

接下來跟進this.reader.loadBeanDefinition(resource)方法里面,

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {

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

在loadBeanDefinition(resource)方法里對資源文件resource使用EncodedResource進行編碼處理后繼續(xù)傳入loadBeanDefinitions方法,繼續(xù)跟進loadBeanDefinitions(new EncodedResource(resource))方法源碼:

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
  Assert.notNull(encodedResource, "EncodedResource must not be null");
  if (logger.isInfoEnabled()) {
    logger.info("Loading XML bean definitions from " + encodedResource.getResource());
  }

  // 通過屬性記錄已加載的資源
  Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
  if (currentResources == null) {
    currentResources = new HashSet<EncodedResource>(4);
    this.resourcesCurrentlyBeingLoaded.set(currentResources);
  }
  if (!currentResources.add(encodedResource)) {
    throw new BeanDefinitionStoreException(
        "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
  }
  try {
    // 從resource中獲取對應的InputStream,用于下面構造InputSource
    InputStream inputStream = encodedResource.getResource().getInputStream();
    try {
      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();
    }
  }
}

繼續(xù)跟進doLoadBeanDefinitions(inputSource, encodedResource.getResource())方法,這是整個bean加載過程的核心方法,在這個方法執(zhí)行bean的加載。

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
    throws BeanDefinitionStoreException {
  try {
    Document doc = doLoadDocument(inputSource, resource);
    return registerBeanDefinitions(doc, resource);
  }
  /* 省略一堆catch */
}

跟進doLoadDocument(inputSource, resource)源碼:

protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
  return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
      getValidationModeForResource(resource), isNamespaceAware());
}

在doLoadDocument(inputSource, resource)方法里就使用到了前面講的documentLoader加載Document,這里DocumentLoader是個接口,真正調用的是其實現(xiàn)類DefaultDocumentLoader的loadDocument方法,跟進源碼:

public class DefaultDocumentLoader implements DocumentLoader {

  @Override
  public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
      ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {

    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);
  }
}

從源碼可以看出這里先創(chuàng)建DocumentBuilderFactory,再用它創(chuàng)建DocumentBuilder,進而解析inputSource來返回Document對象。得到Document對象后就可以準備注冊我們的Bean信息了。

在上面的doLoadBeanDefinitions(inputSource, encodedResource.getResource())方法中拿到Document對象后下面就是執(zhí)行registerBeanDefinitions(doc, resource)方法了,看源碼:

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
  BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
  documentReader.setEnvironment(getEnvironment());
  // 還沒注冊bean前的BeanDefinition加載個數(shù)
  int countBefore = getRegistry().getBeanDefinitionCount();
  // 加載注冊bean
  documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
  // 本次加載注冊的BeanDefinition個數(shù)
  return getRegistry().getBeanDefinitionCount() - countBefore;
}

這里的doc就是上面的loadDocument方法加載轉換來的,從上面可以看出主要工作是交給BeanDefinitionDocumentReader的registerBeanDefinitions()方法實現(xiàn)的,這里BeanDefinitionDocumentReader是個接口,注冊bean功能在默認實現(xiàn)類DefaultBeanDefinitionDocumentReader的該方法實現(xiàn),跟進它的源碼:

@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
  this.readerContext = readerContext;
  logger.debug("Loading bean definitions");
  Element root = doc.getDocumentElement();
  doRegisterBeanDefinitions(root);
}

到這里通過doc.getDocumentElement()獲得Element對象后,交給doRegisterBeanDefinitions()方法后就是真正執(zhí)行XML文檔的解析了,跟進doRegisterBeanDefinitions()方法源碼:

protected void doRegisterBeanDefinitions(Element root) {
  BeanDefinitionParserDelegate parent = this.delegate;
  this.delegate = createDelegate(getReaderContext(), root, parent);

  if (this.delegate.isDefaultNamespace(root)) {
    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)) {
        return;
      }
    }
  }

  preProcessXml(root);
  parseBeanDefinitions(root, this.delegate);
  postProcessXml(root);

  this.delegate = parent;
}

到這里處理流程就很清晰了,先是對profile進行處理,之后就通過parseBeanDefinitions()方法進行文檔的解析操作,跟進parseBeanDefinitions()方法源碼:

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
  if (delegate.isDefaultNamespace(root)) {
    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;
        // 下面對bean進行處理
        if (delegate.isDefaultNamespace(ele)) {
          parseDefaultElement(ele, delegate);
        }
        else {
          delegate.parseCustomElement(ele);
        }
      }
    }
  }
  else {
    delegate.parseCustomElement(root);
  }
}

上面if-else語句塊中的parseDefaultElement(ele, delegate)和delegate.parseCustomElement(ele)就是對Spring配置文件中的默認命名空間和自定義命名空間進行解析用的。在Spring的XML配置中,默認Bean聲明就如前面定義的:

<bean id="testBean" class="bean.TestBean">

自定義的Bean聲明如:

<tx:annotation-driven />

XmlBeanFactory加載bean的整個過程基本就講解到這里了。希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關文章

  • Spring如何使用通知?Advice?管理事務

    Spring如何使用通知?Advice?管理事務

    Spring 默認采用聲明式事務管理(通過配置的方式) ,也可以實現(xiàn)編程式事務管理,這篇文章主要介紹了Spring使用通知Advice管理事務,需要的朋友可以參考下
    2023-06-06
  • mybatis和mybatisplus批量插入問題示例詳解

    mybatis和mybatisplus批量插入問題示例詳解

    最近在處理一個功能的時候,需要批量插入數(shù)據(jù),這篇文章主要給大家介紹了關于mybatis和mybatisplus批量插入問題的相關資料,文中通過實例代碼介紹非常詳細,需要的朋友可以參考下
    2023-04-04
  • Spring實戰(zhàn)之使用c:命名空間簡化配置操作示例

    Spring實戰(zhàn)之使用c:命名空間簡化配置操作示例

    這篇文章主要介紹了Spring實戰(zhàn)之使用c:命名空間簡化配置操作,結合實例形式詳細分析了Spring使用c:命名空間簡化配置的相關接口與配置操作技巧,需要的朋友可以參考下
    2019-12-12
  • 詳解Java多線程處理List數(shù)據(jù)

    詳解Java多線程處理List數(shù)據(jù)

    這篇文章主要介紹了Java多線程處理List數(shù)據(jù),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-03-03
  • 在controller中如何設置接收參數(shù)的默認值

    在controller中如何設置接收參數(shù)的默認值

    這篇文章主要介紹了在controller中如何設置接收參數(shù)的默認值,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Java中的構造方法(構造函數(shù))與普通方法區(qū)別及說明

    Java中的構造方法(構造函數(shù))與普通方法區(qū)別及說明

    這篇文章主要介紹了Java中的構造方法(構造函數(shù))與普通方法區(qū)別及說明,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • Spring AOP 與代理的概念與使用

    Spring AOP 與代理的概念與使用

    大家知道我現(xiàn)在還是一個 CRUD 崽,平時用 AOP 也是 CV 大法。最近痛定思痛,決定研究一下 Spring AOP 的原理。 這里寫一篇文章總結一下。主要介紹 Java 中 AOP 的實現(xiàn)原理,最后以兩個簡單的示例來收尾。
    2020-10-10
  • Java之字符串轉換成Java對象方式

    Java之字符串轉換成Java對象方式

    這篇文章主要介紹了Java之字符串轉換成Java對象方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • 詳解 Java HashMap 實現(xiàn)原理

    詳解 Java HashMap 實現(xiàn)原理

    這篇文章主要介紹了詳解 Java HashMap 實現(xiàn)原理的相關資料,幫助大家更好的理解和學習使用Java,感興趣的朋友可以了解下
    2021-03-03
  • SpringBoot文件訪問映射如何實現(xiàn)

    SpringBoot文件訪問映射如何實現(xiàn)

    這篇文章主要介紹了SpringBoot文件訪問映射如何實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-02-02

最新評論

临漳县| 密山市| 濮阳县| 友谊县| 金寨县| 缙云县| 峨边| 鸡西市| 深州市| 定远县| 砚山县| 恭城| 兴安盟| 襄城县| 平塘县| 庆城县| 大丰市| 磐安县| 庆阳市| 伊川县| 宁远县| 阜康市| 万年县| 吉木乃县| 都江堰市| 繁峙县| 洛扎县| 大安市| 阳原县| 伊金霍洛旗| 怀仁县| 临沧市| 天门市| 玉山县| 包头市| 湘潭县| 邹城市| 罗源县| 康乐县| 房产| 通榆县|