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

Spring的初始化和XML解析的實(shí)現(xiàn)

 更新時(shí)間:2019年03月13日 11:36:21   作者:清幽之地  
這篇文章主要介紹了Spring的初始化和XML解析的實(shí)現(xiàn),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧

前言

Spring是什么?它是一個(gè)應(yīng)用程序框架,為應(yīng)用程序的開發(fā)提供強(qiáng)大的支持,例如對事務(wù)處理和持久化的支持等;它也是一個(gè)bean容器,管理bean對象的整個(gè)生命周期,維護(hù)bean的各種存在狀態(tài),例如bean對象的實(shí)例化、銷毀、bean的單實(shí)例和多實(shí)例狀態(tài)等。

Spring作為Java發(fā)展史上不可忽視的存在,說他重新定義了Java也不為過。它功能強(qiáng)大,著實(shí)為日常開發(fā)提供了大大的便利。表面越簡單的東西,背后越復(fù)雜。
從本章節(jié)開始,我們一起分析Spring的源碼,看它到底是怎么樣來實(shí)現(xiàn)我們常說常用的諸如IOC、Annotation、AOP、事務(wù)等功能的。

1、Spring的入口

在我們的項(xiàng)目中,web.xml必不可少,其中就定義了Spring的監(jiān)聽器。

<listener>
   <listener-class>
      org.springframework.web.context.ContextLoaderListener
   </listener-class>
</listener>

我們來看ContextLoaderListener類,可以看到它實(shí)現(xiàn)了ServletContextListener接口,
contextInitialized就是Spring初始化的入口方法。

Spring還有一個(gè)入口,叫做org.springframework.web.servlet.DispatcherServlet,它們之間是父子容器的關(guān)系,最終都會(huì)調(diào)用到同一個(gè)方法org.springframework.context.support.AbstractApplicationContext.refresh()。

2、初始化

Spring的初始化第一步就是要加載配置文件,然后解析里面的配置項(xiàng)。

ok,我們來到XmlWebApplicationContext類的loadBeanDefinitions方法。

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
  String[] configLocations = getConfigLocations();
  if (configLocations != null) {
    for (String configLocation : configLocations) {
      reader.loadBeanDefinitions(configLocation);
    }
  }
}

可以看到,configLocations是一個(gè)數(shù)組,它獲取的就是配置文件。在筆者的項(xiàng)目中,只有一個(gè)配置文件,名字是applicationContext.xml。下一步就是通過loadBeanDefinitions這個(gè)方法解析這個(gè)配置文件。

3、解析XML配置

首先把一個(gè)配置文件封裝成一個(gè)Resource對象,然后獲取Resource對象的輸入流,轉(zhuǎn)換成InputSource對象,最后解析成Document對象。下面代碼只保留了主要部分。

public int loadBeanDefinitions(String location, Set<Resource> actualResources) 
             throws BeanDefinitionStoreException {
    ResourceLoader resourceLoader = getResourceLoader();
    if (resourceLoader instanceof ResourcePatternResolver) {
      // Resource pattern matching available.
      try {
        //這里的location就是配置文件-applicationContext.xml,轉(zhuǎn)成Resource對象
        Resource[] resources=resourceLoader).getResources(location);
        //獲取resources對象的輸入流 再轉(zhuǎn)成JDK的InputSource對象,最后解析成Document
        InputStream inputStream = resources.getInputStream();
        InputSource inputSource = new InputSource(inputStream);
        Document doc = doLoadDocument(inputSource, resource);
      }
      catch (IOException ex) {
        throw new BeanDefinitionStoreException(
            "Could not resolve bean definition resource pattern [" + location + "]", ex);
      }
    }
  }

applicationContext.xml配置文件解析成Document對象,它的Root節(jié)點(diǎn)信息如下:

