我的日常

登录/注册
您现在的位置:论坛 盖世程序员(我猜到了开头 却没有猜到结局) 盖世程序员 > Spring源码学习(2):XmlBeanFactory导读(转)
总共48087条微博

动态微博

查看: 1731|回复: 0

Spring源码学习(2):XmlBeanFactory导读(转)

[复制链接]

13

主题

2

听众

115

金钱

三袋弟子

该用户从未签到

跳转到指定楼层
楼主
发表于 2014-07-07 15:07:22 |只看该作者 |倒序浏览
源代码分析,是一件既痛苦又快乐的事情,看别人写的代码是通过的,但当你能够看明白的时候,相信快乐也会随之而来,为了减少痛苦,更快的带来快乐,在这里希望通过这篇文章对觉得困难的朋友有一个帮助。
8 k; j2 W8 T& d$ {1 g# n" s% w! O/ H% c% J
   本文以spring框架的XmlBeanFactory为入手点进行分析,希望能够以尽量简洁明了的方式给予有需要的朋友一定的帮助。% X* n" f7 h( o- z3 ?$ o. g! o

/ _/ u3 O/ F% Z: l4 Y  x   首先来打开该类的代码,我们将看到如下代码:
: F3 b0 R; n0 ^+ F, b4 O以下内容为程序代码:
* H" |; o& ~0 g( d' a( V- ^, W. V) S! t. J; d) {, b7 }
public class XmlBeanFactory extends DefaultListableBeanFactory {9 f" G9 ~4 u0 a/ s$ ?+ n/ q: N

8 m6 C. v: S5 g   private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
* M/ i) M- C" M+ h( M
7 n) @' W! o" O# `$ Q  K: Q   public XmlBeanFactory(Resource resource) throws BeansException {( C* @6 T7 K  m2 s* t7 i& s6 v( B% C
       this(resource, null);6 B6 L# g3 ?1 l
   }  p% N& s7 _, J( |! j

& @* m& n# g0 e8 \   public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
. p! Z, o3 b" U0 E$ d       super(parentBeanFactory);9 b7 D0 o* w/ s/ x5 Y* o3 [
       this.reader.loadBeanDefinitions(resource);
8 f' S  ~  f4 T. ?   }  X9 }. |6 H6 @
0 d0 `4 G; F/ J! G1 m: B
}9 o% Y- t* b3 V/ A
6 w) ]; e) v$ O1 j( W
9 N1 g" V% ?/ d* l
   这个类的代码很简单,一个成员对象加两个构造函数,从这里我们可以看出,最重要的地方在于最后一个构造函数:% M: _7 g! A9 M* _1 S  w; T( p

: y7 Y3 Z. U& a+ x3 ?* ?3 o以下内容为程序代码:, j" L, s. U. {) H

+ F' i! y7 {* ]* e& z   super(parentBeanFactory);
: n  v4 w, s2 c3 w  H+ q# B   this.reader.loadBeanDefinitions(resource);0 f/ G! L8 n7 O; b

, p1 g/ L# U* u. K' Y8 d, R( F; _
5 b& o' M" _. n7 O4 O   第一句就是将父亲工厂交给父类的构造函数,实际上最后也就是把父工厂保存到类的parentBeanFactory成员对象中,这个对象是在AbstractBeanFactory抽象类中定义的,而这个父工厂也会一直传递到该抽象类进行保存。第二句就是整个类中最重要的地方了,顾名思义,它的目的是通过XmlBeanDefinitionReader这个XML的Reader从资源resource中(也就是你的配置文件)读取bean的定义。接下来我们打开XmlBeanDefinitionReader的loadBeanDefinitions方法,我们可看到在这个方法里代码就一行,调用了一个同名不同参的方法,而参数是EncodedResource的一个实例,这个类实际上是Resource的一个包装类,用来保存资源的Encode的,那接下来我们再看被调用的loadBeanDefinitions方法,这个方法里最主要的部分就是:; R% z- [2 W0 E4 H" @& c; p

4 T  y2 e7 G8 k  z# n* r" `, H以下内容为程序代码:8 v: U( ^* p- X

+ Z3 y. I  p- H5 s" D( a. e0 Q8 l       InputSource inputSource = new InputSource(inputStream);
( k. P9 c; ?% P' X7 V! O- C               if (encodedResource.getEncoding() != null) {8 Y) x3 ~! ^. n/ M5 z
                   inputSource.setEncoding(encodedResource.getEncoding());
* M/ `1 P, W! L! F; Q2 Z% r               }3 i! Y, Q" d: \0 s- @0 U$ d( g
       return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
3 o( x& T# R" i, b+ n& ~. g, o% z) l* T1 E- G# V1 M% r
2 ]0 x+ r8 e4 `5 D0 P0 C3 L0 L
   这里的目的是将资源包装成一个InputSource,连同Resource作为参数传递到doLoadBeanDefinitions方法+ o0 j3 U/ t1 h8 {2 h

! x& |2 _' R0 @* B' a  W) t
  1. ( {! L2 j" d  C
  2.            DocumentBuilderFactory factory = createDocumentBuilderFactory();
    ! p6 L+ c' @- c
  3.            if (logger.isDebugEnabled()) {
    2 x0 v+ h2 O+ ]; \. g4 K
  4.                logger.debug("Using JAXP implementation [" + factory + "]");
    ' x5 }' [+ g/ t) |+ N& h
  5.            }, a! P6 n+ A4 J5 n. k
  6.            DocumentBuilder builder = createDocumentBuilder(factory);( ^' B" E" [! e6 u  j7 t
  7.            Document doc = builder.parse(inputSource);
    + r7 t# D2 |( ^; m
  8.            return registerBeanDefinitions(doc, resource);
    , l. J7 L) u0 _; j/ m; w
复制代码
" T0 g, h9 b! N& B9 h

4 d$ R# ~9 s$ I9 N! x   这个方法的目的一目了然,就是为了将资源解释成为Document对象,然后调用registerBeanDefinitions方法,这里不做详细解释,不了解的话请去看看关于JAXP的介绍。接下来我们打开registerBeanDefinitions方法:4 P& o% m- m, E' K# ^7 F" [* w
以下内容为程序代码:
) r3 f. T) Q5 }# t& d! H( X
. K* e# N/ l+ d" Q: H   public int registerBeanDefinitions(Document doc, Resource resource) throws BeansException {
) D$ i8 H; E: u/ g       XmlBeanDefinitionParser parser =! Q' d- g- R& S% i& x
               (XmlBeanDefinitionParser) BeanUtils.instantiateClass(this.parserClass);  }: ?4 c; Y  v- _
       return parser.registerBeanDefinitions(this, doc, resource);# S$ K6 y* p( c. f
   }
