likes
comments
collection
share

Spring IOC源码阅读(三)-Spring IOC容器的初始化过程

作者站长头像
站长
· 阅读数 28

Spring IOC源码阅读(三)-Spring IOC容器的初始化过程

前言

  1. 获取BeanDefinition的Resource。
  2. 从Resource中加载解析BeanDefinition。
  3. 将得到的BeanDefinition注册到Spring IOC容器中。
  4. 调用refresh()
  1. 使用DefaultListableBeanFactory作为Spring IOC容器。
  2. 使用AnnotationConfigApplicationContext作为Spring IOC容器。

接下来将从AnnotationConfigApplicationContext源码阐述Spring IOC容器的初始化的四个步骤。

Spring IOC容器初始化过程详解

AnnotationConfigApplicationContext使用示例:

public class AppStarter {
    public static void main(String[] args) {
        // 实例化一个spring IOC容器,将包名作为构造参数
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext("ioc.container.demo");
        // 获取Bean
        MyTestBean myTestBean = applicationContext.getBean(MyTestBean.class);
        // 调用对象的方法
        myTestBean.print();
    }
}

AnnotationConfigApplicationContext构造过程:

public AnnotationConfigApplicationContext(String... basePackages) {
	this();
	scan(basePackages);
	refresh();
}

public AnnotationConfigApplicationContext() {
	this.reader = new AnnotatedBeanDefinitionReader(this);
	this.scanner = new ClassPathBeanDefinitionScanner(this);
}

public void scan(String... basePackages) {
	Assert.notEmpty(basePackages, "At least one base package must be specified");
	this.scanner.scan(basePackages);
}
  1. 执行this()无参构造函数,先后初始化AnnotatedBeanDefinitionReader、ClassPathBeanDefinitionScanner对象。
  2. 将传入的包名作为参数调用内部的scan(),其内部调用的ClassPathBeanDefinitionScanner的scan()。
  3. 调用内部的refresh()方法

ClassPathBeanDefinitionScanner的scan()执行过程:

public int scan(String... basePackages) {
    int beanCountAtScanStart = this.registry.getBeanDefinitionCount();

    doScan(basePackages);

    // Register annotation config processors, if necessary.
    if (this.includeAnnotationConfig) {
        AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
    }

    return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Assert.notEmpty(basePackages, "At least one base package must be specified");
    Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
    for (String basePackage : basePackages) {
        Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
        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;
}

  1. scan(String... basePackages)主要逻辑是调用内部的doScan(String... basePackages),得到一个BeanDefinition的集合
  2. doScan(String... basePackages)内部逻辑是
    1. 18行,findCandidateComponents(),找到包下面定义的BeanDefinition,内部处理逻辑是
      1. 先通过ResourceLoader的实现类,加载包名下所有的Resource
      2. 将Resource封装成BeanDefinition的实现类ScannedGenericBeanDefinition对象
    2. 34行,registerBeanDefinition(),借助this.registry对象将BeanDefinition注册到容器中

获取BeanDefinition的Resource

Spring IOC源码阅读(三)-Spring IOC容器的初始化过程 前面提到findCandidateComponents()第一步逻辑是:通过ResourceLoader的实现类,加载包名下所有的Resource。详细过程是:

  1. 调用scanCandidateComponents(),在方法内部先调用getResourcePatternResolver()的到: PathMatchingResourcePatternResolver对象,他是ResourceLoader的实现类。
  2. 再调用PathMatchingResourcePatternResolver对象的getResources()获取Resource的数组。
  3. getResources()内部调用findPathMatchingResources(),该方法中定义了3中获取资源的方式,根据传入参数判断使用哪种方式。
    1. 通过jar的方式,对应方法doFindPathMatchingJarResources()
    2. 通过文件系统的方式,对应方法doFindPathMatchingFileResources()
    3. 通过VFS的方式,VfsResourceMatchingDelegate.findMatchingResources()
  4. 由于我们传入的是包名,所以使用文件系统的方式,即执行doFindPathMatchingFileResources(),其内部逻辑是调用doRetrieveMatchingFiles()
  5. doRetrieveMatchingFiles()是一个递归方法,递归获取传入包名下编译之后所有的class文件(注意不是java文件),然后通过new FileSystemResource的方式将文件封装成Resource。

下面贴出一部分关键的源码 : ClassPathBeanDefinitionScanner#scanCandidateComponents:

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<>();
try {
    String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
    resolveBasePackage(basePackage) + '/' + this.resourcePattern;
    Resource[] resources = getResourcePatternResolver().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 = getMetadataReaderFactory().getMetadataReader(resource);
                if (isCandidateComponent(metadataReader)) {
                    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;
}

:PathMatchingResourcePatternResolver#findPathMatchingResources

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<>(16);
    for (Resource rootDirResource : rootDirResources) {
        rootDirResource = resolveRootDirResource(rootDirResource);
        URL rootDirUrl = rootDirResource.getURL();
        if (equinoxResolveMethod != null && rootDirUrl.getProtocol().startsWith("bundle")) {
            URL resolvedUrl = (URL) ReflectionUtils.invokeMethod(equinoxResolveMethod, null, rootDirUrl);
            if (resolvedUrl != null) {
                rootDirUrl = resolvedUrl;
            }
            rootDirResource = new UrlResource(rootDirUrl);
        }
        if (rootDirUrl.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
            result.addAll(VfsResourceMatchingDelegate.findMatchingResources(rootDirUrl, subPattern, getPathMatcher()));
        }
        else if (ResourceUtils.isJarURL(rootDirUrl) || isJarResource(rootDirResource)) {
            result.addAll(doFindPathMatchingJarResources(rootDirResource, rootDirUrl, subPattern));
        }
        else {
            result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern));
        }
    }
    if (logger.isTraceEnabled()) {
        logger.trace("Resolved location pattern [" + locationPattern + "] to resources " + result);
    }
    return result.toArray(new Resource[0]);
}

