1,核心类的介绍
1.1
public class DefaultListableBeanFactory
bean加载的核心部分,xmlBeanFactory中定义了XML的读取器,xmlBeanFactory对DefaultListableBeanFactory的拓展,注册和获取bean都是从父类继承的方法,增加了reader的属性,通过reader属性对资源文件读取和注册
1.2
public class XmlBeanDefinitionReader
读取xml配置的过程如下
读取xml文件的过程
2,xmlBeanFactory容器的基础
我们要深入理解一下代码的过程
new XmlBeanFactory(new ClassPathResource("......xml"));
我们先通过ClassPathResource读取xml配置文件,生成Resource对象,后续的处理就可以通过Resource提供的各种服务进行操作了
2.1 了解配置文件的读取(resource接口)
/**
* Interface for a resource descriptor that abstracts from the actual
* type of underlying resource, such as a file or class path resource.
*
* <p>An InputStream can be opened for every resource if it exists in
* physical form, but a URL or File handle can just be returned for
* certain resources. The actual behavior is implementation-specific.
*
* @author Juergen Hoeller
* @since 28.12.2003
* @see #getInputStream()
* @see #getURL()
* @see #getURI()
* @see #getFile()
* @see WritableResource
* @see ContextResource
* @see UrlResource
* @see FileUrlResource
* @see FileSystemResource
* @see ClassPathResource
* @see ByteArrayResource
* @see InputStreamResource
*/
public interface Resource extends InputStreamSource {
/**
* Simple interface for objects that are sources for an {@link InputStream}.
*
* <p>This is the base interface for Spring's more extensive {@link Resource} interface.
*
* <p>For single-use streams, {@link InputStreamResource} can be used for any
* given {@code InputStream}. Spring's {@link ByteArrayResource} or any
* file-based {@code Resource} implementation can be used as a concrete
* instance, allowing one to read the underlying
content stream multiple times.
* This makes this interface useful as an abstract content source for mail
* attachments, for example.
*
* @author Juergen Hoeller
* @since 20.01.2004
* @see java.io.InputStream
* @see Resource
* @see InputStreamResource
* @see ByteArrayResource
*/
public interface InputStreamSource {
通过Resource和InputStreamSource就可以加载文件,按照原来的方式进行开发,如下
Resource resource=new ClassPathResource("文件名称");
InputStream in=resource.getInputStream();
读取完毕之后,工作就交给XmlReaderDefinitionReader进行处理
2.2 XmlFactoryFactory
2.2.1
//XmlFactoryFactory的构造方法
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, null);
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);//用于合并
this.reader.loadBeanDefinitions(resource);
}
通过阅读以上的构造方法,this.reader.loadBeanDefinitions(resource)为核心代码,xmlBeanFactoryDefinition就是在这段代码中完成的,我们首先进入super(parentBeanFactory)研究一下
public AbstractAutowireCapableBeanFactory() {
super();
ignoreDependencyInterface(BeanNameAware.class);
ignoreDependencyInterface(BeanFactoryAware.class);
ignoreDependencyInterface(BeanClassLoaderAware.class);
}
摘自书籍
接下来我们介绍XMLBeanFactoryDefinitionReader,整个资源的加载的切入点
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
其中EncodedResource的作用大概是对资源文件进行编码的,编码的逻辑在EncodedResource中的getReader方法中,如下
public Reader getReader() throws IOException {
if (this.charset != null) {
return new InputStreamReader(this.resource.getInputStream(), this.charset);
}
else if (this.encoding != null) {
return new InputStreamReader(this.resource.getInputStream(), this.encoding);
}
else {
return new InputStreamReader(this.resource.getInputStream());
}
}
对EncodedResource的研究到此为止,然后我们转入研究loadBeanDefinitions
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isTraceEnabled()) {
logger.trace("Loading XML bean definitions from " + encodedResource);
}
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());是此方法的核心逻辑,我们在进入这个方法,此方法最终的三个地方如下(没有截所有的方法)
try {
Document doc = doLoadDocument(inputSource, resource);
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
}
最重要的registerBeanDefinitions,根据这个注册bean的信息
3 获取document
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isTraceEnabled()) {
logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
}
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
return builder.parse(inputSource);
}
大概操作就是创建对象先是DocumentBuilderFactory,再是 DocumentBuilder
最后通过DocumentBuilder解析inputSource,来获取document的对象,这是我么注意到传入的参数中有EntityResolver类型的参数,那么此类型的参数的作用是?
大概意思如下:通过EntityResolver来寻找xml文件的DTD的所在位置,我们进入到EntityResolver的接口中
public abstract InputSource resolveEntity (String publicId,
String systemId)
throws SAXException, IOException;
非重点内容,大概了解
4 BeanDefinitions
此时我们拥有了document的文件,这是我们就开始注册并且提取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;
}
//BeanDefinitionDocumentReader是一个接口
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//获得已经加载好的bean的个数
int countBefore = getRegistry().getBeanDefinitionCount();
//加载并且注册bean
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
//返货本次加载的bean的个数
return getRegistry().getBeanDefinitionCount() - countBefore;
第一句代码的createBeanDefinitionDocumentReader实际上创建的是一个
defaultBeanDefinitionDocumentReader,我们在进入defaultBeanDefinitionDocumentReader,查看代码如下
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
doRegisterBeanDefinitions(doc.getDocumentElement());
}
在进入doRegisterBeanDefinitions的方法中,如下
protected void doRegisterBeanDefinitions(Element root) {
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
// We cannot use Profiles.of(...) since profile expressions are not supported
// in XML config. See SPR-12458 for details.
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
preProcessXml(root);
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);
this.delegate = parent;
}
其中的preProcessXml和postProcessXml方法,进入其中是空的,此类是面向继承而创建的,模板设计模式
4.1 profile属性的使用
在配置文件中,方便切换开发和部署的环境,最常用的就是更换不同的数据库
4.2解析并注册BeanDefinition
这是我们就可以对xml进行读取了
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;
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
网友评论