; d! Z+ b1 J0 }5 S# U$ v
: D) g& X0 z3 a* P9 j! b' H8 Q) D1 e/ j. g
   这里创建了一个XmlBeanDefinitionParser接口的实现,这个接口的具体类是DefaultXmlBeanDefinitionParser,这个接口很简单,只有registerBeanDefinitions一个方法,这个方法的作用也很明了,就是用来注册Bean的定义的,所以说类和方法的名字一定要起得有意义,这样可以让人一看就大概了解其作用,减少了很多阅读代码的痛苦。废话不多说,我们打开DefaultXmlBeanDefinitionParser的registerBeanDefinitions方法,这个类就是解释XML配置文件的核心类了,打开registerBeanDefinitions方法后我们看到如下代码:
2 n# k7 Z4 H# i" {2 c$ q以下内容为程序代码:" F; {4 N- H) i) A; X

( p5 @9 i7 |, Y$ p& M$ K   public int registerBeanDefinitions(BeanDefinitionReader reader, Document doc, Resource resource)
. @" R0 e/ j" K5 s' v           throws BeanDefinitionStoreException {0 W9 F6 e. h1 @! U

' U2 Z" w$ t; U6 X% r1 p       this.beanDefinitionReader = reader;. D# J- p8 X& e0 j) X
       this.resource = resource;' {( w4 F& `' i
% \; E( O9 z! q4 r
       logger.debug("Loading bean definitions");2 f4 h  N8 \+ B5 @
       Element root = doc.getDocumentElement();' X5 _( F7 b2 n& e0 j- k( ~
       //初始化根元素( Y' B# v$ g0 W0 S, u! H
       initDefaults(root);5 F7 y- M  O3 j6 k% q1 c& q. o7 Z
       if (logger.isDebugEnabled()) {
9 I2 a8 m  w: b* d           logger.debug("Default lazy init '" + getDefaultLazyInit() + "'");
: A1 _' M+ ?! N           logger.debug("Default autowire '" + getDefaultAutowire() + "'");
3 e: @! h; ^' [4 i           logger.debug("Default dependency check '" + getDefaultDependencyCheck() + "'");3 n% U& `- H. }
       }/ B, O% P7 Y. z6 g0 C8 q7 V
3 l3 ?; U( q" u& j1 }, L
       preProcessXml(root);//一个空方法用于扩展8 `) u) a* B! U4 \$ L! _9 c$ K1 M: M
       int beanDefinitionCount = parseBeanDefinitions(root);//解释配置的主要方法' G8 Q- F, t6 N* o* m+ M
       if (logger.isDebugEnabled()) {3 a* t3 @6 J5 J. _# n
           logger.debug("Found " + beanDefinitionCount + " <bean> elements in " + resource);
) `, H( ^, A% z* y       }
/ z6 ]7 [/ K5 h       postProcessXml(root); //一个空方法用于扩展+ u; Q+ |, \! {4 P9 z" @

  v$ Q9 y  \/ h7 [$ M$ _       return beanDefinitionCount;
7 t' t5 {8 @/ ^# d. n6 W5 K$ p   }
% ~, m; H5 l2 ^+ e- j& x* b* M; m+ q; w" g+ {
1 ]' d8 {4 O0 A4 h! a/ q' @+ c
   在这个方法当中,主要用于解释定义的有两个方法,一个是initDefaults,一个是parseBeanDefinitions,第一个方法是用来解释根元素的属性的,例如lazy-init, autowire等,而parseBeanDefinitions就是用来解释具体的bean定义了,方法代码如下:
: x2 _; k2 F' Z, r" V5 B* ]以下内容为程序代码:
/ w% t5 F6 e( ]1 n8 h; M& T/ y7 n% I0 D/ ?$ T; F2 R, Q; q
   protected int parseBeanDefinitions(Element root) throws BeanDefinitionStoreException {& a# m6 C; ^& a) O
       NodeList nl = root.getChildNodes();
: f) n. u8 Y; p6 R! O5 h- J       int beanDefinitionCount = 0;& [' j# \, g0 p8 v2 [
       for (int i = 0; i < nl.getLength(); i++) {
0 f& T$ E- D( `  e# J) T           Node node = nl.item(i);3 k  ?0 v- u  @2 f+ l
           if (node instanceof Element) {
5 T6 `, S9 ?- R               Element ele = (Element) node;1 P$ {8 n  p3 q6 K, Q8 F& m) r
               if (IMPORT_ELEMENT.equals(node.getNodeName())) {6 _8 a! l6 n- [6 e1 a; l, A1 _8 ^# U
                   importBeanDefinitionResource(ele);
2 t. M2 I, j1 h               }
% e. D4 g. U) s               else if (ALIAS_ELEMENT.equals(node.getNodeName())) {; a& Z' B- p; e- w; R
                   String name = ele.getAttribute(NAME_ATTRIBUTE);
2 f) N* d0 f  K5 e9 k/ v! ]                   String alias = ele.getAttribute(ALIAS_ATTRIBUTE);
8 g6 D% I, n! G                   this.beanDefinitionReader.getBeanFactory().registerAlias(name, alias);6 E8 o* `3 `6 G
               }
/ M" j0 p' f, N  C. y& L1 K) }               else if (BEAN_ELEMENT.equals(node.getNodeName())) {" M, E" q0 z- |& H8 `1 x
                   beanDefinitionCount++;
) W5 ^# `4 g! J$ _0 ^* A8 R8 ^* f                   BeanDefinitionHolder bdHolder = parseBeanDefinitionElement(ele, false);
" A% _! m3 D4 g1 _# k% n                   BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.beanDefinitionReader.getBeanFactory());
* `( d4 M+ x0 T% b% e" f4 R               }8 V6 l& a: t( ~
           }
- `1 G6 f7 ]+ G       }4 A7 `$ T+ T! h
       return beanDefinitionCount;9 @) j' u! r6 a5 Z. i
   }
$ P/ I+ N( ?4 K/ A. X& I' o$ n4 O5 m( N! H' j& `
. r/ s+ F$ }2 M/ n3 _# k  a, d
   其他标签具体如何被解释这里就不多说,相信大家也能看得懂,这里主要讲一下解释bean的的处理,我们注意以下代码:% c: k2 h1 S$ U: M4 ]; p
