spring的启动过程

Spring的启动过程,就是其IoC容器的启动过程,本质就是创建和初始化bean的工厂(BeanFactory),BeanFactory其实就是整个SpringIoc的核心,Spring 使用 BeanFactory 来实例化、配置和管理 Bean。

对于web程序,IoC容器启动过程即是建立上下文的过程,在web应用中,web容器会提供一个全局的ServletContext上下文环境,ServletContext上下文为Spring IoC提供了一个宿主环境。

Spring应用在Web容器中启动的整个过程如下:
在这里插入图片描述
先介绍下源码中出现的各位小伙伴:

  • Resource : 是spring对于资源的一种抽象,因为资源的来源可能很丰富,利于File,Class Path Resource,Url Resource等,进行统一封装,暴露出getInputStream进行统一读取解析
  • Document : 这个没啥好讲的,XML文档对象
  • EncodedResource :封装了Resource,指定Resource的编码
  • ReaderContext :Bean Definition解析过程中的上下文对象,封装了Resource、ProblemReporter、EventListener、SourceExtractor等
  • Element :XML中的元素节点对象
  • BeanDefinition :这个接口及其实现类非常重要,他描述了XML中一个bean节点及其子节点的所有属性,将xml中的描述转变成内部的field信息,举例:scope,lazyinit,ConstructorArgumentValues(描述构造器),PropertyValues(描述属性值)等,是一个保罗万象的接口,其子类实现包括GenericBeanDefinition、RootBeanDefinition、ChildBeanDefinition等
  • BeanDefinitionHolder : 顾名思义包含了一个BeanDefinition,同时其包含了beanName和aliases,更好的封装了一次
  • BeanDefinitionReader : 定义了读取BeanDefinition的接口,主要作用是从Resource中读取Bean定义,XmlBeanDefinitionReader是其具体的实现类
  • BeanDefinitionDocumentReader :定义了从Document对象中解析BeanDefinition并且注册到Registry中设计到的接口,其默认实现类是DefaultBeanDefinitionDocumentReader,主要是被XmlBeanDefinitionReader委派去处理Document对象
  • BeanDefinitionParserDelegate:是用于最终处理XML bean定义的人,它做的可都是脏活累活,import/alias/bean等element以及element的子节点以及属性都是它解析并且填充到BeanDefinition中然后使用ReaderContext中的Registry(实际就是DefaultListableBeanFactory)来将该BeanDefinition注册

在Web项目使用Spring,是通过在web.xml里面配置:

org.springframework.web.context.ContextLoaderListener 来初始化IOC容器的。

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

确切的说,ContextLoaderListener这个监听对象,监听的是ServletContext这个,当web容器初始化,ServletContext发生变化的时候,会触发相应的事件。

public class ContextLoaderListener extends ContextLoader implements ServletContextListener

ContextLoaderListener继承了ContextLoader,并实现了ServletContextListener接口,在web容器初始化的时候,会触发ServletContextListener接口中的contextInitialized()方法,同理,在容器关闭的时候,会触发对应的contextDestroyed()方法。

在这里插入图片描述
其中,initWebApplicationContext()方法为父类ContextLoader中的方法:

public WebApplicationContext initWebApplicationContext(ServletContext servletContext)

由于initWebApplicationContext()方法源码较长,我们进行拆分阅读。

if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
	throw new IllegalStateException(
			"Cannot initialize context because there is already a root application context present - " +
			"check whether you have multiple ContextLoader* definitions in your web.xml!");
}
//日志记录
Log logger = LogFactory.getLog(ContextLoader.class);
servletContext.log("Initializing Spring root WebApplicationContext");
if (logger.isInfoEnabled()) {
	logger.info("Root WebApplicationContext: initialization started");
}
//启动startTime,记录启动耗时
long startTime = System.currentTimeMillis();

首先判断是否创建了WebApplicationContext,正常情况下创建了一个WebApplicationContext后,会把context set到ServletContext中,setAttribute的本质其实是往LinkedHashMap中set值。

servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

如果从servletContext中根据key获得了WebApplicationContext对象,则表示之前已经创建过了根上下文WebApplicationContext,此时抛出异常,提示检查web.xml中的配置,避免重复创建root上下文,保证只有一个Spring容器

再看下面的代码:

