Spring IOC源码阅读(三)-Spring IOC容器的初始化过程
前言
- 获取BeanDefinition的Resource。
- 从Resource中加载解析BeanDefinition。
- 将得到的BeanDefinition注册到Spring IOC容器中。
- 调用refresh()
- 使用DefaultListableBeanFactory作为Spring IOC容器。
- 使用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);
}
- 执行this()无参构造函数,先后初始化AnnotatedBeanDefinitionReader、ClassPathBeanDefinitionScanner对象。
- 将传入的包名作为参数调用内部的scan(),其内部调用的ClassPathBeanDefinitionScanner的scan()。
- 调用内部的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;
}
- scan(String... basePackages)主要逻辑是调用内部的doScan(String... basePackages),得到一个BeanDefinition的集合
- doScan(String... basePackages)内部逻辑是
- 18行,findCandidateComponents(),找到包下面定义的BeanDefinition,内部处理逻辑是
- 先通过ResourceLoader的实现类,加载包名下所有的Resource
- 将Resource封装成BeanDefinition的实现类ScannedGenericBeanDefinition对象
- 34行,registerBeanDefinition(),借助this.registry对象将BeanDefinition注册到容器中
- 18行,findCandidateComponents(),找到包下面定义的BeanDefinition,内部处理逻辑是
获取BeanDefinition的Resource
前面提到findCandidateComponents()第一步逻辑是:通过ResourceLoader的实现类,加载包名下所有的Resource。详细过程是:
- 调用scanCandidateComponents(),在方法内部先调用getResourcePatternResolver()的到: PathMatchingResourcePatternResolver对象,他是ResourceLoader的实现类。
- 再调用PathMatchingResourcePatternResolver对象的getResources()获取Resource的数组。
- getResources()内部调用findPathMatchingResources(),该方法中定义了3中获取资源的方式,根据传入参数判断使用哪种方式。
- 通过jar的方式,对应方法doFindPathMatchingJarResources()
- 通过文件系统的方式,对应方法doFindPathMatchingFileResources()
- 通过VFS的方式,VfsResourceMatchingDelegate.findMatchingResources()
- 由于我们传入的是包名,所以使用文件系统的方式,即执行doFindPathMatchingFileResources(),其内部逻辑是调用doRetrieveMatchingFiles()
- 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
前面提到findCandidateComponents()第二步逻辑是:需要将Resource封装成BeanDefinition的实现类ScannedGenericBeanDefinition对象。详细过程是:
- 遍历Resource,如果Resource可访问,则通过调用getMetadataReaderFacotry()获取MetadataReader的工厂实例。
- 调用MetadataReader工厂实例的getMetadataReader()方法,将Resource对象作为参数传入,得到该Resource对象的MetadataReader对象。
- 将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;
}
注册BeanDefinition
前面提到doSacn(),在获取到所有的BeanDefinition之后,通过registerBeanDefinition(),借助this.registry对象将BeanDefinition注册到容器中。详细过程是:
- 遍历BeanDefinition的过程中,依次调用ClassPathBeanDefinitionScanner的registerBeanDefinition()
- ClassPathBeanDefinitionScanner的registerBeanDefinition()实际上调用的时BeanDefinitionRegistry实现类的registerBeanDefinition()。
- 这里AnnotionConfigApplicationContext就是BeanDefinitionRegistry的实现类,所以实际调用的是AnnotionConfigApplicationContext的registerBeanDefinition()。
- 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