以下内容为程序代码:
' A7 l7 v; e- {5 s; n( x8 P* |7 S  d" }' w1 S1 ^4 Y# ]6 ?% K
               else if (BEAN_ELEMENT.equals(node.getNodeName())) {
. @. @3 g: h/ z! h- Z$ B' Z                   beanDefinitionCount++;
+ o# L6 e1 Z  v: I" q) ?, x                   BeanDefinitionHolder bdHolder = parseBeanDefinitionElement(ele, false);1 S+ d( H" F; M/ M
                   BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.beanDefinitionReader.getBeanFactory());" M" A% e2 R3 [5 _6 \6 }5 z
               }
4 ^; L# f& ^+ a  @, C+ X# ?2 N2 a* }2 z1 o5 |0 h+ J
   这里是当碰到一个bean标签的时候所进行的处理,也既是对bean的定义进行解释,可以看到parseBeanDefinitionElement方法的第一个参数就是bean则个元素,第二个参数表示该bean是否为内置的bean,从这里进行解释的bean都不可能是内置的,所以这里直接以false为参数,打开parseBeanDefinitionElement方法,就可以看到这个方法里就是对bean的内部的解释,也很简单,也不多讲了,呵呵(下班时间已经到了,所以就写这么多了,基本的流程也就这样,没什么特别难的地方。),对了,最后还有一点就是解释完后,bean的定义将会被保存到beanFactory中,这个beanFactory的实现就是XmlBeanFactory了,该beanFactory是在new的时候被传递到reader中的,就是该类中以下这行代码:5 J# ]8 r. \+ X- H
