十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
这篇文章将为大家详细讲解有关在Spring中如何使用BeanFactory进行解析bean,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。
创新互联建站制作网站网页找三站合一网站制作公司,专注于网页设计,成都网站设计、网站制作,网站设计,企业网站搭建,网站开发,建网站业务,680元做网站,已为上千服务,创新互联建站网站建设将一如既往的为我们的客户提供最优质的网站建设、网络营销推广服务!
在该文中来讲讲Spring框架中BeanFactory解析bean的过程,该文之前在小编原文中有发表过,先来看一个在Spring中一个基本的bean定义与使用。
package bean; public class TestBean { private String beanName = "beanName"; public String getBeanName() { return beanName; } public void setBeanName(String beanName) { this.beanName = beanName; } }
Spring配置文件root.xml定义如下:
<?xml version="1.0" encoding="UTF-8"?>
下面使用XmlBeanFactory来获取该bean:
public class BeanTest { private static final java.util.logging.Logger logger = LoggerFactory.getLogger(BeanTest.class); @Test public void getBeanTest() { BeanFactory factory = new XmlBeanFactory(new ClassPathResource("root.xml")); TestBean bean = factory.getBean("testBean"); logger.info(bean.getBeanName); } }
这个单元测试运行结果就是输出beanName,上面就是Spring最基本的bean的获取操作,这里我用BeanFactory作为容器来获取bean的操作并不多见,在企业开发中一般是使用功能更完善的ApplicationContext,这里先不讨论这个,下面重点讲解使用BeanFactory获取bean的过程。
现在就来分析下上面的测试代码,看看Spring到底为我们做了什么工作,上面代码完成功能的流程不外乎如此:
1. 读取Spring配置文件root.xml;
2. 根据root.xml中的bean配置找到对应的类的配置,并实例化;
3. 调用实例化后的对象输出结果。
先来看看XmlBeanFactory源码:
public class XmlBeanFactory extends DefaultListableBeanFactory { private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this); public XmlBeanFactory(Resource resource) throws BeansException { this(resource, null); } public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { super(parentBeanFactory); this.reader.loadBeanDefinitions(resource); } }
从上面可以看出XmlBeanFactory继承了DefaultListableBeanFactory,DefaultListableBeanFactory是Spring注册加载bean的默认实现,它是整个bean加载的核心部分,XmlBeanFactory与它的不同点就是XmlBeanFactory使用了自定义的XML读取器XmlBeanDefinitionReader,实现了自己的BeanDefinitionReader读取。
XmlBeanFactory加载bean的关键就在于XmlBeanDefinitionReader,下面看看XmlBeanDefinitionReader的源码(只列出部分):
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { private Class<?> documentReaderClass = DefaultBeanDefinitionDocumentReader.class; private ProblemReporter problemReporter = new FailFastProblemReporter(); private ReaderEventListener eventListener = new EmptyReaderEventListener(); private SourceExtractor sourceExtractor = new NullSourceExtractor(); private NamespaceHandlerResolver namespaceHandlerResolver; private DocumentLoader documentLoader = new DefaultDocumentLoader(); private EntityResolver entityResolver; private ErrorHandler errorHandler = new SimpleSaxErrorHandler(logger); }
XmlBeanDefinitionReader继承自AbstractBeanDefinitionReader,下面是AbstractBeanDefinitionReader的源码(只列出部分):
public abstract class AbstractBeanDefinitionReader implements EnvironmentCapable, BeanDefinitionReader { protected final Log logger = LogFactory.getLog(getClass()); private final BeanDefinitionRegistry registry; private ResourceLoader resourceLoader; private ClassLoader beanClassLoader; private Environment environment; private BeanNameGenerator beanNameGenerator = new DefaultBeanNameGenerator(); }
XmlBeanDefinitionReader主要通过以下三步来加载Spring配置文件中的bean:
1. 通过继承自AbstractBeanDefinitionReader中的方法,使用ResourLoader将资源文件(root.xml)路径转换为对应的Resource文件;
2. 通过DocumentLoader对Resource文件进行转换,将Resource文件转换为Ducument文件;
3. 通过DefaultBeanDefinitionDocumentReader类对Document进行解析,最后再对解析后的Element进行解析。
了解以上基础后,接下来详细分析下一开始例子中的代码:
BeanFactory factory = new XmlBeanFactory(new ClassPathResource("root.xml"));
先看看下面XmlBeanFactory初始化的时序图来进一步了解这段代码的执行,
在这里可以看出BeanTest测试类通过向ClassPathResource的构造方法传入spring的配置文件构造一个Resource资源文件的实例对象,再通过这个Resource资源文件来构造我们想要的XmlBeanFactory实例。在前面XmlBeanFactory源码中的构造方法可以看出,
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.loadBeanDefinition(resource)就是资源加载真正的实现,时序图中XmlBeanDefinitionReader加载数据就是在这里完成的。
接下来跟进this.reader.loadBeanDefinition(resource)方法里面,
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader { @Override public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { return loadBeanDefinitions(new EncodedResource(resource)); } }
在loadBeanDefinition(resource)方法里对资源文件resource使用EncodedResource进行编码处理后继续传入loadBeanDefinitions方法,继续跟进loadBeanDefinitions(new EncodedResource(resource))方法源码:
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { Assert.notNull(encodedResource, "EncodedResource must not be null"); if (logger.isInfoEnabled()) { logger.info("Loading XML bean definitions from " + encodedResource.getResource()); } // 通过属性记录已加载的资源 SetcurrentResources = 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 { // 从resource中获取对应的InputStream,用于下面构造InputSource InputStream inputStream = encodedResource.getResource().getInputStream(); try { InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } // 调用doLoadBeanDefinitions方法 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(); } } }
继续跟进doLoadBeanDefinitions(inputSource, encodedResource.getResource())方法,这是整个bean加载过程的核心方法,在这个方法执行bean的加载。
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { Document doc = doLoadDocument(inputSource, resource); return registerBeanDefinitions(doc, resource); } /* 省略一堆catch */ }
跟进doLoadDocument(inputSource, resource)源码:
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception { return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler, getValidationModeForResource(resource), isNamespaceAware()); }
在doLoadDocument(inputSource, resource)方法里就使用到了前面讲的documentLoader加载Document,这里DocumentLoader是个接口,真正调用的是其实现类DefaultDocumentLoader的loadDocument方法,跟进源码:
public class DefaultDocumentLoader implements DocumentLoader { @Override public Document loadDocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception { DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware); if (logger.isDebugEnabled()) { logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]"); } DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler); return builder.parse(inputSource); } }
从源码可以看出这里先创建DocumentBuilderFactory,再用它创建DocumentBuilder,进而解析inputSource来返回Document对象。得到Document对象后就可以准备注册我们的Bean信息了。
在上面的doLoadBeanDefinitions(inputSource, encodedResource.getResource())方法中拿到Document对象后下面就是执行registerBeanDefinitions(doc, resource)方法了,看源码:
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); documentReader.setEnvironment(getEnvironment()); // 还没注册bean前的BeanDefinition加载个数 int countBefore = getRegistry().getBeanDefinitionCount(); // 加载注册bean documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); // 本次加载注册的BeanDefinition个数 return getRegistry().getBeanDefinitionCount() - countBefore; }
这里的doc就是上面的loadDocument方法加载转换来的,从上面可以看出主要工作是交给BeanDefinitionDocumentReader的registerBeanDefinitions()方法实现的,这里BeanDefinitionDocumentReader是个接口,注册bean功能在默认实现类DefaultBeanDefinitionDocumentReader的该方法实现,跟进它的源码:
@Override public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; logger.debug("Loading bean definitions"); Element root = doc.getDocumentElement(); doRegisterBeanDefinitions(root); }
到这里通过doc.getDocumentElement()获得Element对象后,交给doRegisterBeanDefinitions()方法后就是真正执行XML文档的解析了,跟进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); if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { return; } } } preProcessXml(root); parseBeanDefinitions(root, this.delegate); postProcessXml(root); this.delegate = parent; }
到这里处理流程就很清晰了,先是对profile进行处理,之后就通过parseBeanDefinitions()方法进行文档的解析操作,跟进parseBeanDefinitions()方法源码:
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; // 下面对bean进行处理 if (delegate.isDefaultNamespace(ele)) { parseDefaultElement(ele, delegate); } else { delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } }
上面if-else语句块中的parseDefaultElement(ele, delegate)和delegate.parseCustomElement(ele)就是对Spring配置文件中的默认命名空间和自定义命名空间进行解析用的。在Spring的XML配置中,默认Bean声明就如前面定义的:
自定义的Bean声明如:
关于在Spring中如何使用BeanFactory进行解析bean就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。