用了很多第三方库,也看了些源码,总是想如果自己写一个类似的库,读取xml配置文件(properties配置文件比较简单) 该如何给配置文件添加头,添加校验,因为xml配置文件相对于properties配置文件结构更加清晰。最近刚好在看spring 1.2.9版本的源码,就来总结一下如何定义一个dtd文件用于约束xml文件的配置。这里之所以选择这个非常久远的低版本,主要是因为代码少,便于阅读。虽然版本很低,但是其核心设计,核心类都没变,比如ApplicationContext ,各种 ApplicationEvent ,各种Listenser及解析XML的入口BeanDefinition, BeanDefinitionParser, BeanDefinitionReader等。这里记录一下spring各种版本的获取地址:Spring各种版本源码官方仓库 。找了好久。废话有点多,下面进入正题。
关于dtd文件的结构
随便找一个第三方库的dtd文件,我这里直接把spring beans的模块中的dtd文件拿出来:
1 <?xml version="1.0" encoding="UTF-8"?> 2 3 <!-- 4 Spring XML Beans DTD, version 1.2 5 Authors: Rod Johnson, Juergen Hoeller, Alef Arendsen, Colin Sampaleanu, Rob Harrop 6 7 This defines a simple and consistent way of creating a namespace 8 of JavaBeans objects, managed by a Spring BeanFactory, read by 9 XmlBeanDefinitionReader (with DefaultXmlBeanDefinitionParser). 10 11 This document type is used by most Spring functionality, including 12 web application contexts, which are based on bean factories. 13 14 Each "bean" element in this document defines a JavaBean. 15 Typically the bean class is specified, along with JavaBean properties 16 and/or constructor arguments. 17 18 Bean instances can be "singletons" (shared instances) or "prototypes" 19 (independent instances). Further scopes are supposed to be built on top 20 of the core BeanFactory infrastructure and are therefore not part of it. 21 22 References among beans are supported, that is, setting a JavaBean property 23 or a constructor argument to refer to another bean in the same factory 24 (or an ancestor factory). 25 26 As alternative to bean references, "inner bean definitions" can be used. 27 Singleton flags of such inner bean definitions are effectively ignored: 28 Inner beans are typically anonymous prototypes. 29 30 There is also support for lists, sets, maps, and java.util.Properties 31 as bean property types or constructor argument types. 32 33 As the format is simple, a DTD is sufficient, and there's no need 34 for a schema at this point. 35 36 XML documents that conform to this DTD should declare the following doctype: 37 38 <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" 39 "http://www.springframework.org/dtd/spring-beans.dtd"> 40 --> 41 42 43 <!-- 44 The document root. A document can contain bean definitions only, 45 imports only, or a mixture of both (typically with imports first). 46 --> 47 <!ELEMENT beans ( 48 description?, 49 (import | alias | bean)* 50 )> 51 52 <!-- 53 Default values for all bean definitions. Can be overridden at 54 the "bean" level. See those attribute definitions for details. 55 --> 56 <!ATTLIST beans default-lazy-init (true | false) "false"> 57 <!ATTLIST beans default-autowire (no | byName | byType | constructor | autodetect) "no"> 58 <!ATTLIST beans default-dependency-check (none | objects | simple | all) "none"> 59 <!ATTLIST beans default-init-method CDATA #IMPLIED> 60 <!ATTLIST beans default-destroy-method CDATA #IMPLIED> 61 62 <!-- 63 Element containing informative text describing the purpose of the enclosing 64 element. Always optional. 65 Used primarily for user documentation of XML bean definition documents. 66 --> 67 <!ELEMENT description (#PCDATA)> 68 69 70 <!-- 71 Specifies an XML bean definition resource to import. 72 --> 73 <!ELEMENT import EMPTY> 74 75 <!-- 76 The relative resource location of the XML bean definition file to import, 77 for example "myImport.xml" or "includes/myImport.xml" or "../myImport.xml". 78 --> 79 <!ATTLIST import resource CDATA #REQUIRED> 80 81 82 <!-- 83 Defines an alias for a bean, which can reside in a different definition file. 84 --> 85 <!ELEMENT alias EMPTY> 86 87 <!-- 88 The name of the bean to define an alias for. 89 --> 90 <!ATTLIST alias name CDATA #REQUIRED> 91 92 <!-- 93 The alias name to define for the bean. 94 --> 95 <!ATTLIST alias alias CDATA #REQUIRED> 96 97 98 <!-- 99 Defines a single (usually named) bean. 100 101 A bean definition may contain nested tags for constructor arguments, 102 property values, lookup methods, and replaced methods. Mixing constructor 103 injection and setter injection on the same bean is explicitly supported. 104 --> 105 <!ELEMENT bean ( 106 description?, 107 (constructor-arg | property | lookup-method | replaced-method)* 108 )> 109 110 <!-- 111 Beans can be identified by an id, to enable reference checking. 112 113 There are constraints on a valid XML id: if you want to reference your bean 114 in Java code using a name that's illegal as an XML id, use the optional 115 "name" attribute. If neither is given, the bean class name is used as id 116 (with an appended counter like "#2" if there is already a bean with that name). 117 --> 118 <!ATTLIST bean id ID #IMPLIED> 119 120 <!-- 121 Optional. Can be used to create one or more aliases illegal in an id. 122 Multiple aliases can be separated by any number of spaces, commas, or 123 semi-colons (or indeed any mixture of the three). 124 --> 125 <!ATTLIST bean name CDATA #IMPLIED> 126 127 <!-- 128 Each bean definition must specify the fully qualified name of the class, 129 except if it pure serves as parent for child bean definitions. 130 --> 131 <!ATTLIST bean class CDATA #IMPLIED> 132 133 <!-- 134 Optionally specify a parent bean definition. 135 136 Will use the bean class of the parent if none specified, but can 137 also override it. In the latter case, the child bean class must be 138 compatible with the parent, i.e. accept the parent's property values 139 and constructor argument values, if any. 140 141 A child bean definition will inherit constructor argument values, 142 property values and method overrides from the parent, with the option 143 to add new values. If init method, destroy method, factory bean and/or factory 144 method are specified, they will override the corresponding parent settings. 145 146 The remaining settings will always be taken from the child definition: 147 depends on, autowire mode, dependency check, singleton, lazy init. 148 --> 149 <!ATTLIST bean parent CDATA #IMPLIED> 150 151 <!-- 152 Is this bean "abstract", i.e. not meant to be instantiated itself but 153 rather just serving as parent for concrete child bean definitions. 154 Default is "false". Specify "true" to tell the bean factory to not try to 155 instantiate that particular bean in any case. 156 157 Note: This attribute will not be inherited by child bean definitions. 158 Hence, it needs to be specified per concrete bean definition. 159 --> 160 <!ATTLIST bean abstract (true | false) #IMPLIED> 161 162 <!-- 163 Is this bean a "singleton" (one shared instance, which will 164 be returned by all calls to getBean() with the id), 165 or a "prototype" (independent instance resulting from each call to 166 getBean(). Default is singleton. 167 168 Singletons are most commonly used, and are ideal for multi-threaded 169 service objects. 170 171 Note: This attribute will not be inherited by child bean definitions. 172 Hence, it needs to be specified per concrete bean definition. 173 --> 174 <!ATTLIST bean singleton (true | false) #IMPLIED> 175 176 <!-- 177 If this bean should be lazily initialized. 178 If false, it will get instantiated on startup by bean factories 179 that perform eager initialization of singletons. 180 181 Note: This attribute will not be inherited by child bean definitions. 182 Hence, it needs to be specified per concrete bean definition. 183 --> 184 <!ATTLIST bean lazy-init (true | false | default) "default"> 185 186 <!-- 187 Optional attribute controlling whether to "autowire" bean properties. 188 This is an automagical process in which bean references don't need to be coded 189 explicitly in the XML bean definition file, but Spring works out dependencies. 190 191 There are 5 modes: 192 193 1. "no" 194 The traditional Spring default. No automagical wiring. Bean references 195 must be defined in the XML file via the <ref> element. We recommend this 196 in most cases as it makes documentation more explicit. 197 198 2. "byName" 199 Autowiring by property name. If a bean of class Cat exposes a dog property, 200 Spring will try to set this to the value of the bean "dog" in the current factory. 201 If there is no matching bean by name, nothing special happens; 202 use dependency-check="objects" to raise an error in that case. 203 204 3. "byType" 205 Autowiring if there is exactly one bean of the property type in the bean factory. 206 If there is more than one, a fatal error is raised, and you can't use byType 207 autowiring for that bean. If there is none, nothing special happens; 208 use dependency-check="objects" to raise an error in that case. 209 210 4. "constructor" 211 Analogous to "byType" for constructor arguments. If there isn't exactly one bean 212 of the constructor argument type in the bean factory, a fatal error is raised. 213 214 5. "autodetect" 215 Chooses "constructor" or "byType" through introspection of the bean class. 216 If a default constructor is found, "byType" gets applied. 217 218 The latter two are similar to PicoContainer and make bean factories simple to 219 configure for small namespaces, but doesn't work as well as standard Spring 220 behaviour for bigger applications. 221 222 Note that explicit dependencies, i.e. "property" and "constructor-arg" elements, 223 always override autowiring. Autowire behavior can be combined with dependency 224 checking, which will be performed after all autowiring has been completed. 225 226 Note: This attribute will not be inherited by child bean definitions. 227 Hence, it needs to be specified per concrete bean definition. 228 --> 229 <!ATTLIST bean autowire (no | byName | byType | constructor | autodetect | default) "default"> 230 231 <!-- 232 Optional attribute controlling whether to check whether all this 233 beans dependencies, expressed in its properties, are satisfied. 234 Default is no dependency checking. 235 236 "simple" type dependency checking includes primitives and String 237 "object" includes collaborators (other beans in the factory) 238 "all" includes both types of dependency checking 239 240 Note: This attribute will not be inherited by child bean definitions. 241 Hence, it needs to be specified per concrete bean definition. 242 --> 243 <!ATTLIST bean dependency-check (none | objects | simple | all | default) "default"> 244 245 <!-- 246 The names of the beans that this bean depends on being initialized. 247 The bean factory will guarantee that these beans get initialized before. 248 249 Note that dependencies are normally expressed through bean properties or 250 constructor arguments. This property should just be necessary for other kinds 251 of dependencies like statics (*ugh*) or database preparation on startup. 252 253 Note: This attribute will not be inherited by child bean definitions. 254 Hence, it needs to be specified per concrete bean definition. 255 --> 256 <!ATTLIST bean depends-on CDATA #IMPLIED> 257 258 <!-- 259 Optional attribute for the name of the custom initialization method 260 to invoke after setting bean properties. The method must have no arguments, 261 but may throw any exception. 262 --> 263 <!ATTLIST bean init-method CDATA #IMPLIED> 264 265 <!-- 266 Optional attribute for the name of the custom destroy method to invoke 267 on bean factory shutdown. The method must have no arguments, 268 but may throw any exception. Note: Only invoked on singleton beans! 269 --> 270 <!ATTLIST bean destroy-method CDATA #IMPLIED> 271 272 <!-- 273 Optional attribute specifying the name of a factory method to use to 274 create this object. Use constructor-arg elements to specify arguments 275 to the factory method, if it takes arguments. Autowiring does not apply 276 to factory methods. 277 278 If the "class" attribute is present, the factory method will be a static 279 method on the class specified by the "class" attribute on this bean 280 definition. Often this will be the same class as that of the constructed 281 object - for example, when the factory method is used as an alternative 282 to a constructor. However, it may be on a different class. In that case, 283 the created object will *not* be of the class specified in the "class" 284 attribute. This is analogous to FactoryBean behavior. 285 286 If the "factory-bean" attribute is present, the "class" attribute is not 287 used, and the factory method will be an instance method on the object 288 returned from a getBean call with the specified bean name. The factory 289 bean may be defined as a singleton or a prototype. 290 291 The factory method can have any number of arguments. Autowiring is not 292 supported. Use indexed constructor-arg elements in conjunction with the 293 factory-method attribute. 294 295 Setter Injection can be used in conjunction with a factory method. 296 Method Injection cannot, as the factory method returns an instance, 297 which will be used when the container creates the bean. 298 --> 299 <!ATTLIST bean factory-method CDATA #IMPLIED> 300 301 <!-- 302 Alternative to class attribute for factory-method usage. 303 If this is specified, no class attribute should be used. 304 This should be set to the name of a bean in the current or 305 ancestor factories that contains the relevant factory method. 306 This allows the factory itself to be configured using Dependency 307 Injection, and an instance (rather than static) method to be used. 308 --> 309 <!ATTLIST bean factory-bean CDATA #IMPLIED> 310 311 312 <!-- 313 Bean definitions can specify zero or more constructor arguments. 314 This is an alternative to "autowire constructor". 315 Arguments correspond to either a specific index of the constructor argument 316 list or are supposed to be matched generically by type. 317 318 Note: A single generic argument value will just be used once, rather than 319 potentially matched multiple times (as of Spring 1.1). 320 321 constructor-arg elements are also used in conjunction with the factory-method 322 element to construct beans using static or instance factory methods. 323 --> 324 <!ELEMENT constructor-arg ( 325 description?, 326 (bean | ref | idref | value | null | list | set | map | props)? 327 )> 328 329 <!-- 330 The constructor-arg tag can have an optional index attribute, 331 to specify the exact index in the constructor argument list. Only needed 332 to avoid ambiguities, e.g. in case of 2 arguments of the same type. 333 --> 334 <!ATTLIST constructor-arg index CDATA #IMPLIED> 335 336 <!-- 337 The constructor-arg tag can have an optional type attribute, 338 to specify the exact type of the constructor argument. Only needed 339 to avoid ambiguities, e.g. in case of 2 single argument constructors 340 that can both be converted from a String. 341 --> 342 <!ATTLIST constructor-arg type CDATA #IMPLIED> 343 344 <!-- 345 A short-cut alternative to a child element "ref bean=". 346 --> 347 <!ATTLIST constructor-arg ref CDATA #IMPLIED> 348 349 <!-- 350 A short-cut alternative to a child element "value". 351 --> 352 <!ATTLIST constructor-arg value CDATA #IMPLIED> 353 354 355 <!-- 356 Bean definitions can have zero or more properties. 357 Property elements correspond to JavaBean setter methods exposed 358 by the bean classes. Spring supports primitives, references to other 359 beans in the same or related factories, lists, maps and properties. 360 --> 361 <!ELEMENT property ( 362 description?, 363 (bean | ref | idref | value | null | list | set | map | props)? 364 )> 365 366 <!-- 367 The property name attribute is the name of the JavaBean property. 368 This follows JavaBean conventions: a name of "age" would correspond 369 to setAge()/optional getAge() methods. 370 --> 371 <!ATTLIST property name CDATA #REQUIRED> 372 373 <!-- 374 A short-cut alternative to a child element "ref bean=". 375 --> 376 <!ATTLIST property ref CDATA #IMPLIED> 377 378 <!-- 379 A short-cut alternative to a child element "value". 380 --> 381 <!ATTLIST property value CDATA #IMPLIED> 382 383 384 <!-- 385 A lookup method causes the IoC container to override the given method and return 386 the bean with the name given in the bean attribute. This is a form of Method Injection. 387 It's particularly useful as an alternative to implementing the BeanFactoryAware 388 interface, in order to be able to make getBean() calls for non-singleton instances 389 at runtime. In this case, Method Injection is a less invasive alternative. 390 --> 391 <!ELEMENT lookup-method EMPTY> 392 393 <!-- 394 Name of a lookup method. This method should take no arguments. 395 --> 396 <!ATTLIST lookup-method name CDATA #IMPLIED> 397 398 <!-- 399 Name of the bean in the current or ancestor factories that the lookup method 400 should resolve to. Often this bean will be a prototype, in which case the 401 lookup method will return a distinct instance on every invocation. This 402 is useful for single-threaded objects. 403 --> 404 <!ATTLIST lookup-method bean CDATA #IMPLIED> 405 406 407 <!-- 408 Similar to the lookup method mechanism, the replaced-method element is used to control 409 IoC container method overriding: Method Injection. This mechanism allows the overriding 410 of a method with arbitrary code. 411 --> 412 <!ELEMENT replaced-method ( 413 (arg-type)* 414 )> 415 416 <!-- 417 Name of the method whose implementation should be replaced by the IoC container. 418 If this method is not overloaded, there's no need to use arg-type subelements. 419 If this method is overloaded, arg-type subelements must be used for all 420 override definitions for the method. 421 --> 422 <!ATTLIST replaced-method name CDATA #IMPLIED> 423 424 <!-- 425 Bean name of an implementation of the MethodReplacer interface 426 in the current or ancestor factories. This may be a singleton or prototype 427 bean. If it's a prototype, a new instance will be used for each method replacement. 428 Singleton usage is the norm. 429 --> 430 <!ATTLIST replaced-method replacer CDATA #IMPLIED> 431 432 <!-- 433 Subelement of replaced-method identifying an argument for a replaced method 434 in the event of method overloading. 435 --> 436 <!ELEMENT arg-type (#PCDATA)> 437 438 <!-- 439 Specification of the type of an overloaded method argument as a String. 440 For convenience, this may be a substring of the FQN. E.g. all the 441 following would match "java.lang.String": 442 - java.lang.String 443 - String 444 - Str 445 446 As the number of arguments will be checked also, this convenience can often 447 be used to save typing. 448 --> 449 <!ATTLIST arg-type match CDATA #IMPLIED> 450 451 452 <!-- 453 Defines a reference to another bean in this factory or an external 454 factory (parent or included factory). 455 --> 456 <!ELEMENT ref EMPTY> 457 458 <!-- 459 References must specify a name of the target bean. 460 The "bean" attribute can reference any name from any bean in the context, 461 to be checked at runtime. 462 Local references, using the "local" attribute, have to use bean ids; 463 they can be checked by this DTD, thus should be preferred for references 464 within the same bean factory XML file. 465 --> 466 <!ATTLIST ref bean CDATA #IMPLIED> 467 <!ATTLIST ref local IDREF #IMPLIED> 468 <!ATTLIST ref parent CDATA #IMPLIED> 469 470 471 <!-- 472 Defines a string property value, which must also be the id of another 473 bean in this factory or an external factory (parent or included factory). 474 While a regular 'value' element could instead be used for the same effect, 475 using idref in this case allows validation of local bean ids by the xml 476 parser, and name completion by helper tools. 477 --> 478 <!ELEMENT idref EMPTY> 479 480 <!-- 481 ID refs must specify a name of the target bean. 482 The "bean" attribute can reference any name from any bean in the context, 483 potentially to be checked at runtime by bean factory implementations. 484 Local references, using the "local" attribute, have to use bean ids; 485 they can be checked by this DTD, thus should be preferred for references 486 within the same bean factory XML file. 487 --> 488 <!ATTLIST idref bean CDATA #IMPLIED> 489 <!ATTLIST idref local IDREF #IMPLIED> 490 491 492 <!-- 493 Contains a string representation of a property value. 494 The property may be a string, or may be converted to the 495 required type using the JavaBeans PropertyEditor 496 machinery. This makes it possible for application developers 497 to write custom PropertyEditor implementations that can 498 convert strings to objects. 499 500 Note that this is recommended for simple objects only. 501 Configure more complex objects by populating JavaBean 502 properties with references to other beans. 503 --> 504 <!ELEMENT value (#PCDATA)> 505 506 <!-- 507 The value tag can have an optional type attribute, to specify the 508 exact type that the value should be converted to. Only needed 509 if the type of the target property or constructor argument is 510 too generic: for example, in case of a collection element. 511 --> 512 <!ATTLIST value type CDATA #IMPLIED> 513 514 <!-- 515 Denotes a Java null value. Necessary because an empty "value" tag 516 will resolve to an empty String, which will not be resolved to a 517 null value unless a special PropertyEditor does so. 518 --> 519 <!ELEMENT null (#PCDATA)> 520 521 522 <!-- 523 A list can contain multiple inner bean, ref, collection, or value elements. 524 Java lists are untyped, pending generics support in Java 1.5, 525 although references will be strongly typed. 526 A list can also map to an array type. The necessary conversion 527 is automatically performed by the BeanFactory. 528 --> 529 <!ELEMENT list ( 530 (bean | ref | idref | value | null | list | set | map | props)* 531 )> 532 533 <!-- 534 A set can contain multiple inner bean, ref, collection, or value elements. 535 Java sets are untyped, pending generics support in Java 1.5, 536 although references will be strongly typed. 537 --> 538 <!ELEMENT set ( 539 (bean | ref | idref | value | null | list | set | map | props)* 540 )> 541 542 543 <!-- 544 A Spring map is a mapping from a string key to object. 545 Maps may be empty. 546 --> 547 <!ELEMENT map ( 548 (entry)* 549 )> 550 551 <!-- 552 A map entry can be an inner bean, ref, value, or collection. 553 The key of the entry is given by the "key" attribute or child element. 554 --> 555 <!ELEMENT entry ( 556 key?, 557 (bean | ref | idref | value | null | list | set | map | props)? 558 )> 559 560 <!-- 561 Each map element must specify its key as attribute or as child element. 562 A key attribute is always a String value. 563 --> 564 <!ATTLIST entry key CDATA #IMPLIED> 565 566 <!-- 567 A short-cut alternative to a "key" element with a "ref bean=" child element. 568 --> 569 <!ATTLIST entry key-ref CDATA #IMPLIED> 570 571 <!-- 572 A short-cut alternative to a child element "value". 573 --> 574 <!ATTLIST entry value CDATA #IMPLIED> 575 576 <!-- 577 A short-cut alternative to a child element "ref bean=". 578 --> 579 <!ATTLIST entry value-ref CDATA #IMPLIED> 580 581 <!-- 582 A key element can contain an inner bean, ref, value, or collection. 583 --> 584 <!ELEMENT key ( 585 (bean | ref | idref | value | null | list | set | map | props) 586 )> 587 588 589 <!-- 590 Props elements differ from map elements in that values must be strings. 591 Props may be empty. 592 --> 593 <!ELEMENT props ( 594 (prop)* 595 )> 596 597 <!-- 598 Element content is the string value of the property. 599 Note that whitespace is trimmed off to avoid unwanted whitespace 600 caused by typical XML formatting. 601 --> 602 <!ELEMENT prop (#PCDATA)> 603 604 <!-- 605 Each property element must specify its key. 606 --> 607 <!ATTLIST prop key CDATA #REQUIRED>
下面解释一下:
1.从上面文件的内容头部
<?xml version="1.0" encoding="UTF-8"?>
可以看出dtd文件也是一种xml文件,并且指定了编码方式
2.接着下面一段是注释,没啥好说的
3.紧接着的
<!-- The document root. A document can contain bean definitions only, imports only, or a mixture of both (typically with imports first). --> <!ELEMENT beans ( description?, (import | alias | bean)* )>
a.定义了根元素,根元素的名称是beans,下面可以有description元素, ?是正则表达式,表示这个元素可有,可无,按字面意思就是这个元素是整个配置文件的描述
b.后面的import ,alias, bean 也是和description同级的兄弟元素, 后面的 * 表示每一个元素可以没有,也可以有多个.
4.接下来的
<!-- Default values for all bean definitions. Can be overridden at the "bean" level. See those attribute definitions for details. --> <!ATTLIST beans default-lazy-init (true | false) "false"> <!ATTLIST beans default-autowire (no | byName | byType | constructor | autodetect) "no"> <!ATTLIST beans default-dependency-check (none | objects | simple | all) "none"> <!ATTLIST beans default-init-method CDATA #IMPLIED> <!ATTLIST beans default-destroy-method CDATA #IMPLIED>
ATTLIST 这一看就是 attribute list的缩写吧,嗯,就是定义属性列表, 前面的beans 指的是当前的属性属于哪个元素, 后面的default-lazy-init, default-autowire 就是属性名称,()里面的值是当前属性的可选值 | 是或者的意思,后面带“”表示如果没有配置,这个属性使用的默认值。
这里default-init-method这个属性的值是 CDATA 类型的,CDATA 是一个数据类型CharactorData的缩写,也即初始化方法的值是个字符串,后面的 #IMPLITED 表示当前这个属性可以忽略,可配置,也可不配置 是 implementation ignored的缩写