gpt4 book ai didi

Spring的初始化和XML解析的实现

转载 作者:qq735679552 更新时间:2022-09-28 22:32:09 31 4
gpt4 key购买 nike

CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.

这篇CFSDN的博客文章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 。

最后此篇关于Spring的初始化和XML解析的实现的文章就讲到这里了,如果你想了解更多关于Spring的初始化和XML解析的实现的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com