try {
	// Store context in local instance variable, to guarantee that
	// it is available on ServletContext shutdown.
	if (this.context == null) {
		this.context = createWebApplicationContext(servletContext);
	}
	if (this.context instanceof ConfigurableWebApplicationContext) {
		ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
		if (!cwac.isActive()) {
			// The context has not yet been refreshed -> provide services such as
			// setting the parent context, setting the application context id, etc
			if (cwac.getParent() == null) {
				// The context instance was injected without an explicit parent ->
				// determine parent for root web application context, if any.
				ApplicationContext parent = loadParentContext(servletContext);
				cwac.setParent(parent);
			}
			configureAndRefreshWebApplicationContext(cwac, servletContext);
		}
	}
	servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
	ClassLoader ccl = Thread.currentThread().getContextClassLoader();
	if (ccl == ContextLoader.class.getClassLoader()) {
		currentContext = this.context;
	}
	else if (ccl != null) {
		currentContextPerThread.put(ccl, this.context);
	}
	if (logger.isDebugEnabled()) {
		logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
				WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
	}
	if (logger.isInfoEnabled()) {
		long elapsedTime = System.currentTimeMillis() - startTime;
		logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
	}
	return this.context;
}

代码片段较长,一步一步来,try中有俩关键的地方:

createWebApplicationContext()configureAndRefreshWebApplicationContext()

先逐步往下看

首先是判断 this.context == null,通过createWebApplicationContext方法创建一个

WebApplicationContext,具体代码如下:

protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
	Class<?> contextClass = determineContextClass(sc);
	if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
		throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
				"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
	}
	return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}

determineContextClass,字面意思为检测Context的class类型,会读取servletContext的初始化参数contextClass,大部分情况下我们不会配置此参数,在未配置的情况下,Spring会去org.springframework.web.context包中的ContextLoader.properties配置文件读取默认配置:
在这里插入图片描述
通过Spring提供的ClassUtil进行反射,反射出XmlWebApplicationContext类,再通过BeanUtils进行反射,调用无参构造器,instance出一个WebApplicationContext并返回ConfigurableWebApplicationContext。

if (this.context instanceof ConfigurableWebApplicationContext) {
	ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
	if (!cwac.isActive()) {
		// The context has not yet been refreshed -> provide services such as
		// setting the parent context, setting the application context id, etc
		if (cwac.getParent() == null) {
			// The context instance was injected without an explicit parent ->
			// determine parent for root web application context, if any.
			ApplicationContext parent = loadParentContext(servletContext);
			cwac.setParent(parent);
		}
		configureAndRefreshWebApplicationContext(cwac, servletContext);
	}
}

获得WebApplicationContext后,下一步判断获得的context是否为

ConfigurableWebApplicationContext的实例,默认的XmlWebApplicationContext满足判断条件。

判断父上下文是否为active状态,如果active为false下,需要判断父上下文是否为null,如果parent上下文为null的情况,则执行loadParentContext()。loadParentContext()方法,是一个默认的是模板实现方法,用于加载/获取ApplicationContext,此context为根WebApplicationContext的父上下文。

下一步,进入configureAndRefreshWebApplicationContext

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc)

将上面创建的XmlWebApplicationContext进行初始化操作。

if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
	// The application context id is still set to its original default value
	// -> assign a more useful id based on available information
	String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
	if (idParam != null) {
		wac.setId(idParam);
	}
	else {
		// Generate default id...
		wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
				ObjectUtils.getDisplayString(sc.getContextPath()));
	}
}

主要创建一个默认id,org.springframework.web.context.WebApplicationContext:+项目的ContextPath

