一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|JavaScript|易語言|

服務器之家 - 編程語言 - Java教程 - Spring的初始化和XML解析的實現

Spring的初始化和XML解析的實現

2021-07-22 15:23清幽之地 Java教程

這篇文章主要介紹了Spring的初始化和XML解析的實現,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

前言

spring是什么?它是一個應用程序框架,為應用程序的開發提供強大的支持,例如對事務處理和持久化的支持等;它也是一個bean容器,管理bean對象的整個生命周期,維護bean的各種存在狀態,例如bean對象的實例化、銷毀、bean的單實例和多實例狀態等。

spring作為java發展史上不可忽視的存在,說他重新定義了java也不為過。它功能強大,著實為日常開發提供了大大的便利。表面越簡單的東西,背后越復雜。
從本章節開始,我們一起分析spring的源碼,看它到底是怎么樣來實現我們常說常用的諸如ioc、annotation、aop、事務等功能的。

1、spring的入口

在我們的項目中,web.xml必不可少,其中就定義了spring的監聽器。

?
1
2
3
4
5
<listener>
   <listener-class>
      org.springframework.web.context.contextloaderlistener
   </listener-class>
</listener>

我們來看contextloaderlistener類,可以看到它實現了servletcontextlistener接口,
contextinitialized就是spring初始化的入口方法。

spring還有一個入口,叫做org.springframework.web.servlet.dispatcherservlet,它們之間是父子容器的關系,最終都會調用到同一個方法org.springframework.context.support.abstractapplicationcontext.refresh()。

2、初始化

spring的初始化第一步就是要加載配置文件,然后解析里面的配置項。

ok,我們來到xmlwebapplicationcontext類的loadbeandefinitions方法。

?
1
2
3
4
5
6
7
8
protected void loadbeandefinitions(xmlbeandefinitionreader reader) throws ioexception {
  string[] configlocations = getconfiglocations();
  if (configlocations != null) {
    for (string configlocation : configlocations) {
      reader.loadbeandefinitions(configlocation);
    }
  }
}

可以看到,configlocations是一個數組,它獲取的就是配置文件。在筆者的項目中,只有一個配置文件,名字是applicationcontext.xml。下一步就是通過loadbeandefinitions這個方法解析這個配置文件。

3、解析xml配置

