本系列文章代码基于Spring Framework 5.2.x
上一篇文章Spring Ioc源码分析系列--Ioc的基础知识准备介绍了Ioc的基础概念以及Spring Ioc体系的部分基础知识。那么这一篇就会真正通过一个例子,启动Ioc容器,获取容器里的bean
。
首先说明,本文的例子是基于xml
配置文件去完成的。
为什么是xml
?因为xml
是Spring的灵魂,可能我们初学Spring都会有畏难情绪,看到繁杂的xml
就会打退堂鼓。但是实际上不然,xml
的格式是相当清晰的,一个配置文件可以说没有一行配置是多余的。现在大部分的配置是用注解去完成的,相比xml
而言是简洁许多,但是对于我们初学而言,xml
其实是更好的方式,xml
相对于注解而言是相对繁杂,但是它的信息也更多更明确,注解只是添加了一个注解就完成配置,细节上是更为隐蔽的。再加上xml
配置文件和注解配置的原理是相通的,核心思想是一样的,掌握核心就万变不离其宗。所以这系列文章的例子大部分都会采取xml
的方式去配置,当然后续可能也会补充一下注解方式的例子和分析文章。
万事开头难,如果实在觉得看不懂但又想学的,可以硬着头皮看下去,等以后回过头来再看的时候,会有豁然开朗的感觉。
废话少说,下面开始搞个例子分析一下。所有源码都在我的仓库ioc-sourcecode-analysis-code-demo里找到。
首先弄个实体类User
/** * @author Codegitz * @date 2022/4/26 10:58 **/ public class User { private String id; private String name; private String age; }
创建业务类UserService
以及业务实现类UserServiceImpl
,这里的逻辑很简单,就是根据传入的name
和age
返回一个新的user
对象。
/** * @author Codegitz * @date 2022/4/26 10:59 **/ public interface UserService { User getUser(String name,String age); } public class UserServiceImpl implements UserService { @Override public User getUser(String name, String age) { User user = new User(); user.setId("1"); user.setName(name); user.setAge(age); return user; } }
业务类的准备工作已经完成了,接下来就是要写个xml配置文件,告诉Spring Ioc我需要一个UserService
的bean
。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="userService" class="io.codegitz.service.impl.UserServiceImpl"/> </beans>
这个xml配置文件比较简单,我们来解释一下每一行是什么意思。
<?xml version="1.0" encoding="UTF-8"?>
为xml文件的规定头,没啥好说的。
xmlns="http://www.springframework.org/schema/beans"
表明了xml的命名空间,xmlns
全称为xml namespace
,一个xml里面命名空间可以有多个。
xmlns:xsi
和 xsi:schemaLocation
是指明了xml文件的验证模型和验证模型文件的位置。可以看到验证模型能通过http
方式获取,但是为了防止网络抖动的影响,一般会在本地存放验证文件。spring-beans.xsd
就可以在图示的目录下找到。
接下来就到了<bean id="userService" class="io.codegitz.service.impl.UserServiceImpl"/>
这一行,这是我们获取一个Bean的关键配置,这里告诉Ioc我需要一个id
为userService
,class
为io.codegitz.service.impl.UserServiceImpl
的Bean。
到这里配置已经完成了,接下来创建一个引导类启动Ioc就能获取到这个bean了。
新建引导类Application
,通过ClassPathXmlApplicationContext
类引导启动Ioc容器,这是加载xml
配置的常用引导类。
/** * @author Codegitz * @date 2022/4/26 10:57 **/ public class Application { public static void main(String[] args) { ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml"); UserService userService = (UserService) applicationContext.getBean("userService"); User codegitz = userService.getUser("codegitz", "25"); System.out.println(codegitz); } }
到这里其实已经完成了所有样例的准备,可以启动容器了。
可以看到,只是简单的ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
就可以启动一个容器,随后就能通过getBean()
方法获取容器里的bean了,接下来我们看看new ClassPathXmlApplicationContext("beans.xml")
发生了什么。
这里我们使用的是ClassPathXmlApplicationContext
,那么就从这个类的构造方法开始分析。
摘取的代码片段我会保留原文注释,看官可以细细品味。
/** * Create a new ClassPathXmlApplicationContext, loading the definitions * from the given XML file and automatically refreshing the context. * @param configLocation resource location * @throws BeansException if context creation failed */ public ClassPathXmlApplicationContext(String configLocation) throws BeansException { this(new String[] {configLocation}, true, null); }
继续跟进构造函数,根据方法上的注释和代码可以看到。这里就做了两件事,根据给定的xml配置文件加载BeanDefinition,然后调用refresh()
方法刷新容器。
/** * Create a new ClassPathXmlApplicationContext with the given parent, * loading the definitions from the given XML files. * @param configLocations array of resource locations * @param refresh whether to automatically refresh the context, * loading all bean definitions and creating all singletons. * Alternatively, call refresh manually after further configuring the context. * @param parent the parent context * @throws BeansException if context creation failed * @see #refresh() */ public ClassPathXmlApplicationContext( String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException { // 初始化父类 super(parent); // 设置配置文件信息,这里会进行占位符的替换 // 例如/${env}-beans.xml类型的路径会被占位符替换,如果env=sit,那么路径就会被替换为/sit-beans.xml setConfigLocations(configLocations); // 如果没有刷新,就会调用refresh()方法进行刷新,这个方法是整个Ioc的关键入口,由父类AbstractApplicationContext实现 if (refresh) { refresh(); } }
前面两个方法比较简单,可以跟着注释看一下。重点在refresh()
方法。这个方法会完成Ioc所需要的所有配置加载,完成所有bean的注册以及Spring的一系列实现。
到这里,才是真正摸到了Ioc的源码入口。看一下refresh()
方法的代码。
public void refresh() throws BeansException, IllegalStateException { //给容器refresh加锁,避免容器在refresh阶段时,容器进行了初始化或者销毁操作 synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. //调用容器准备刷新的方法,获取容器当前时间,同时给容器设置同步标识、具体方法 prepareRefresh(); // Tell the subclass to refresh the internal bean factory. /** * 告诉子类启动refreshBeanFactory方法,refreshBeanFactory方法会载入bean的定义文件,该方法的实现会针对xml配置,最终创建内部容器 * 该容器负责bean的创建与管理,会进行BeanDefinition的注册 */ ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. //注册一些容器中需要使用的系统bean,例如classloader,BeanFactoryPostProcessor等 prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses // 允许容器的子类去注册PostProcessor,钩子方法. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. //激活容器中注册为bean的BeanFactoryPostProcessor //对于注解容器,org.springframework.context.annotation.ConfigurationClassPostProcessor 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(); } } }
咋一看代码量非常大,但是逻辑是比较清晰的,结合注释来看,再调试一遍,没啥大问题。
下面对方法进行逐个分析,鉴于一次性写完会比较长,本文先分析前三个方法,即prepareRefresh()
、obtainFreshBeanFactory()
和prepareBeanFactory(beanFactory)
三个方法。
跟进prepareRefresh()
方法,这个方法主要是做了一些初始化属性设置和校验。
/** * Prepare this context for refreshing, setting its startup date and * active flag as well as performing any initialization of property sources. */ protected void prepareRefresh() { // Switch to active. this.startupDate = System.currentTimeMillis(); this.closed.set(false); this.active.set(true); // 省略部分日志... // Initialize any placeholder property sources in the context environment. // 初始化一些属性,交由子类实现 initPropertySources(); // Validate that all properties marked as required are resolvable: // see ConfigurablePropertyResolver#setRequiredProperties // 校验是否有必须的属性,如果有必须的属性但是环境没配置,则抛出异常 getEnvironment().validateRequiredProperties(); // Store pre-refresh ApplicationListeners... // 存储在refresh之前注册的ApplicationListener,避免这部分的ApplicationListener在后续的调用中被丢失 // 这是一个2019年修复的bug if (this.earlyApplicationListeners == null) { this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners); } else { // Reset local application listeners to pre-refresh state. this.applicationListeners.clear(); this.applicationListeners.addAll(this.earlyApplicationListeners); } // Allow for the collection of early ApplicationEvents, // to be published once the multicaster is available... this.earlyApplicationEvents = new LinkedHashSet<>(); }
这个里面比较简单,没啥好看。稍微来看下getEnvironment().validateRequiredProperties()
的代码。
/** * Return the {@code Environment} for this application context in configurable * form, allowing for further customization. * <p>If none specified, a default environment will be initialized via * {@link #createEnvironment()}. */ @Override public ConfigurableEnvironment getEnvironment() { if (this.environment == null) { this.environment = createEnvironment(); } return this.environment; } /** * Create and return a new {@link StandardEnvironment}. * <p>Subclasses may override this method in order to supply * a custom {@link ConfigurableEnvironment} implementation. */ protected ConfigurableEnvironment createEnvironment() { return new StandardEnvironment(); }
可以看到这里返回的是StandardEnvironment
类型Environment
,这里的逻辑就是有则返回,无则创建。接下来看validateRequiredProperties()
方法。最终实现是在AbstractPropertyResolver#validateRequiredProperties()
方法。
@Override public void validateRequiredProperties() { MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException(); // 遍历requiredProperties,逐个去当前环境获取是否存在 for (String key : this.requiredProperties) { if (this.getProperty(key) == null) { ex.addMissingRequiredProperty(key); } } // 如果存在缺失的必须属性,则抛出异常 if (!ex.getMissingRequiredProperties().isEmpty()) { throw ex; } }
prepareRefresh()
方法比较简单,到此已经基本看完,接下来看下一个obtainFreshBeanFactory()
方法。
obtainFreshBeanFactory()
方法从名字上就知道是获取并且同时刷新一个beanfactory
。
在没有分析代码之前,先来猜测一下它的实现。我们知道它最终会返回一个可以使用的beanfactory
,说明此时在beanfactory
里已经初始化完成了我们定义的BeanDefinition
,那么这里应该会有一些基础信息的配置,然后解析xml
文件,获取xml
配置信息,然后初始化相应的BeanDefinition
,准备就绪后返回beanfactory
。当然这只是猜测,具体实现如何,马上分析。
那么接下来就跟进这个方法看一下代码实现。
/** * Tell the subclass to refresh the internal bean factory. * @return the fresh BeanFactory instance * @see #refreshBeanFactory() * @see #getBeanFactory() */ protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { // 调用子类的实现,刷新beanFactory refreshBeanFactory(); // 返回刷新完成(即启动完成)的beanFactory return getBeanFactory(); }
跟进refreshBeanFactory()
方法,关键地方都已经加上注释,也比较简单易懂,跟着看一下就行。
/** * This implementation performs an actual refresh of this context's underlying * bean factory, shutting down the previous bean factory (if any) and * initializing a fresh bean factory for the next phase of the context's lifecycle. * 此实现执行此上下文的底层 bean 工厂的实际刷新, * 关闭先前的 bean 工厂(如果有)并为上下文生命周期的下一阶段初始化一个新的 bean 工厂。 */ @Override protected final void refreshBeanFactory() throws BeansException { // 如果有前一个,先关闭 if (hasBeanFactory()) { // 先销毁所有已经注册的bean destroyBeans(); // 关闭工厂,将this.beanFactory设为null closeBeanFactory(); } try { //创建DefaultListableBeanFactory DefaultListableBeanFactory beanFactory = createBeanFactory(); //为了序列化指定id,如果需要的话,让这个beanFactory从id反序列化到BeanFactory对象 beanFactory.setSerializationId(getId()); //定制beanFactory,设置相关属性,包括是否允许覆盖同名称的不同定义的对象已经循环依赖 //以及设置@Autowire和@Qualifier注册解析器QualifierAnnotationAutowire-CandidateResolver customizeBeanFactory(beanFactory); //初始化DocumentReader,并进行xml文件读取和解析 loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } }
前面的准备工作是比较简单的,不再细说。重点在loadBeanDefinitions(beanFactory)
方法,这里会加载xml
获取我们配置的BeanDefinition
。
记住这个位置AbstractRefreshableApplicationContext#refreshBeanFactory()
,这是经过前面繁杂的摸索后真正进入BeanDefinition加载的分水岭。
跟进代码查看。
/** * Loads the bean definitions via an XmlBeanDefinitionReader. * 通过 XmlBeanDefinitionReader 加载 bean 定义。 * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader * @see #initBeanDefinitionReader * @see #loadBeanDefinitions */ @Override protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // Create a new XmlBeanDefinitionReader for the given BeanFactory. //为指定 beanFactory创建XmlBeanDefinitionReader XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context's // resource loading environment. //对 beanDefinitionReader 进行环境变量的设置 beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // Allow a subclass to provide custom initialization of the reader, // then proceed with actually loading the bean definitions. // 允许子类提供阅读器的自定义初始化,然后继续实际加载 bean 定义。 // 对 beanDefinitionReader 进行属性设置 initBeanDefinitionReader(beanDefinitionReader); // 加载 bean 定义 loadBeanDefinitions(beanDefinitionReader); }
可以看到这里也是进行了一波准备工作,首先是给beanFactory
创建一个XmlBeanDefinitionReader
,后续xml解析都是由它来完成,接下来对这个XmlBeanDefinitionReader
进行一些属性设置,接下来调用loadBeanDefinitions(beanDefinitionReader)
加载BeanDefinition
,跟进代码查看。
/** * * 使用给定的 XmlBeanDefinitionReader 加载 bean 定义。 * <p>bean 工厂的生命周期由 {@link refreshBeanFactory} 方法处理;因此这个方法只是应该加载和或注册 bean 定义。 * * 在初始化了DefaultListableBeanFactory和XmlBeanDefinitionReader后就可以进行配置文件的读取了 * */ protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { // 返回一个引用构建此上下文的 XML bean 定义文件的 Resource 对象数组, 默认实现返回 {@code null}。 // 子类可以覆盖它以提供预构建的资源对象而不是占位符字符串。 Resource[] configResources = getConfigResources(); if (configResources != null) { reader.loadBeanDefinitions(configResources); } // 返回引用构建此上下文的 XML bean 定义文件资源位置数组,还可以包括占位符模式,这将通过 ResourcePatternResolver 进行解析获取。 // <p>默认实现返回 {@code null}。子类可以覆盖它以提供一组资源位置以从中加载 bean 定义。 String[] configLocations = getConfigLocations(); if (configLocations != null) { reader.loadBeanDefinitions(configLocations); } }
可以看到,由于没有预构建的资源,所以configResources
会为null
,configLocations
则获取到了我们的beans.xml
,那么接下来就会解析beans.xml
获取BeanDefinition
。
跟进reader.loadBeanDefinitions(configLocations)
方法,具体的实现是在 AbstractBeanDefinitionReader#loadBeanDefinitions(String... locations)
方法。
@Override public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException { Assert.notNull(locations, "Location array must not be null"); int count = 0; for (String location : locations) { count += loadBeanDefinitions(location); } return count; }
继续套娃,进入loadBeanDefinitions(location)
方法。
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException { //获取资源加载器,主要功能是根据路径和类加载器获取Resource对象 ResourceLoader resourceLoader = getResourceLoader(); if (resourceLoader == null) { throw new BeanDefinitionStoreException( "Cannot load bean definitions from location [" + location + "]: no ResourceLoader available"); } //ResourcePatternResolver用于加载多个文件或者加载Ant风格路径的资源文件 if (resourceLoader instanceof ResourcePatternResolver) { // Resource pattern matching available. try { // 以Resource形式返回所有配置文件 Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); // 通过resources加载 int count = loadBeanDefinitions(resources); if (actualResources != null) { Collections.addAll(actualResources, resources); } if (logger.isTraceEnabled()) { logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]"); } return count; } catch (IOException ex) { throw new BeanDefinitionStoreException( "Could not resolve bean definition resource pattern [" + location + "]", ex); } } else { /** * 加载单个资源,直接使用ResourceLoader加载 */ // Can only load single resources by absolute URL. Resource resource = resourceLoader.getResource(location); int count = loadBeanDefinitions(resource); if (actualResources != null) { actualResources.add(resource); } if (logger.isTraceEnabled()) { logger.trace("Loaded " + count + " bean definitions from location [" + location + "]"); } return count; } }
这里会经过一系列的重载方法,最终会来到doLoadBeanDefinitions()
方法里,这才是真正解析的地方,好家伙,山路十八弯。
跟进doLoadBeanDefinitions()
方法。
/** * 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 * @see #doLoadDocument * @see #registerBeanDefinitions */ protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { /** * 创建Document对象,XML文档对象,即dom树 * 使用这个Document对象可以获取XML文件中的节点并且创建节点 * SAX XML * 解析dom树,即解析出一个个属性,将其属性保存到BeanDefinition当中 * 并向容器注册BeanDefinition */ Document doc = doLoadDocument(inputSource, resource); int count = registerBeanDefinitions(doc, resource); if (logger.isDebugEnabled()) { logger.debug("Loaded " + count + " bean definitions from " + resource); } return count; } catch (BeanDefinitionStoreException ex) { throw ex; } // 省略部分异常信息 }
获取Document
对象就不说了,可以单独出一篇文章Spring Ioc源码分析系列--获取xml文件Document对象,这里再跟进去就离主题太远了。
跟进registerBeanDefinitions(doc, resource)
方法。
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { //创建BeanDefinitionDocumentReader,这个是实际从XML的dom树中服务BeanDefinition BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); //获取注册表BeanDefinitionMap在本次加载前的BeanDefinition数量 int countBefore = getRegistry().getBeanDefinitionCount(); //加载并注册 documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); //用本次加载后的数据减去以前有的数量,即为本次加载的BeanDefinition数量 return getRegistry().getBeanDefinitionCount() - countBefore; }
重点在方法documentReader.registerBeanDefinitions(doc, createReaderContext(resource))
里,跟进代码。
protected void doRegisterBeanDefinitions(Element root) { // Any nested <beans> elements will cause recursion in this method. In // order to propagate and preserve <beans> default-* attributes correctly, // keep track of the current (parent) delegate, which may be null. Create // the new (child) delegate with a reference to the parent for fallback purposes, // then ultimately reset this.delegate back to its original (parent) reference. // this behavior emulates a stack of delegates without actually necessitating one. //BeanDefinition的解析委托类 BeanDefinitionParserDelegate parent = this.delegate; //判断这个根节点是否是默认的命名空间 //底层就是判断这个根节点的namespace="http://www.springframework.org/schema/beans" this.delegate = createDelegate(getReaderContext(), root, parent); if (this.delegate.isDefaultNamespace(root)) { //获取这个profile的值,表示剖面,用于设置环境 String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { //根据分隔符换成数组 String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); // We cannot use Profiles.of(...) since profile expressions are not supported // in XML config. See SPR-12458 for details. //判断这个切面是否是激活的环境,如果不是直接返回,表示这个配置文件不是当前运行环境的配置文件 if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { if (logger.isDebugEnabled()) { logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource()); } return; } } } //解析XML之前做的准备工作,其实什么也没做 preProcessXml(root); //调用这个方法解析 parseBeanDefinitions(root, this.delegate); //后续处理 postProcessXml(root); this.delegate = parent; }
跟进解析BeanDefinition的方法parseBeanDefinitions(root, this.delegate)
里面。
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; //Bean定义的document对象使用了spring的默认命名空间,如http://www.springframework.org/schema/beans if (delegate.isDefaultNamespace(ele)) { //若是则按照spring原有的逻辑进行解析 parseDefaultElement(ele, delegate); } else { delegate.parseCustomElement(ele); } } } } else { //使用扩展的自定义代理类去解析 delegate.parseCustomElement(root); } }
这个例子没有自定义标签,进入到默认标签的解析。
//根据不同的标签进行解析 private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { //如果是import标签,进入导入解析 if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { importBeanDefinitionResource(ele); } //若果是别名元素,则进行别名解析 else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { processAliasRegistration(ele); } //bean元素解析 else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { processBeanDefinition(ele, delegate); } else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // recurse doRegisterBeanDefinitions(ele); } }
显然我们的例子只有一个Bean标签,所以会进入到processBeanDefinition()
方法里。
/** * Process the given bean element, parsing the bean definition * and registering it with the registry. */ protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { //BeanDefinitionHolder是对BeanDefinition的封装,即bean定义的封装类 //对document对象中的bean标签解析由BeanDefinitionParserDelegate实现 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // Register the final decorated instance. //从springIOC容器注册解析得到的BeanDefinition,这是BeanDefinition向IOC容器注册的入口 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex); } // Send registration event. getReaderContext().fireComponentRegistered(new BeanComponentDefinition ·(bdHolder)); } }
调用BeanDefinitionReaderUtils.registerBeanDefinition()
方法真正将BeanDefinition注册进容器里。咋注册呢?其实就是加到BeanFactory
的beanDefinitionMap
属性里。beanDefinitionMap
可以说就是BeanDefinition的容器了。
public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. String beanName = definitionHolder.getBeanName(); // 这里真正把BeanDefinition注册到了BeanFactory里 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any. // 注册别名 String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { registry.registerAlias(beanName, alias); } } }
到这里已经完成了BeanDefinition的注册,是不是有点曲折?其实很简单,就是细节比较多。
到这里obtainFreshBeanFactory()
方法已经基本结束,已经完成了配置文件的解析并且注册BeanDefinition的过程了。剩下的操作就是把这个BeanFactory返回给上一步。
接下来分析一下prepareBeanFactory()
方法,这方法也简单,主要就是对BeanFactory进行一些属性的设置,跟着注释看一下就行。
/** * Configure the factory's standard context characteristics, * such as the context's ClassLoader and post-processors. * @param beanFactory the BeanFactory to configure */ protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) { // Tell the internal bean factory to use the context's class loader etc. // 设置 beanFactory的classLoader 为当前 context的classLoader beanFactory.setBeanClassLoader(getClassLoader()); /** * 设置 beanFactory的表达式语言处理器,Spring3 增加了表达式语言的支持, * 默认可以使用#{bean.xxx}的形式来调用相关属性值 * @Qusetion 在注册了这个解析器之后,spring是在什么时候调用这个解析器的呢? * Spring在bean进行初始化的时候会有属性填充的步骤,而在这一步中 * Spring会调用AbstractAutowireCapableBeanFactory类的applyPropertyValues函数来完成功能。 * 就这个函数中,会通过构造 BeanDefinitionValueResolver类型实例valueResolver来进行属性值的解析。 * 同时,也是在这个步骤中一般通过 AbstractBeanFactory 中的 evaluateBeanDefinitionString * 方法去完成SpEL解析 */ beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader())); //为beanFactory增加一个PropertyEditor,这个主要是对bean的属性等设置管理的一个工具 beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment())); // Configure the bean factory with context callbacks. //添加beanPostProcessor beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this)); //设置几个忽略自动装配的接口 // aware都是由invokeAware方法注入,忽略自动Autowire beanFactory.ignoreDependencyInterface(EnvironmentAware.class); beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class); beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class); beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class); beanFactory.ignoreDependencyInterface(MessageSourceAware.class); beanFactory.ignoreDependencyInterface(ApplicationContextAware.class); // BeanFactory interface not registered as resolvable type in a plain factory. // MessageSource registered (and found for autowiring) as a bean. // 设置了几个自动装配的规则,后面三个都是把当前对象注册了进去,只有BeanFactory老老实实得注册了一个BeanFactory beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory); beanFactory.registerResolvableDependency(ResourceLoader.class, this); beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this); beanFactory.registerResolvableDependency(ApplicationContext.class, this); // Register early post-processor for detecting inner beans as ApplicationListeners. beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this)); // Detect a LoadTimeWeaver and prepare for weaving, if found. // 添加对AspectJ的支持 if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); // Set a temporary ClassLoader for type matching. beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); } // Register default environment beans.添加系统环境默认的bean if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) { beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment()); } if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) { beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties()); } if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) { beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment()); } }
到这里refresh()的前面三个方法已经简单过完了,除了解析配置文件复杂点,其他的都是些属性配置居多。
本文先通过一个列子驱动,找到了Ioc容器启动的入口,简单分析了一下前三个方法,最重要的就是加载BeanDefinition的过程了,可以仔细看多几遍。
今天发生了点小插曲,本该写得更详细点的,但是写不了了,只能作罢,将就着看吧。
如果有人看到这里,那在这里老话重提。与君共勉,路漫漫其修远兮,吾将上下而求索。