//设置sc到wac中,便于Spring获得ServletContext
wac.setServletContext(sc);
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
	wac.setConfigLocation(configLocationParam);
}
// The wac environment's #initPropertySources will be called in any case when the context
// is refreshed; do it eagerly here to ensure servlet property sources are in place for
// use in any post-processing or initialization that occurs below prior to #refresh
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
	((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
}
customizeContext(sc, wac);
wac.refresh();

创建好id后,把servletContext放入创建好的XmlWebApplicationContext中,便于spring后续获得servletContext上下文。

紧接着会去读取web.xml中contextConfigLocation的配置
在这里插入图片描述
如果在web.xml中未配置,则会去读取WEB-INF下面的applicationContext.xml配置文件,

即XmlWebApplicationContext中的属性 DEFAULT_CONFIG_LOCATION的默认值

/** Default config location for the root context */
public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";

读取到contextConfigLocation相关文件配置路径后,设置到XmlWebApplicationContext中,用于加载指定路径的配置文件。

下一步 customizeContext(sc, wac);

主要用于自定义context相关配置,比如定义bean是否可以循环引用,bean定义是否可以被覆盖等,通常情况下不做任何操作。

最后一步:

wac.refresh();

整个容器启动的最核心方法,在这个refresh()方法中,会完成资源文件的加载、配置文件解析、Bean定义的注册、组件的初始化等核心工作。

先看下refresh()方法的说明。
在这里插入图片描述
refresh()方法是ConfigurableApplicationContext接口提供的方法,refresh()方法的具体实现是由

AbstractApplicationContext这个抽象类实现的。

AbstractApplicationContext这个抽象类继承了DefaultResourceLoader,并且实现了接口

ConfigurableApplicationContext

废话不多,上源码

@Override
public void refresh() throws BeansException, IllegalStateException {
	synchronized (this.startupShutdownMonitor) {
		// Prepare this context for refreshing.
		prepareRefresh();
		// Tell the subclass to refresh the internal bean factory.
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
		// Prepare the bean factory for use in this context.
		prepareBeanFactory(beanFactory);
		try {
			// Allows post-processing of the bean factory in context subclasses.
			postProcessBeanFactory(beanFactory);
			// Invoke factory processors registered as beans in the context.
			invokeBeanFactoryPostProcessors(beanFactory);
			// Register bean processors that intercept bean creation.
			registerBeanPostProcessors(beanFactory);
			// Initialize message source for this context.
			initMessageSource();
			// Initialize event multicaster for this context.
			initApplicationEventMulticaster();
			// Initialize other special beans in specific context subclasses.
			onRefresh();
			// Check for listener beans and register them.
			registerListeners();
			// Instantiate all remaining (non-lazy-init) singletons.
			finishBeanFactoryInitialization(beanFactory);
			// Last step: publish corresponding event.
			finishRefresh();
		}
		catch (BeansException ex) {
			if (logger.isWarnEnabled()) {
				logger.warn("Exception encountered during context initialization - " +
						"cancelling refresh attempt: " + ex);
			}
			// Destroy already created singletons to avoid dangling resources.
			destroyBeans();
			// Reset 'active' flag.
			cancelRefresh(ex);
			// Propagate exception to caller.
			throw ex;
		}
		finally {
			// Reset common introspection caches in Spring's core, since we
			// might not ever need metadata for singleton beans anymore...
			resetCommonCaches();
		}
	}
}

显而易见的,refresh()中是一段同步代码块,this.startupShutdownMonitor对象是一个Object对象,主要作用其实就是 用于“刷新”和“销毁”的同步监视器,换句话说,就是在refresh()和close()两个方法中,都会同步此对象,用来保证统时间只有一个线程执行刷新或者关闭操作。

1. prepareRefresh()
在这里插入图片描述
主要就是一些准备操作,记录context的startup时间,设置closed为false,active为true,其中,设置active为true这一步,对应在ContextLoader中的initWebApplicationContext方法,用于判断当前context对象是否被刷新过。

回到refresh方法中,接下来是obtainFreshBeanFactory()方法。

2.obtainFreshBeanFactory()

// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

obtainFreshBeanFactory()方法是整个refresh()方法的核心,主要做完成BeanFactory的创建,bean配置文件的加载,解析和注册。

来看obtainFreshBeanFactory的实现
在这里插入图片描述

第一步refreshBeanFactory
在这里插入图片描述
顾名思义,刷新beanFactory,在刷新的时候,判断是否已经有beanFactory了,如果有相关beanFactory,则destory所有单利的bean,并且关闭beanFactory,在关闭了原来旧的beanFactory后,再创建一个新的beanFactory,创建了新的beanFactory后,完成加载bean的配置文件,解析bean等一系列相关操作。

先进入hasBeanFactory()
在这里插入图片描述
又是一段同步代码块,有没有很熟悉,又是同步了this.beanFactoryMonitor,用于判断当前是否有已经持有了一个bean factory,并且至少已经被刷新了一次,未被关闭的。

如果返回有bean factory,则需要destory所有单例的bean,因为bean factory只能管理单例的bean,非单例的bean不归bean factory管,所以只destory了所有单利的bean,并且关闭了bean factory。

在关闭了bean factory后,再次创建的factory是 DefaultListableBeanFactory,创建好factory后,设置序列号,个性化设置。

重点来了,

loadBeanDefinitions(factory);

这个方法,主要做了加载bean到factory中

通过XmlBeanDefinitionReader进行加载bean的定义,进入loadBeanDefinitions方法,里面调用的层数很多,大部分都是@Override的方法,主要就是用来加载Spring的配置文件,大部分情况下是多个,加载好配置文件后用于后面的解析。

最后真正执行加载配置文件xml的地方,是在XmlBeanDefinitionReader里的loadBeanDefinitions()方法中
在这里插入图片描述
先通过InputStream读取当前已经被加载的配置文件,读取好后再通过

doLoadBeanDefinitions(InputSource inputSource, Resource resource)

方法进行加载,进入doLoadBeanDefinitions方法
在这里插入图片描述
可以看出,在doLoadBeanDefinitions方法中,try代码块中第一行方法主要做的就是将配置文件加载成Document类型,具体怎么处理的这里先不细说。真正在执行bean加载的,就是第二行的方法。

进入registerBeanDefinitions(doc, resource)方法

在这里插入图片描述
显而易见的,方法中的第三行代码,进行了bean的加载和注册
在这里插入图片描述
继续进入doRegisterBeanDefinitions(root)
在这里插入图片描述
方法里面先是创建了一个BeanDefinitionParserDelegate,具体怎么解析xml的,这里先跳过,我们的目标是知道怎么加载bean的。

parseBeanDefinitions(root, this.delegate)这个方法
在这里插入图片描述
方法上的注释说的很明白:解析document中root level(import,alias,bean标签)的element。

先判断是否为defaultNameSpace,通过判断element的父类Node中的NamespaceURI,是否与创建的

BeanDefinitionParserDelegate中的属性BEANS_NAMESPACE_URI中的值是否相等。

先贴上配置文件和路径,结合配置文件说会比较清楚。
在这里插入图片描述
resources下面两个目录,一个config,一个spring

在启动的时候,debug到上面的parseBeanDefinitions方法中,我们先看看下delegate中读取的是哪个配置文件:
在这里插入图片描述
很明显,先读取了spring文件夹下面的servlet-context.xml配置文件。

再看servlet-context.xml配置文件中的内容。
在这里插入图片描述
首先配置的是配置文件注入的bean,注意上面的注释,在解析的时候也会被读取,但是由于不是Element的实例,所以不会进行处理。

再下一步继续debug
在这里插入图片描述
进入parseDefaultElement(ele,delegate)方法,
在这里插入图片描述
判断delegate中node的name,是否为root level的name标签(import,alias,beans,bean)

由于是ele是bean标签,所以会进入第二个elseif delegate.nodeNameEquals(ele, BEAN_ELEMENT)判断
在这里插入图片描述
再走下一步看:
在这里插入图片描述
方法中的第一行,就是把ele标签中的配置,解析成一个BeanDefinitionHolder

解析完成后,我们可以看到,整个BeanDefinitionHolder对象中,属性beanName已经有值了,对应的就是上面servlet-context.xml中bean标签的id里面的值,beanDefinition中就是bean的所有定义,包括bean的class,是否singleton,是否lazy-init等属性配置。

再往下走,进入try中的registerBeanDefinition这个方法,主要就是注册当前bean到当前的beanfactory中。

继续深入registerBeanDefinition(String beanName, BeanDefinition beanDefinition),代码较长==

@Override
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 oldBeanDefinition;
	oldBeanDefinition = this.beanDefinitionMap.get(beanName);
	if (oldBeanDefinition != null) {
		if (!isAllowBeanDefinitionOverriding()) {
			throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
					"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
					"': There is already [" + oldBeanDefinition + "] bound.");
		}
		else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
			// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
			if (this.logger.isWarnEnabled()) {
				this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
						"' with a framework-generated bean definition: replacing [" +
						oldBeanDefinition + "] with [" + beanDefinition + "]");
			}
		}
		else if (!beanDefinition.equals(oldBeanDefinition)) {
			if (this.logger.isInfoEnabled()) {
				this.logger.info("Overriding bean definition for bean '" + beanName +
						"' with a different definition: replacing [" + oldBeanDefinition +
						"] with [" + beanDefinition + "]");
			}
		}
		else {
			if (this.logger.isDebugEnabled()) {
				this.logger.debug("Overriding bean definition for bean '" + beanName +
						"' with an equivalent definition: replacing [" + oldBeanDefinition +
						"] 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<String>(this.beanDefinitionNames.size() + 1);
				updatedDefinitions.addAll(this.beanDefinitionNames);
				updatedDefinitions.add(beanName);
				this.beanDefinitionNames = updatedDefinitions;
				if (this.manualSingletonNames.contains(beanName)) {
					Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames);
					updatedSingletons.remove(beanName);
					this.manualSingletonNames = updatedSingletons;
				}
			}
		}
		else {
			// Still in startup registration phase
			this.beanDefinitionMap.put(beanName, beanDefinition);
			this.beanDefinitionNames.add(beanName);
			this.manualSingletonNames.remove(beanName);
		}
		this.frozenBeanDefinitionNames = null;
	}
	if (oldBeanDefinition != null || containsSingleton(beanName)) {
		resetBeanDefinition(beanName);
	}
}

