Spring在创建Bean的过程中,其中一种方式是通过读取XML配置文件,同时XML配置文件,可以配置事务,切面,Bean的初始化等一些列操作,但是Spring是如何读取配置文件,如下:
1.配置文件的读取过程:
启动类创建Bean的代码为:
public class App
{
public static void main( String[] args )
{
BeanFactory bf = new XmlBeanFactory(new ClassPathResource("application.xml"));
People p =(People) bf.getBean("people");
System.out.println("the name is : " + p.getName());
}
}
XML中的配置为:
<?xml version="1.0" encoding="UTF-8"?>
<!--suppress ALL -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="people" class="com.dong.bean.People">
<property name="age" value="26"></property>
<property name="name" value="Liudong"></property>
</bean>
</beans>
2.SpringBean配置文件解读:
(以XMLBeanFactory方式获取)
- new ClassPathResource("application.xml");
ClassPathResource
解析:
application.xml为Spring的配置文件,配置文件中可以配置Bean,事务,数据库。
ClassPathResource类:将资源抽象成URL,并使用不同的Resource处理类来进行处理。Java中使用内部定义的自己的Resource抽象结构:对于不同的资源文件,使用不同的实现类FileSystemResource,ClassPathResource,UrlResource,InputStream等。
ClassPathResource的继承关系:
ClassPathResource extends AbstractFileResolvingResource;
AbstractFileResolvingResource extends AbstractResource;
AbstractResource implements Resource;
Resource extends InputStreamSource;
Resource
Resource类中定义了资源的基本操作:资源是否存在,是否可读,可打开,获取资源路径,获取文件名;
InputStreamSource
getInputStream()将资源对象转换为输入流,只要得到字节流,就可以获取资源的任何信息;
- BeanFactory bf = new XmlBeanFactory(new ClassPathResource("application.xml"));
XmlBeanFactory
解析:
工厂类,继承DefaultListableBeanFactory,XmlBeanFactory使用自定义的XML读取其XmlBeanDefinitionReader.
它的继承关系有:
XmlBeanFactory extends DefaultListableBeanFactory;
XmlBeanFactory使用自定义的XML读取器XmlBeanDefinitionReader,实现个性化的BeanDefinitionReader读取。
DefaultListableBeanFactory
Spring注册,以及加载bean的默认实现;
它的继承关系为
DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable
AbstractAutowireCapableBeanFactory
功能是忽略给定接口的自定装配功能,当Bean中有属性需要初始化时,就会被自动初始化,但是有时候我们不需要初始化,例如属性实现了BeanNameAware接口。
- this(resource, null);
XmlBeanFactory调用内部的构造器,this(resource, parentBeanFactory null);
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}
- XmlBeanDefinitionReader:
reader是XmlBeanDefinitionReader的实例,
继承关系为:
XmlBeanDefinitionReader extends AbstractBeanDefinitionReader;
AbstractBeanDefinitionReader implements EnvironmentCapable, BeanDefinitionReader;
AbstractBeanDefinitionReader:定义bean的注册,bean的定义;
EnvironmentCapable:所有Spring应用程序上下文都具有EnvironmentCapable功能,并且该接口主要用于在接受BeanFactory实例的框架方法中执行检查,以便与环境进行交互;
BeanDefinitionReader:Bean定义阅读器的接口。使用Resource和String位置参数指定加载方法。
loadBeanDefinitions(resource):用来装载资源文件,调用内部的loadBeanDefinitions.
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { return loadBeanDefinitions(new EncodedResource(resource));}
- EncodedResource
用于对文件的编码进行处理,可以通过EncoderResource构造函数,设置相应的文件编码, 同时也可以通过get,set等相应的扩展方法设置相应的编码,以及获取相应编码的Reader对象; - loadBeanDefinitions(new EncodedResource(resource))
装载编译好的资源,返回doLoadBeanDefinitions(inputSource, encodedResource.getResource()); - doLoadBeanDefinitions(inputSource, encodedResource.getResource())
Document doc = doLoadDocument(inputSource, resource);
return registerBeanDefinitions(doc, resource);
- Document
表示的是XML或HTML文档的实例,继承与Node接口,主要方法有获取文档的类型,创建元素,创建文本节点,创建属性,获取文档的id,等等; - doLoadDocument(inputSource, resource)
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,getValidationModeForResource(resource), isNamespaceAware());
- getEntityResolver()
EntityResolver 类,提供一个如何寻找DTD声明的方法,它的两个参数:publicId,systemId;
publicId:被引用的外部实体的公共标识符;如果未提供,则为null。
systemId:被引用的外部实体的系统标识符;
SAX是一个用于各种各样的XML解析器(或者其他可以看做XML解析器的解析器)通用接口的实现 ,SAX读取XML文档上的声明,根据声明寻找DTD定义,默认的寻找规则是即通过声明的DTD的URI地址进行下载相应的DTD声明,并进行认证。
补充:DTD:Document Type Definition,文档类型定义,是用来保证XML文档格式正确的有效方法,可以通过比较XML和DTD文件来看文档是否符合规范。
protected EntityResolver getEntityResolver() {
if (this.entityResolver == null) {
// Determine default EntityResolver to use.
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader != null) {
this.entityResolver = new ResourceEntityResolver(resourceLoader);
}
else {
this.entityResolver = new DelegatingEntityResolver(getBeanClassLoader());
}
}
return this.entityResolver;
}
Spring默认使用 new DelegatingEntityResolver(getBeanClassLoader());
- getValidationModeForResource(resource)
获取对应资源的验证模式;
验证模式有DTD和XSD.
DTD:Document Type Definition,文档类型定义,是用来保证XML文档格式正确的有效方法,可以通过比较XML和DTD文件来看文档是否符合规范,元素和标签的使用是否正确。
XSD:XML Schema Definition,表述XML的文档结构,可用来验证XML文档.XML本身也是XML文档,可以使用XML解析器去解析它。XML Schema文档时,需要声明命名空间,还需要指定命名空间对应的位置。
- 如果设置了验证模式,则使用设定的,没有的话,则使用默认的验证模式;
至此,loadDocument方法执行完毕,返回Document对象; - registerBeanDefinitions(doc, resource)
实现Bean的注册;
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
createBeanDefinitionDocumentReader();创建一个BeanDefinitionDocumentReader实例对象;
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));调用registerBeanDefinitions方法,注意:这里的documentReader对象是DefaultBeanDefinitionDocumentReader类的实例对象,调用的也是该实例的registerBeanDefinitions方法;
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement();
doRegisterBeanDefinitions(root);
}
- doRegisterBeanDefinitions(root);
开始进行解析,
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);//处理profile属性,
profile是用来进行多环境配置的属性;
preProcessXml(root);//解析前处理
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);//解析后处理
4.parseBeanDefinitions(root, this.delegate);
进行具体的解析;
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate); //默认
}
else {
delegate.parseCustomElement(ele); //自定义
}
根据标签是默认标签还是自定义标签,执行不同的解析;