以下内容为程序代码:; i4 C! q& g1 K: L  y. V1 A5 Y0 b8 ]7 w
0 o8 `2 S% R- O5 e
   private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
; ]- H0 D' Z+ }2 }! G& M
# |" A2 E! E+ m1 d) ?6 \1 M) i; b5 `; ]! P: U* K7 S; C3 Z  p
   好了,就这么多了,本文只作为参考,只讲解了如何加载bean定义这块,只作为一个参考,希望对其他朋友能有所帮助吧,因为时间匆忙,有错漏的地方请指正。

科帮网 1、本主题所有言论和图片纯属会员个人意见,与本社区立场无关
2、本站所有主题由该帖子作者发表,该帖子作者与科帮网享有帖子相关版权
3、其他单位或个人使用、转载或引用本文时必须同时征得该帖子作者和科帮网的同意
4、帖子作者须承担一切因本文发表而直接或间接导致的民事或刑事法律责任
5、本帖部分内容转载自其它媒体,但并不代表本站赞同其观点和对其真实性负责
6、如本帖侵犯到任何版权问题,请立即告知本站,本站将及时予与删除并致以最深的歉意
7、科帮网管理员和版主有权不事先通知发贴者而删除本文


JAVA爱好者①群:JAVA爱好者① JAVA爱好者②群:JAVA爱好者② JAVA爱好者③ : JAVA爱好者③

快速回复
您需要登录后才可以回帖 登录 | 立即注册

   

关闭

站长推荐上一条 /1 下一条

发布主题 快速回复 返回列表 联系我们 官方QQ群 科帮网手机客户端
快速回复 返回顶部 返回列表