看最后几行代码,
在这里插入图片描述
往beanDefinitionMap这个map中放值,key是beanName,value是beanDefinition

往beanDefinitionNames这个arrayList中add beanName,看一下这个beanDefinitionNames的注释:
在这里插入图片描述
根据注册顺序,往list中add name。

add完bean的name和definition后,就返回了,回到parseBeanDefinitions继续解析下一个doc。

继续看配置文件:
在这里插入图片描述
再解析完bean标签后,跳过注释,下一个标签是context:component-scan这个指定扫描包的标签,此标签不是defaultNamespace,所以会跳转到else中的parseCustomElement(ele)方法中。
在这里插入图片描述
进入parseCustomElement(ele)中,
在这里插入图片描述
首先根据当前element元素的namespaceURI,获得对应的handler,其实就是根据不同的xml标签,找出合适的BeanDefinitionParser。

由于我们在xml配置文件中配置了context:component-scan,可以看到,component-scan对应的handler

其实是 org.springframework.context.annotation.ComponentScanBeanDefinitionParser

进入ComponentScanBeanDefinitionParser的parse()方法
在这里插入图片描述
basePackage的值其实就是xml中base-package后面我们填写的包名。

再往下,创建了一个ClassPathBeanDefinitionScanner,注释说的很清楚,扫描bean的定义然后注册他们,注意:这里是类文件而不是类,因为现在这些类还没有被加载,只是ClassLoader能找到这些class的路径而已

