第十步:

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {/*** Load bean definitions from the specified XML file.* @param resource the resource descriptor for the XML file* @return the number of bean definitions found* @throws BeanDefinitionStoreException in case of loading or parsing errors*///这里是调用的入口public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {return loadBeanDefinitions(new EncodedResource(resource));}/*** Load bean definitions from the specified XML file.* @param encodedResource the resource descriptor for the XML file,* allowing to specify an encoding to use for parsing the file* @return the number of bean definitions found* @throws BeanDefinitionStoreException in case of loading or parsing errors*///这里是载入XML形式的BeanDefinition的地方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());}Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();if (currentResources == null) {currentResources = new HashSet<EncodedResource>(4);this.resourcesCurrentlyBeingLoaded.set(currentResources);}if (!currentResources.add(encodedResource)) {throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");}try {//这里得到XML文件,并得到IO的InputStream准备进行读取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();}}}...
}

第十一步:

    /*** Actually load bean definitions from the specified XML file.* @param inputSource the SAX InputSource to read from* @param resource the resource descriptor for the XML file* @return the number of bean definitions found* @throws BeanDefinitionStoreException in case of loading or parsing errors*///具体的读取过程。这是从特定的XML文件中实际载入的BeanDefinition的地方protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)throws BeanDefinitionStoreException {try {int validationMode = getValidationModeForResource(resource);//取得XML文件的Document对象,这个解析过程由DocumentLoader完成,这个DocumentLoader是DefaultDocumentLoader,在定义DocumentLoader的地方创建Document doc = this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());//启动对BeanDefinition解析的详细过程,这个解析会使用到Spring的Bean配置规则。return registerBeanDefinitions(doc, resource);}catch (BeanDefinitionStoreException ex) {throw ex;}catch (SAXParseException ex) {throw new XmlBeanDefinitionStoreException(resource.getDescription(),"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);}catch (SAXException ex) {throw new XmlBeanDefinitionStoreException(resource.getDescription(),"XML document from " + resource + " is invalid", ex);}catch (ParserConfigurationException ex) {throw new BeanDefinitionStoreException(resource.getDescription(),"Parser configuration exception parsing XML from " + resource, ex);}catch (IOException ex) {throw new BeanDefinitionStoreException(resource.getDescription(),"IOException parsing XML document from " + resource, ex);}catch (Throwable ex) {throw new BeanDefinitionStoreException(resource.getDescription(),"Unexpected exception parsing XML document from " + resource, ex);}}

第十二步:

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {//这里得到BeanDefinitionDocumentReader来对XML的BeanDefinition进行解析BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();documentReader.setEnvironment(this.getEnvironment());//根据通用的XML进行解析,并没有根据Spring的Bean规则int countBefore = getRegistry().getBeanDefinitionCount();//具体的解析过程在这个registerBeanDefinitions中完成,这里是按照Spring的Bean规则进行解析
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));//总的Bean减去通用的,剩下就是spring的bean规则的bean个数return getRegistry().getBeanDefinitionCount() - countBefore;}/*** Create the {@link BeanDefinitionDocumentReader} to use for actually* reading bean definitions from an XML document.* <p>The default implementation instantiates the specified "documentReaderClass".* @see #setDocumentReaderClass*///创建BeanDefinitionDocumentReaderprotected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));}}

第十三步:

public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {/*** {@inheritDoc}* <p>This implementation parses bean definitions according to the "spring-beans" XSD* (or DTD, historically).* <p>Opens a DOM Document; then initializes the default settings* specified at the {@code <beans/>} level; then parses the contained bean definitions.*///根据spring的bean规则解析bean的定义public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {this.readerContext = readerContext;logger.debug("Loading bean definitions");Element root = doc.getDocumentElement();doRegisterBeanDefinitions(root);}protected void doRegisterBeanDefinitions(Element root) {String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);if (StringUtils.hasText(profileSpec)) {Assert.state(this.environment != null, "environment property must not be null");String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);if (!this.environment.acceptsProfiles(specifiedProfiles)) {return;}}BeanDefinitionParserDelegate parent = this.delegate;this.delegate = createHelper(readerContext, root, parent);preProcessXml(root);//委派给delegate解析parseBeanDefinitions(root, this.delegate);postProcessXml(root);this.delegate = parent;}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);}}...}

第十四步:

public class BeanDefinitionParserDelegate {//在这里面定义了大量的bean的属性public BeanDefinition parseCustomElement(Element ele) {return parseCustomElement(ele, null);}public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {String namespaceUri = getNamespaceURI(ele);NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);if (handler == null) {error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);return null;}return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));}...
}

第十五步:

public abstract class NamespaceHandlerSupport implements NamespaceHandler {public BeanDefinition parse(Element element, ParserContext parserContext) {//这里就是解析Bean得到BeanDefinition的地方了return findParserForElement(element, parserContext).parse(element, parserContext);}/*** Locates the {@link BeanDefinitionParser} from the register implementations using* the local name of the supplied {@link Element}.*/private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {String localName = parserContext.getDelegate().getLocalName(element);BeanDefinitionParser parser = this.parsers.get(localName);if (parser == null) {parserContext.getReaderContext().fatal("Cannot locate BeanDefinitionParser for element [" + localName + "]", element);}return parser;}...}

在上面的BeanDefinitionParser中的parse,有很多种BeanDefinitionParser对其进行解析,BeanDefinitionParser接口就定义了一个BeanDefinition parse(Element element, ParserContext parserContext);专业用来解析bean的,具体的实现交给他的具体。结构如下图:

我们来看下这个ComponentScanBeanDefinitionParser

public class ComponentScanBeanDefinitionParser implements BeanDefinitionParser {//这两个是我们常用的属性配置 当然还有别的private static final String ANNOTATION_CONFIG_ATTRIBUTE = "annotation-config";private static final String BASE_PACKAGE_ATTRIBUTE = "base-package";...//这里就是他解析的地方了public BeanDefinition parse(Element element, ParserContext parserContext) {String[] basePackages = StringUtils.tokenizeToStringArray(element.getAttribute(BASE_PACKAGE_ATTRIBUTE),ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);// Actually scan for bean definitions and register them.ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);//得到ClassPathBeanDefinitionScanner,通过它去扫描包中的类文件,注意:这里是类文件而不是类,因为现在这些类还没有被加载,只是ClassLoader能找到这些class的路径而已。Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);registerComponents(parserContext.getReaderContext(), beanDefinitions, element);return null;}...}

第十六步:再来看看ClassPathBeanDefinitionScanner的doScan方法

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {Assert.notEmpty(basePackages, "At least one base package must be specified");//用来保存BeanDefinitionHolder,即Bean的属性Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();for (String basePackage : basePackages) {//得到扫描出来的类Set<BeanDefinition> candidates = findCandidateComponents(basePackage);//得到扫描出来的类后,把他加进beanDefinitions中for (BeanDefinition candidate : candidates) {ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);candidate.setScope(scopeMetadata.getScopeName());String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);if (candidate instanceof AbstractBeanDefinition) {postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);}if (candidate instanceof AnnotatedBeanDefinition) {AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);}if (checkCandidate(beanName, candidate)) {BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);beanDefinitions.add(definitionHolder);registerBeanDefinition(definitionHolder, this.registry);}}}return beanDefinitions;
}

第十七:ClassPathScanningCandidateComponentProvider的findCandidateComponents方法

public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();public Set<BeanDefinition> findCandidateComponents(String basePackage) {//创建一个candidates,用来保存BeanDefinitionSet<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();try {String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +resolveBasePackage(basePackage) + "/" + this.resourcePattern;Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);boolean traceEnabled = logger.isTraceEnabled();boolean debugEnabled = logger.isDebugEnabled();for (Resource resource : resources) {if (traceEnabled) {logger.trace("Scanning " + resource);}if (resource.isReadable()) {try {MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);if (isCandidateComponent(metadataReader)) {//封装一个ScannedGenericBeanDefinition,并设置属性,然后添加进candidates中。ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);sbd.setResource(resource);sbd.setSource(resource);if (isCandidateComponent(sbd)) {if (debugEnabled) {logger.debug("Identified candidate component class: " + resource);}candidates.add(sbd);}else {if (debugEnabled) {logger.debug("Ignored because not a concrete top-level class: " + resource);}}}else {if (traceEnabled) {logger.trace("Ignored because not matching any filter: " + resource);}}}catch (Throwable ex) {throw new BeanDefinitionStoreException("Failed to read candidate component class: " + resource, ex);}}else {if (traceEnabled) {logger.trace("Ignored because not readable: " + resource);}}}}catch (IOException ex) {throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);}return candidates;}}

来看这两句:

String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + "/" + this.resourcePattern;

假设我们配置的需要扫描的包名为com.cengle.service,那么packageSearchPath的值就是classpath*:com.cengle.service/**/*.class,意思就是com.cengle.service包(包括子包)下所有class文件;如果配置的是*,那么packageSearchPath的值就是classpath*:*/**/*.class。这里的表达式是Spring自己定义的。Spring会根据这种表达式找出相关的class文件。

Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);

这些资源是怎么得到的。看下面:

    public Resource[] getResources(String locationPattern) throws IOException {Assert.notNull(locationPattern, "Location pattern must not be null");//判断是否以classpath*:开头if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {// a class path resource (multiple resources for same name possible)//判断多个资源是否有相同的名称if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {// a class path resource pattern//得到类路径的资源模式return findPathMatchingResources(locationPattern);}else {// all class path resources with the given name//得到所有的类路径资源名字return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));}}else {// Only look for a pattern after a prefix here// (to not get fooled by a pattern symbol in a strange prefix).int prefixEnd = locationPattern.indexOf(":") + 1;if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {// a file pattern//得到文件模式return findPathMatchingResources(locationPattern);}else {// a single resource with the given name//得到一个给定名称的资源return new Resource[] {getResourceLoader().getResource(locationPattern)};}}}/*** Find all class location resources with the given location via the ClassLoader.* @param location the absolute path within the classpath* @return the result as Resource array* @throws IOException in case of I/O errors* @see java.lang.ClassLoader#getResources* @see #convertClassLoaderURL*/protected Resource[] findAllClassPathResources(String location) throws IOException {String path = location;if (path.startsWith("/")) {path = path.substring(1);}//根据路径,将资源存进一个迭代器中Enumeration<URL> resourceUrls = getClassLoader().getResources(path);Set<Resource> result = new LinkedHashSet<Resource>(16);while (resourceUrls.hasMoreElements()) {URL url = resourceUrls.nextElement();//将迭代器的元素存进set集合中
            result.add(convertClassLoaderURL(url));}//将集合转化为数组return result.toArray(new Resource[result.size()]);}protected Resource[] findPathMatchingResources(String locationPattern) throws IOException {String rootDirPath = determineRootDir(locationPattern);String subPattern = locationPattern.substring(rootDirPath.length());//根据路径,得到一个资源数组Resource[] rootDirResources = getResources(rootDirPath);Set<Resource> result = new LinkedHashSet<Resource>(16);//循环数组,将数组元素放进一个set集合中。for (Resource rootDirResource : rootDirResources) {rootDirResource = resolveRootDirResource(rootDirResource);if (isJarResource(rootDirResource)) {result.addAll(doFindPathMatchingJarResources(rootDirResource, subPattern));}else if (rootDirResource.getURL().getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {result.addAll(VfsResourceMatchingDelegate.findMatchingResources(rootDirResource, subPattern, getPathMatcher()));}else {result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern));}}if (logger.isDebugEnabled()) {logger.debug("Resolved location pattern [" + locationPattern + "] to resources " + result);}//将集合转化为数组return result.toArray(new Resource[result.size()]);}

Spring也是用的ClassLoader加载的class文件。一路追踪,原始的ClassLoader是Thread.currentThread().getContextClassLoader();。到此为止,就拿到class文件了。 
Spring会将class信息封装成BeanDefinition,然后再放进DefaultListableBeanFactorybeanDefinitionMap中。

转载于:https://www.cnblogs.com/hjy9420/p/4218140.html

spring容器启动的加载过程(三)相关推荐

  1. 面试官:讲讲Spring框架Bean的加载过程

    spring作为目前我们开发的基础框架,每天的开发工作基本和他形影不离,作为管理bean的最经典.优秀的框架,它的复杂程度往往令人望而却步. 不过作为朝夕相处的框架,我们必须得明白一个问题就是spri ...

  2. Spring component-scan类扫描加载过程

    2019独角兽企业重金招聘Python工程师标准>>> https://github.com/javahongxi 有朋友最近问到了spring加载类的过程,尤其是基于annotat ...

  3. spring容器的懒加载

    默认情况下,spring的IOC容器中lazy-init是false的,即没有打开懒加载模式. 如果你没有看到这个lazy-init 的参数设置就说明是false啦. 那么什么是懒加载? 懒加载--- ...

  4. spring容器的懒加载lazy-init设置

    默认情况下,spring的IOC容器中lazy-init是false的,即没有打开懒加载模式. 如果你没有看到这个lazy-init 的参数设置就说明是false啦. 那么什么是懒加载? 懒加载--- ...

  5. Spring容器和Bean加载

    Spring容器的IOC和DI概念 IOC(控制反转):对于组件的控制权进行了转移,传统的程序设计是由客户端new出对象,是程序主动创建所依赖的对象.而IOC是专门将对象的创建交给容器处理,组件的控制 ...

  6. Spring mvc 启动配置文件加载两遍问题

    问题描述 在使用spring mvc 启动的时候,用到了一个在程序启动时加载的配置方法init-method="initLoad",并启动多线程来做数据同步,但是在程序启动之后发现 ...

  7. JVM之类的加载过程(三):初始化——>Initialization

    本篇主要讲解类的初始化,也就是上图红框的部分. 类的初始化都做了哪些事呢? 一.初始化: 1.初始化阶段就是执行类构造器方法<clinit>() 的过程. 2.此方法不需要定义,是java ...

  8. 类的加载过程三:Initialization

    package com.leon.java;public class ClassInitTest {private static int num = 1;static{num = 2;number = ...

  9. Spring框架—SpringBean加载过程

    原文作者:RunAlgorithm 原文地址:图文并茂,揭秘 Spring 的 Bean 的加载过程 1. 概述 Spring 作为 Ioc 框架,实现了依赖注入,由一个中心化的 Bean 工厂来负责 ...

  10. Spring : Bean延时加载注解(@Lazy)

    1.美图 2.概述 @Lazy:用于标识Bean是否需要延迟加载,延时加载就是在第一次使用的时候才加载.@Lazy的主要作用就是用来减少Spring IOC容器启动的加载时间. 3.源码 @Targe ...

最新文章

  1. ng build --prod --aot打包Angluar4项目报javaScript heap out of memory,内存溢出
  2. 基于VS的连连看小游戏
  3. ASP.NET 使用Ajax
  4. 消息队列的使用场景和使用技巧
  5. 问答| car-like robot为何需要设置多个坐标系?
  6. 在k8s中将文件通过configmap添加为pod的文件
  7. 【ROS学习笔记】(八)服务数据的定义与使用
  8. chrome 模拟点击_详解爬虫模拟登陆的三种方法
  9. 软件研发管理:置身其中看问题
  10. TFTP 简单文件传输协议 简介
  11. JavaScript截取文件扩展名
  12. zzzfun网站连接不上服务器,zzzfun网页版
  13. Flixel Dame 坦克大战(三)完成基本逻辑
  14. 深入了解物联网,这几个物联网技术了解吗?
  15. Qt之九宫格图片处理
  16. 2023美赛思路2023美国大学生数学建模竞赛思路
  17. 保研面试/考研复试:英语口语面试必备话题及常用句型句式整理(一)
  18. 中国厨房更净一步:一场科技“下凡”带来的方太式浪漫
  19. 模糊神经网络-PID Smith预估集成控制系统
  20. 【渝粤教育】广东开放大学 客户服务管理 形成性考核 (39)

热门文章

  1. H5 设备运动事件 DeviceMotionEvent
  2. jq向php文件传json,jQuery向后台传入json格式数据的方法
  3. 手把手编写自己的PHP MVC框架实例教程
  4. 如何获取一个需要登录页面的HTML代码
  5. Spring时间(Date)类型转换+自定义
  6. java连接数据库(sqlserver和mysql)
  7. LOJ2392 JOISC2017 烟花棒 二分、贪心
  8. 有效利用1 on 1
  9. tomcat无法正常shutdown
  10. 单调队列(数列中长度不超过k的子序列和的最值)