首先把一個配置文件封裝成一個resource對象,然后獲取resource對象的輸入流,轉換成inputsource對象,最后解析成document對象。下面代碼只保留了主要部分。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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,轉成resource對象
        resource[] resources=resourceloader).getresources(location);
        //獲取resources對象的輸入流 再轉成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節點信息如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  [#text:],
  [context:component-scan: null],
  [#text:],
  [bean: null],
  [#text:],
  [bean: null],
  [#text:],
  [bean: null],
  [#text:],
  [bean: null],
  [#text:],
  [#comment: 指定了表現層資源的前綴和后綴
    viewclass:jstlview表示jsp模板頁面需要使用jstl標簽庫
    prefix 和suffix:查找視圖頁面的前綴和后綴,比如傳進來的邏輯視圖名為hello,則該該
            jsp視圖頁面應該存放在“web-inf/jsp/hello.jsp”],
  [#text:],
  [bean: null],
  [#text: ]
]

4、加載bean信息

上一步我們看到spring已經把applicationcontext.xml這個配置文件解析成了document對象,接下來就是關鍵的一步。先看源碼

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//這里拿到的是document對象的根節點,根節點信息參考上圖
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;
        //這里有兩個分支。
        //一個是處理默認的節點(import、alias、bean、beans)
        //一個是處理自定義的節點(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);

最終調用了org.springframework.context.annotation.componentscanbeandefinitionparser.parse(element element, parsercontext parsercontext),不過它是怎么調用到這個類的呢?說起來就比較有意思了。

我們先來看spring里面的一個配置文件,/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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public namespacehandler resolve(string namespaceuri) {
  //handlermappings里有個方法loadallproperties(),獲取spring所有的配置項
  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
      //通過反射,實例化這個contextnamespacehandler,然后調用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這個標簽,拿到componentscanbeandefinitionparser類,調用它的parse()方法。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public beandefinition parse(element element, parsercontext parsercontext) {
   //獲取包掃描路徑,對應配置文件中的base-package="com.viewscenes.netsupervisor"
   string basepackage = element.getattribute(base_package_attribute);
   basepackage = parsercontext.getreadercontext().getenvironment().
      resolveplaceholders(basepackage);
   //這里可能有多個包路徑,分割成數組
   string[] basepackages = stringutils.tokenizetostringarray(basepackage,
       configurableapplicationcontext.config_location_delimiters);
 
   /**
    * configurescanner 配置掃描器。
    * scanner.doscan 掃描執行
    * registercomponents 這里重點是對registercomponents的支持
    *
    * @return
    */
   classpathbeandefinitionscanner scanner = configurescanner(parsercontext, element);
   set<beandefinitionholder> beandefinitions = scanner.doscan(basepackages);
   registercomponents(parsercontext.getreadercontext(), beandefinitions, element);
 
   return null;
 }

4.1.1 configurescanner 配置掃描器

這里面重點就是注冊了默認的過濾器。use-default-filters,默認值是true,如果配置文件配置了此屬性的值為false,有些注解就加不進來,到下一步掃描的時候就注冊不了bean。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
protected void registerdefaultfilters() {
  //這個就是配置的use-default-filters,如果配置了false。那么下面的
  // component、managedbean、named注解都不會被掃描到
  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分為三個步驟。

  • findcandidatecomponents 掃描包路徑下的所有class文件,過濾有component注解的類,轉換成beandefinition對象,加入一個linkedhashset中。
  • 循環上一步返回的linkedhashset,設置基本屬性,比如setlazyinit、setscope。
  • 注冊beandefinition對象,向map容器中緩存beanname和beandefinition,向list中加入beanname。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
protected set<beandefinitionholder> doscan(string... basepackages) {
    set<beandefinitionholder> beandefinitions = new linkedhashset<beandefinitionholder>();
    for (string basepackage : basepackages) {
      //findcandidatecomponents方法掃描class文件,判斷component注解,轉成beandefinition對象返回。
      //值得注意的是,component不止是@component,還有
      //@controller、@service、@repository,因為在這三個注解上面還有個@component。
     //這就相當于它們都是component的子注解。
      set<beandefinition> candidates = findcandidatecomponents(basepackage);
      for (beandefinition candidate : candidates) {
        scopemetadata scopemetadata = this.scopemetadataresolver.
                              resolvescopemetadata(candidate);
        //設置屬性,沒有配置的都是默認值
        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;
  }

最后整個方法返回的就是beandefinition對象的set集合,以兩個controller為例。

?
1
2
3
4
5
  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配置文件有個配置是context:annotation-config 但如果配置了context:component-scan 就不必再配置config,這是因為在解析component-scan的時候已經默認添加了annotation-config的支持,除非你手動設置了annotation-config="false",不過這可不太妙,因為在ioc的時候就沒辦法支持@autowired等注解了。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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標簽的解析

bean標簽的解析,就是默認的處理方法。

獲取bean標簽的id,并且把beanname賦值為id,設置別名。新建abstractbeandefinition對象,通過反射設置beanclass,解析property屬性名稱和值。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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);
    //最后返回已經包含了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 {
      //根據classname反射設置setbeanclass和setbeanclassname
      abstractbeandefinition bd = createbeandefinition(classname, parent);
      //設置默認方法 setscope、setlazyinit、setautowiremode...
      parsebeandefinitionattributes(ele, beanname, containingbean, bd);
      //設置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它們最后都是殊途同歸的,會轉換成一個beandefinition對象。記錄著這個bean對象的屬性和方法,最后都注冊到容器中,等待在實例化和ioc的時候遍歷它們。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

原文鏈接:https://www.jianshu.com/p/baa1d48e7f57

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 99精品在线免费 | 99精彩免费观看 | 乌克兰粉嫩摘花第一次 | 福利片免费一区二区三区 | 亚洲人成毛片线播放 | 色综合久久98天天综合 | 亚洲九九九 | 免费视频精品一区二区三区 | 久久精品美女 | 91视频一区 | 大陆国语自产精品视频在 | 亚洲欧美专区精品久久 | 欧美视| 精品久久久久久久久久香蕉 | 欧美日韩精品亚洲精品v18 | 青丝视频免费版在线看 | ckinese中国男同gay男男 | 日本中文字幕一区二区高清在线 | 91日本在线观看亚洲精品 | 久久精品午夜一区二区福利 | www.尤物 | 国产乱码在线精品可播放 | 99精品免费在线 | 久久国产36精品色熟妇 | 日韩在线视频在线 | 亚洲狠狠婷婷综合久久久久网站 | 亚洲欧美专区精品久久 | 成人资源影音先锋久久资源网 | 涩色爱 | 被调教的校花 | 加勒比福利 | 色先锋影音先锋 | 特黄一级大片 | 91在线视频国产 | 天堂资源在线8 | 色老板视频在线 | 太深了 太粗h1v1 | aigao视频| 午夜一个人在线观看完整版 | 变态np虐高h | 久操久操久操 |