[  
  [#text:], 
  [context:component-scan: null], 
  [#text:], 
  [bean: null], 
  [#text:], 
  [bean: null], 
  [#text:], 
  [bean: null],
  [#text:], 
  [bean: null], 
  [#text:], 
  [#comment: 指定了表現(xiàn)層資源的前綴和后綴
    viewClass:JstlView表示JSP模板頁面需要使用JSTL標(biāo)簽庫
    prefix 和suffix:查找視圖頁面的前綴和后綴,比如傳進(jìn)來的邏輯視圖名為hello,則該該
            jsp視圖頁面應(yīng)該存放在“WEB-INF/jsp/hello.jsp”], 
  [#text:], 
  [bean: null], 
  [#text: ]
]

4、加載Bean信息

上一步我們看到Spring已經(jīng)把a(bǔ)pplicationContext.xml這個(gè)配置文件解析成了Document對象,接下來就是關(guān)鍵的一步。先看源碼

  //這里拿到的是Document對象的根節(jié)點(diǎn),根節(jié)點(diǎn)信息參考上圖
  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;
          //這里有兩個(gè)分支。
          //一個(gè)是處理默認(rèn)的節(jié)點(diǎn)(import、alias、bean、beans)
          //一個(gè)是處理自定義的節(jié)點(diǎn)(context:component-scan)
          if (delegate.isDefaultNamespace(ele)) {
            parseDefaultElement(ele, delegate);
          }
          else {
            delegate.parseCustomElement(ele);
          }
        }
      }
    }
    else {
      delegate.parseCustomElement(root);
    }
  }

4.1 component-scan的解析

首先定位到自定義解析方法delegate.parseCustomElement(ele);

最終調(diào)用了org.springframework.context.annotation.ComponentScanBeanDefinitionParser.parse(Element element, ParserContext parserContext),不過它是怎么調(diào)用到這個(gè)類的呢?說起來就比較有意思了。

我們先來看Spring里面的一個(gè)配置文件,/META-INF/spring.handlers

http\://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler
http\://www.springframework.org/schema/jee=org.springframework.ejb.config.JeeNamespaceHandler
http\://www.springframework.org/schema/lang=org.springframework.scripting.config.LangNamespaceHandler
http\://www.springframework.org/schema/task=org.springframework.scheduling.config.TaskNamespaceHandler
http\://www.springframework.org/schema/cache=org.springframework.cache.config.CacheNamespaceHandler

這里面配置了不同標(biāo)簽的處理類,比如context標(biāo)簽處理類就是ContextNamespaceHandler,然后通過反射實(shí)例化這個(gè)處理類,調(diào)用它的init()方法。init()方法里面它又注冊了一堆處理類,其中就有我們很感興趣的component-scan。

  public NamespaceHandler resolve(String namespaceUri) {
    //handlerMappings里有個(gè)方法loadAllProperties(),獲取Spring所有的配置項(xiàng)
    Map<String, Object> handlerMappings = getHandlerMappings();
    Object handlerOrClassName = handlerMappings.get(namespaceUri);
    if (handlerOrClassName == null) {
      return null;
    }
    else if (handlerOrClassName instanceof NamespaceHandler) {
      return (NamespaceHandler) handlerOrClassName;
    }
    else {
      String className = (String) handlerOrClassName;
      try {
        //以context:component-scan舉例
        //這里拿到的className就是org.springframework.context.config.ContextNamespaceHandler
        //通過反射,實(shí)例化這個(gè)ContextNamespaceHandler,然后調(diào)用init方法
        Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
        NamespaceHandler namespaceHandler = BeanUtils.instantiateClass(handlerClass);
        namespaceHandler.init();
        handlerMappings.put(namespaceUri, namespaceHandler);
        return namespaceHandler;
      }
    }
  }
  public void init() {
    registerBeanDefinitionParser("annotation-config",
       new AnnotationConfigBeanDefinitionParser());
    registerBeanDefinitionParser("component-scan",
       new ComponentScanBeanDefinitionParser());
    //...未完
  }

最終Spring就可以通過component-scan這個(gè)標(biāo)簽,拿到ComponentScanBeanDefinitionParser類,調(diào)用它的parse()方法。

 public BeanDefinition parse(Element element, ParserContext parserContext) {
    //獲取包掃描路徑,對應(yīng)配置文件中的base-package="com.viewscenes.netsupervisor"
    String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
    basePackage = parserContext.getReaderContext().getEnvironment().
       resolvePlaceholders(basePackage);
    //這里可能有多個(gè)包路徑,分割成數(shù)組
    String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
        ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);

    /**
     * configureScanner 配置掃描器。
     * scanner.doScan 掃描執(zhí)行
     * registerComponents 這里重點(diǎn)是對registerComponents的支持
     * 
     * @return
     */
    ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
    Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
    registerComponents(parserContext.getReaderContext(), beanDefinitions, element);

    return null;
  }

4.1.1 configureScanner 配置掃描器

這里面重點(diǎn)就是注冊了默認(rèn)的過濾器。use-default-filters,默認(rèn)值是true,如果配置文件配置了此屬性的值為false,有些注解就加不進(jìn)來,到下一步掃描的時(shí)候就注冊不了Bean。

protected void registerDefaultFilters() {
  //這個(gè)就是配置的use-default-filters,如果配置了false。那么下面的
  // Component、ManagedBean、Named注解都不會(huì)被掃描到
  if (useDefaultFilters) { 
    this.includeFilters.add(new AnnotationTypeFilter(Component.class));
    ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
    try {
      this.includeFilters.add(new AnnotationTypeFilter(
      ((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), 
                                     false));
      logger.debug("JSR-250 'javax.annotation.ManagedBean' 
                       found and supported for component scanning");
    }
    catch (ClassNotFoundException ex) {
      // JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
    }   
    //...未完
  }
}

4.1.2 doScan掃描

doScan分為三個(gè)步驟。

  • findCandidateComponents 掃描包路徑下的所有class文件,過濾有Component注解的類,轉(zhuǎn)換成BeanDefinition對象,加入一個(gè)LinkedHashSet中。
  • 循環(huán)上一步返回的LinkedHashSet,設(shè)置基本屬性,比如setLazyInit、setScope。
  • 注冊BeanDefinition對象,向Map容器中緩存beanName和BeanDefinition,向List中加入beanName。
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
    for (String basePackage : basePackages) {
      //findCandidateComponents方法掃描class文件,判斷Component注解,轉(zhuǎn)成BeanDefinition對象返回。
      //值得注意的是,Component不止是@Component,還有
      //@Controller、@Service、@Repository,因?yàn)樵谶@三個(gè)注解上面還有個(gè)@Component。
     //這就相當(dāng)于它們都是Component的子注解。
      Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
      for (BeanDefinition candidate : candidates) {
        ScopeMetadata scopeMetadata = this.scopeMetadataResolver.
                              resolveScopeMetadata(candidate);
        //設(shè)置屬性,沒有配置的都是默認(rèn)值
        candidate.setScope(scopeMetadata.getScopeName());
        candidate.setxxx(scopeMetadata.getxxxName());
        String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
        //registerBeanDefinition方法 注冊BeanDefinition,等同于下面兩句
        //this.beanDefinitionMap.put(beanName, beanDefinition);
        //this.beanDefinitionNames.add(beanName);
        registerBeanDefinition(definitionHolder, this.registry);
      }
    }
    return beanDefinitions;
  }

最后整個(gè)方法返回的就是beanDefinition對象的Set集合,以兩個(gè)Controller為例。

[  
  Generic bean: class [com.viewscenes.netsupervisor.controller.IndexController]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [D:\apache-tomcat-7.0.78\webapps\springmvc_dubbo_producer\WEB-INF\classes\com\viewscenes\netsupervisor\controller\IndexController.class], 
  
  Generic bean: class [com.viewscenes.netsupervisor.controller.UserController]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [D:\apache-tomcat-7.0.78\webapps\springmvc_dubbo_producer\WEB-INF\classes\com\viewscenes\netsupervisor\controller\UserController.class]
]

4.1.3 對annotation-config的支持

我們知道,在Spring配置文件有個(gè)配置是context:annotation-config 但如果配置了context:component-scan 就不必再配置config,這是因?yàn)樵诮馕鯿omponent-scan的時(shí)候已經(jīng)默認(rèn)添加了annotation-config的支持,除非你手動(dòng)設(shè)置了annotation-config="false",不過這可不太妙,因?yàn)樵贗OC的時(shí)候就沒辦法支持@Autowired等注解了。

protected void registerComponents(XmlReaderContext readerContext, 
           Set<BeanDefinitionHolder> beanDefinitions, Element element) {
  boolean annotationConfig = true;
  if (element.hasAttribute(ANNOTATION_CONFIG_ATTRIBUTE)) {
    annotationConfig = Boolean.valueOf(element.getAttribute(ANNOTATION_CONFIG_ATTRIBUTE));
  }
  if (annotationConfig) { //判斷annotation-config屬性的值
    Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<BeanDefinitionHolder>(4);
    if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
      RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
      beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
    }
    if (!registry.containsBeanDefinition(REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
      RootBeanDefinition def = new RootBeanDefinition(RequiredAnnotationBeanPostProcessor.class);
      beanDefs.add(registerPostProcessor(registry, def, REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
    }
    if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
      RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
      beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
    }
      ......未完
  }
}

4.2 bean標(biāo)簽的解析

bean標(biāo)簽的解析,就是默認(rèn)的處理方法。

獲取bean標(biāo)簽的id,并且把beanName賦值為id,設(shè)置別名。新建AbstractBeanDefinition對象,通過反射設(shè)置beanClass,解析property屬性名稱和值。

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
    //獲取bean_id 
    String id = ele.getAttribute(ID_ATTRIBUTE);
    String beanName = id;
    AbstractBeanDefinition beanDefinition = 
               parseBeanDefinitionElement(ele, beanName, containingBean);
    String[] aliasesArray = StringUtils.toStringArray(aliases);
    //最后返回已經(jīng)包含了beanName、class對象和一系列方法的BeanDefinition對象
    return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, 
                  String beanName, BeanDefinition containingBean) {
    String className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
    try {
      //根據(jù)className反射設(shè)置setBeanClass和setBeanClassName
      AbstractBeanDefinition bd = createBeanDefinition(className, parent);
      //設(shè)置默認(rèn)方法 setScope、setLazyInit、setAutowireMode...
      parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
      //設(shè)置property屬性 <bean><property name="id" value="1001"></property></bean>
      parsePropertyElements(ele, bd);
      return bd;
    }
    return null;
}

注冊BeanDefinition對象,和component-scan掃描的bean注冊一樣。向容器中填充對象。

不管是XML配置的Bean,還是通過component-scan掃描注冊的Bean它們最后都是殊途同歸的,會(huì)轉(zhuǎn)換成一個(gè)BeanDefinition對象。記錄著這個(gè)Bean對象的屬性和方法,最后都注冊到容器中,等待在實(shí)例化和IOC的時(shí)候遍歷它們。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Mybatis使用XML實(shí)現(xiàn)動(dòng)態(tài)sql的示例代碼

    Mybatis使用XML實(shí)現(xiàn)動(dòng)態(tài)sql的示例代碼

    當(dāng)編寫 MyBatis 中復(fù)雜動(dòng)態(tài) SQL 語句時(shí),使用 XML 格式是一種非常靈活的方式,本文主要為大家詳細(xì)介紹了Mybatis使用XML實(shí)現(xiàn)動(dòng)態(tài)sql的具體方法,需要的可以參考下
    2023-12-12
  • SpringBoot配置log4j2的實(shí)現(xiàn)示例

    SpringBoot配置log4j2的實(shí)現(xiàn)示例

    SpringBoot中默認(rèn)使用Logback作為日志框架,本文主要介紹了SpringBoot配置log4j2的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-12-12
  • Java 回調(diào)函數(shù)深入理解

    Java 回調(diào)函數(shù)深入理解

    這篇文章主要介紹了 Java 回調(diào)函數(shù)深入理解的相關(guān)資料,需要的朋友可以參考下
    2017-03-03
  • 關(guān)于SpringBoot整合RabbitMQ實(shí)現(xiàn)死信隊(duì)列

    關(guān)于SpringBoot整合RabbitMQ實(shí)現(xiàn)死信隊(duì)列

    這篇文章主要介紹了關(guān)于SpringBoot整合RabbitMQ實(shí)現(xiàn)死信隊(duì)列,死信隊(duì)列實(shí)際上就是一個(gè)普通的隊(duì)列,只是這個(gè)隊(duì)列跟死信交換機(jī)進(jìn)行了綁定,用來存放死信而已,需要的朋友可以參考下
    2023-05-05
  • Java中的集合框架

    Java中的集合框架

    本文主要介紹了Java中集合框架的相關(guān)知識,具有很好的參考價(jià)值,下面跟著小編一起來看下吧
    2017-03-03
  • MyBatis入門實(shí)例教程之創(chuàng)建一個(gè)簡單的程序

    MyBatis入門實(shí)例教程之創(chuàng)建一個(gè)簡單的程序

    這篇文章主要介紹了MyBatis入門創(chuàng)建一個(gè)簡單的程序,在?MySQL?中創(chuàng)建數(shù)據(jù)庫?mybatisdemo,編碼為?utf8,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-02-02
  • 詳解DES加密算法的原理與Java實(shí)現(xiàn)

    詳解DES加密算法的原理與Java實(shí)現(xiàn)

    DES 加密,是對稱加密。對稱加密,顧名思義,加密和解密的運(yùn)算全都是使用的同樣的秘鑰。這篇文章主要為大家講講DES加密算法的原理與Java實(shí)現(xiàn),需要的可以參考一下
    2022-10-10
  • Java 異常的知識整理

    Java 異常的知識整理

    這篇文章主要介紹了Java 異常的知識整理的相關(guān)資料,需要的朋友可以參考下
    2017-07-07
  • Java之String、StringBuffer、StringBuilder的區(qū)別分析

    Java之String、StringBuffer、StringBuilder的區(qū)別分析

    今天搞安卓在看書的時(shí)候遇到了StringBuilder這個(gè)類型的東東,有點(diǎn)小迷,不知道它跟string、stringbuffer的關(guān)系式怎么樣的,趕快查閱相關(guān)資料,了解了個(gè)大概,拿出來分享一下
    2012-11-11
  • Java String.format()的用法

    Java String.format()的用法

    本篇文章主要介紹了JAVA的 String.format()的使用,具有一定的參考價(jià)值,有需要的可以了解一下,希望能夠給你帶來幫助
    2021-11-11

最新評論

保亭| 丹凤县| 安义县| 焦作市| 咸宁市| 太康县| 洛阳市| 博乐市| 民乐县| 盐池县| 台南县| 昌乐县| 金川县| 马山县| 敖汉旗| 波密县| 铜陵市| 沈阳市| 筠连县| 云梦县| 阿克苏市| 沧源| 浦县| 凌源市| 沾益县| 嘉禾县| 门源| 云南省| 利辛县| 达拉特旗| 嘉峪关市| 济宁市| 包头市| 新密市| 凌云县| 南投市| 卢湾区| 渭源县| 青田县| 孝义市| 福安市|