第十步:
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 */ //創建BeanDefinitionDocumentReader protected 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,用來保存BeanDefinition Set<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
,然后再放進DefaultListableBeanFactory
的beanDefinitionMap
中。