PathMatchingResourcePatternResolver#doFindMatchingFileSystemResources()

protected Set<Resource> doFindMatchingFileSystemResources(File rootDir, String subPattern) throws IOException {
    if (logger.isTraceEnabled()) {
        logger.trace("Looking for matching resources in directory tree [" + rootDir.getPath() + "]");
    }
    Set<File> matchingFiles = retrieveMatchingFiles(rootDir, subPattern);
    Set<Resource> result = new LinkedHashSet<>(matchingFiles.size());
    for (File file : matchingFiles) {
        result.add(new FileSystemResource(file));
    }
    return result;
}

PathMatchingResourcePatternResolver#findPathMatchingResources()

protected void doRetrieveMatchingFiles(String fullPattern, File dir, Set<File> result) throws IOException {
    if (logger.isTraceEnabled()) {
        logger.trace("Searching directory [" + dir.getAbsolutePath() +
                     "] for files matching pattern [" + fullPattern + "]");
    }
    for (File content : listDirectory(dir)) {
        String currPath = StringUtils.replace(content.getAbsolutePath(), File.separator, "/");
        if (content.isDirectory() && getPathMatcher().matchStart(fullPattern, currPath + "/")) {
            if (!content.canRead()) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Skipping subdirectory [" + dir.getAbsolutePath() +
                                 "] because the application is not allowed to read the directory");
                }
            }
            else {
                doRetrieveMatchingFiles(fullPattern, content, result);
            }
        }
        if (getPathMatcher().match(fullPattern, currPath)) {
            result.add(content);
        }
    }
}

加载解析BeanDefinition

Spring IOC源码阅读(三)-Spring IOC容器的初始化过程 前面提到findCandidateComponents()第二步逻辑是:需要将Resource封装成BeanDefinition的实现类ScannedGenericBeanDefinition对象。详细过程是:

  1. 遍历Resource,如果Resource可访问,则通过调用getMetadataReaderFacotry()获取MetadataReader的工厂实例。
  2. 调用MetadataReader工厂实例的getMetadataReader()方法,将Resource对象作为参数传入,得到该Resource对象的MetadataReader对象。
  3. 将MetadataReader对象作为参数传入isCandidateComponent()方法,判断是否保含@Component等相关注解,如果包含则将MetadataReader对象作为参数,实例化ScannedGenericBeanDefinition对象。

下面贴出一部分关键的源码: ClassPathBeanDefinitionScanner#isCandidateComponent:

protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
		for (TypeFilter tf : this.excludeFilters) {
			if (tf.match(metadataReader, getMetadataReaderFactory())) {
				return false;
			}
		}
		for (TypeFilter tf : this.includeFilters) {
			if (tf.match(metadataReader, getMetadataReaderFactory())) {
				return isConditionMatch(metadataReader);
			}
		}
		return false;
	}

Spring IOC源码阅读(三)-Spring IOC容器的初始化过程

注册BeanDefinition

Spring IOC源码阅读(三)-Spring IOC容器的初始化过程 前面提到doSacn(),在获取到所有的BeanDefinition之后,通过registerBeanDefinition(),借助this.registry对象将BeanDefinition注册到容器中。详细过程是:

  1. 遍历BeanDefinition的过程中,依次调用ClassPathBeanDefinitionScanner的registerBeanDefinition()
  2. ClassPathBeanDefinitionScanner的registerBeanDefinition()实际上调用的时BeanDefinitionRegistry实现类的registerBeanDefinition()。
  3. 这里AnnotionConfigApplicationContext就是BeanDefinitionRegistry的实现类,所以实际调用的是AnnotionConfigApplicationContext的registerBeanDefinition()。
  4. AnnotionConfigApplicationContext内部持有DefaultListableBeanFactory对象,AnnotionConfigApplicationContext的registerBeanDefinition(),实际调用的DefaultListableBeanFactory的registerBeanDefinition(),因为DefaultListableBeanFactory也是BeanDefinitionRegistry的实现类。最终,将BeanDefinition注册到Spring IOC容器中。

下面贴出一部分关键的源码: DefaultListableBeanFactory#registerBeanDefinition:

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {

    Assert.hasText(beanName, "Bean name must not be empty");
    Assert.notNull(beanDefinition, "BeanDefinition must not be null");

    if (beanDefinition instanceof AbstractBeanDefinition) {
        try {
            ((AbstractBeanDefinition) beanDefinition).validate();
        }
        catch (BeanDefinitionValidationException ex) {
            throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                                                   "Validation of bean definition failed", ex);
        }
    }

    BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
    if (existingDefinition != null) {
        if (!isAllowBeanDefinitionOverriding()) {
            throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
        }
        else if (existingDefinition.getRole() < beanDefinition.getRole()) {
            // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
            if (logger.isInfoEnabled()) {
                logger.info("Overriding user-defined bean definition for bean '" + beanName +
                            "' with a framework-generated bean definition: replacing [" +
                            existingDefinition + "] with [" + beanDefinition + "]");
            }
        }
        else if (!beanDefinition.equals(existingDefinition)) {
            if (logger.isDebugEnabled()) {
                logger.debug("Overriding bean definition for bean '" + beanName +
                             "' with a different definition: replacing [" + existingDefinition +
                             "] with [" + beanDefinition + "]");
            }
        }
        else {
            if (logger.isTraceEnabled()) {
                logger.trace("Overriding bean definition for bean '" + beanName +
                             "' with an equivalent definition: replacing [" + existingDefinition +
                             "] with [" + beanDefinition + "]");
            }
        }
        this.beanDefinitionMap.put(beanName, beanDefinition);
    }
    else {
        if (hasBeanCreationStarted()) {
            // Cannot modify startup-time collection elements anymore (for stable iteration)
            synchronized (this.beanDefinitionMap) {
                this.beanDefinitionMap.put(beanName, beanDefinition);
                List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
                updatedDefinitions.addAll(this.beanDefinitionNames);
                updatedDefinitions.add(beanName);
                this.beanDefinitionNames = updatedDefinitions;
                removeManualSingletonName(beanName);
            }
        }
        else {
            // Still in startup registration phase
            this.beanDefinitionMap.put(beanName, beanDefinition);
            this.beanDefinitionNames.add(beanName);
            removeManualSingletonName(beanName);
        }
        this.frozenBeanDefinitionNames = null;
    }

    if (existingDefinition != null || containsSingleton(beanName)) {
        resetBeanDefinition(beanName);
    }
}

调用refresh()

转载自:https://juejin.cn/post/7375525093266407465
评论
请登录