博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
spring容器启动的加载过程(三)
阅读量:5171 次
发布时间:2019-06-13

本文共 20840 字,大约阅读时间需要 69 分钟。

第十步:

 

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
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 { //这里得到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. *

The default implementation instantiates the specified "documentReaderClass". * @see #setDocumentReaderClass */ //创建BeanDefinitionDocumentReader protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() { return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass)); } }

 

第十三步:

public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {    /**     * {
@inheritDoc} *

This implementation parses bean definitions according to the "spring-beans" XSD * (or DTD, historically). *

Opens a DOM Document; then initializes the default settings * specified at the {

@code
} 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
beanDefinitions = scanner.doScan(basePackages); registerComponents(parserContext.getReaderContext(), beanDefinitions, element); return null; } ... }

 

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

protected Set
doScan(String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified"); //用来保存BeanDefinitionHolder,即Bean的属性 Set
beanDefinitions = new LinkedHashSet
(); for (String basePackage : basePackages) { //得到扫描出来的类 Set
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
findCandidateComponents(String basePackage) { //创建一个candidates,用来保存BeanDefinition Set
candidates = new LinkedHashSet
(); 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
resourceUrls = getClassLoader().getResources(path); Set
result = new LinkedHashSet
(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
result = new LinkedHashSet
(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

你可能感兴趣的文章
[React] Style the body element with styled-components and "injectGlobal"
查看>>
[Angular2 Router] CanDeactivate Route Guard - How To Confirm If The User Wants To Exit A Route
查看>>
[Grunt] Cleaning your build folder with grunt-contrib-clean
查看>>
discuz!之XML解析错误:废弃 document 元素之后的内容错误
查看>>
jvm类加载机制
查看>>
洛谷P1976 鸡蛋饼
查看>>
HDU 5236 Article 期望
查看>>
hadoop操作
查看>>
架构1
查看>>
第2章 数字之魅——数组循环移位
查看>>
关于CoreData的用法
查看>>
python 结巴分词
查看>>
Eclipse插件手动安装
查看>>
iOS开发肯定会遇到的
查看>>
mysqlslap 一个MySQL数据库压力测试工具
查看>>
python操作MySQL数据库
查看>>
Java中i++的陷阱
查看>>
Python 一些好玩的函数
查看>>
MFC对话框中文出现乱码的解决方法
查看>>
ListView之SimpleAdapter的使用
查看>>