感觉离真相越来越近了。。。继续

进入scanner.doScan()看看,是怎么扫描的
在这里插入图片描述
如果basePackages为空,提示至少需要指定一个base-package

进入for循环,

Set<BeanDefinition> candidates = findCandidateComponents(basePackage);

这一句,意思是根据basePackage找到所有的包中的类,当做候选人。

再进去look look里面干了什么
在这里插入图片描述
又是一大坨代码,是不是有点晕啊

一句一句来分析,先看这一句

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

好像不太明白啥意思,首先假设我们在xml中的base-package写的是"com.abc.efg",那么,

packageSearchPath=classpath*:com.abc.efg//.class,意思就是com.abc.efg包下面的所有class,包括子包中的class。如果base-package写的是"",那就是classpath*😗//*.class,就是全部的class了。

获得到packageSearchPath后,再去获得Resources

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

resourcePatternResolver是一个interface,里面的getResources(String locationPattern)方法,这里使用的实现类是PathMatchingResourcePatternResolver,Spring3的版本中可以看到直接声明:
在这里插入图片描述
Spring4.3.6中移除了后面的new实例化。resolver中本质还是通过AntPathMatcher进行匹配。

进入getResources(packageSearchPath);
在这里插入图片描述
首先会判断locationPattern是否以"classpatch:"开头,上面spring已经替我们加了这个前缀, 所以这里符合条件
在这里插入图片描述
下一步进入findPathMatchingResources(),
在这里插入图片描述
假设我们的locationPattern是:classpath
:com/abc/def/**/*.class

determineRootDir(locationPattern)方法做的就是获得根文件夹目录,去掉/**/*.class

则rootDirPath=classpath*:com/abc/def/

subPattern=**/*.class

继续下一步,回到getResources(rootDirPath)方法,此时依旧符合以classpath*开头,但是不满足if判断条件,进入else逻辑:
在这里插入图片描述
继续下一步:
在这里插入图片描述
zzz,进入find方法后,location=“com/abc/def”,

下面的ClassLoader cl = getClassLoader()

说明了Spring也是通过用的ClassLoader加载的class文件。

到此为止,就获得了bean的class文件了,最终spring会将class封装成beanDefinition。

回到doScan中,
在这里插入图片描述
最终,又回到了registerBeanDefinition(definitionHolder, this.registry);就是本文上面说把beanName放入DefaultListableBeanFactory中的beanDefinitionMap里面。

到这一步,spring已经获得了bean的class文件了,具体spring是怎么实例化bean的呢

这里继续跳转到另一篇专门的博客中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值