spring IOC源码分析(一)bean工厂的创建加载过程
最近学的比较散乱,没有真正学到比较重要的东西。这几天把sprign源码再看一遍,整理下以前看的经历,同时把相关的个人心得记录下来。
spring两大功能模块,IOC和AOP,在之前已经知到了IOC原理是反射来获取bean实例,而AOP是动态代理来实现的。这里先介绍下IOC的源码,它是如何来实现的。当然目前能学到的也只是比较浅的大概流程,再复杂点的看起来就比较吃力了。
推荐在学习spring源码的时候下载一份来,好方便注释。这里先从在一开始学习时见到的说起:
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(“applicationContext.xml”);
进入ClassPathXmlApplicationContext的构造器,在顺着构造器分析来到这,查看注释:
// 使用给定的父级创建新的ClassPathXmlApplicationContext, // 从给定的XML文件加载定义。 //加载所有bean定义并创建所有单例 //或者,在进一步配置上下文后手动调用refresh。 public ClassPathXmlApplicationContext( String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException { super(parent); setConfigLocations(configLocations); if (refresh) { refresh(); } }
从代码可以看出这里有两个方法setConfigLocations(configLocations)和重点refresh()。显然前者是进行配置的,打开看到:
public void setConfigLocations(@Nullable String... locations) { if (locations != null) { //里面对locations进行了空值判定,如果有一个地址是空的,会抛出异常 Assert.noNullElements(locations, "Config locations must not be null"); this.configLocations = new String[locations.length]; for (int i = 0; i < locations.length; i++) { //进一步解析地址 this.configLocations[i] = resolvePath(locations[i]).trim(); } } else { this.configLocations = null; } }
于是spring相关的配置缓存在configLocations中,回到核心方法refresh()中,追踪打开:
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. //配置bean工厂以便在此上下文中使用。 prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. //允许在上下文的子类中对bean factory进行后处理 //注册request/session scopes,一个ServletContextAwareProcessor处理器等。 postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. //在上下文中调用注册为bean的工厂处理器。 invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. //注册拦截bean创建的bean处理器。 registerBeanPostProcessors(beanFactory); // Initialize message source for this context. //为此上下文初始化消息源。 initMessageSource(); // Initialize event multicaster for this context. //为此上下文初始化事件多主机。 initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. //初始化特定上下文子类中的其他特殊bean。 onRefresh(); // Check for listener beans and register them. //检查侦听器bean并注册它们。 registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. //实例化所有剩余的(非lazy init)单例。 finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. //最后一步:发布对应的事件。 finishRefresh(); }
在prepareRefresh()中打开可以发现这里获取了当前时间,以及进行一些设置,为接下来的刷新获取容器作准备;
然后是重点的obtainFreshBeanFactory(),获取bean工厂。
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { refreshBeanFactory();//刷新获取DefaultListableBeanFactory类型的bean工厂,实质的配置也是在这里面配置的 ConfigurableListableBeanFactory beanFactory = getBeanFactory();//获取配置好的DefaultListableBeanFactory if (logger.isDebugEnabled()) { logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory); } return beanFactory; }
首先点开refreshBeanFactory(),它的实现类是AbstractRefreshableApplicationContext
protected final void refreshBeanFactory() throws BeansException { if (hasBeanFactory()) { destroyBeans();//在DisposableBean接口中,destory(),主要的销毁办法还是通过Map或set的clear()来销毁的 closeBeanFactory(); } try { DefaultListableBeanFactory beanFactory = createBeanFactory();//创建工厂是默认的DefaultListableBeanFactory类型 beanFactory.setSerializationId(getId());//设立一个id customizeBeanFactory(beanFactory);//对上面创建的工厂进行初始化,设置是否可以被子类重写等 loadBeanDefinitions(beanFactory);//加载bean定义,用于读取xml配置文件 //在解析<Bean>元素过程中没有创建和实例化Bean对象,只是创建了Bean对象的定义类BeanDefinition, // 将<Bean>元素中的配置信息设置到BeanDefinition中作为记录,当依赖注入时才使用这些记录信息创建和实例化具体的Bean对象。 synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } }
首先是需要销毁之前的bean和beanFactory,对于bean实例的销毁主要是通过之前保存的map,clear()来清除。然后来到createBeanFactory(),这里创建beanFactory,是DefaultListableBeanFactory类型的,点开createBeanFactory(),发现里面是使用DefaultListableBeanFactory的构造器参数是getInternalParentBeanFactory()
protected DefaultListableBeanFactory createBeanFactory() { ////创建并返回一个DefaultListableBeanFactory工厂,其中参数是他的父工厂 return new DefaultListableBeanFactory(getInternalParentBeanFactory()); }
追踪getInternalParentBeanFactory()发现返回ApplicationContext,ApplicationContext是由BeanFactory派生而来的。得到ApplicationContext后便缓存在AbstractBeanFactory抽象类中,同时返回到refreshBeanFactory()中。然后看到loadBeanDefinitions(beanFactory),追踪来到它的实现类AbstractXmlApplicationContext,
@Override protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // Create a new XmlBeanDefinitionReader for the given BeanFactory. //方法内部是采用XmlBeanDefinitionReader来读取加载bean对象 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context's // resource loading environment. //使用此上下文的资源加载环境 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定义。 initBeanDefinitionReader(beanDefinitionReader); //重点 loadBeanDefinitions(beanDefinitionReader);//通过XMLBeanDefinitionReader结合location路径信息读取Resources资源信息 }
我们发现,这里对刚才获得的DefaultListableBeanFactory进行了xml的加载并初始化。这里进行了一些简单的配置后来到主要的加载xml的方法 loadBeanDefinitions(beanDefinitionReader),追踪源码后来到:
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); } Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get(); if (currentResources == null) { currentResources = new HashSet<>(4); this.resourcesCurrentlyBeingLoaded.set(currentResources); } if (!currentResources.add(encodedResource)) { throw new BeanDefinitionStoreException( "Detected cyclic loading of " + encodedResource + " - check your import definitions!"); } try { //获得一个io 其实resource对象就是Spring容器中我们配置的xml文件 InputStream inputStream = encodedResource.getResource().getInputStream(); try { //开始准备解析xml文件了 InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } //重点doLoadBeanDefinitions 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(); } } }
这里才是真正开始加载xml配置的地方,具体的解析方 26a77 法实在doLoadBeanDefinitions里面。由于我们直接获取到的并不是实例bean而是BeanDefinitions ,所以当我们追踪doLoadBeanDefinitions到doRegisterBeanDefinitions方法的时候就差不多能找到它是如何解析这些xml的标签元素的
protected void doRegisterBeanDefinitions(Element root) if (this.delegate.isDefaultNamespace(root)) { String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { if (logger.isInfoEnabled()) { logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource()); } return; } } } //空方法 preProcessXml(root); //重点方法 parseBeanDefinitions(root, this.delegate); //空方法 postProcessXml(root); this.delegate = parent; }
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { //如果使用了Spring默认的XML命名空间 if (delegate.isDefaultNamespace(root)) { //遍历根元素的所有子节点 NodeList nl = root.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); //如果该节点是XML元素节点 if (node instanceof Element) { Element ele = (Element) node; //如果该节点使用的是Spring默认的XML命名空间 if (delegate.isDefaultNamespace(ele)) { //使用Spring的Bean规则解析元素节点 //重点 parseDefaultElement(ele, delegate); } else { //没有使用Spring默认的XML命名空间,则使用用户自定义的解析规则解析元素节点 //(如:自定义的xsd等) delegate.parseCustomElement(ele); } } } } else { //Document的根节点没有使用Spring默认的命名空间,则使用用户自定义的解析规则解析Document根节点 delegate.parseCustomElement(root); } }
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { //如果元素节点是<Import>导入元素,进行导入解析 if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { importBeanDefinitionResource(ele); } //如果元素节点是<Alias>别名元素,进行别名解析 else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { processAliasRegistration(ele); } //如果普通的<Bean>元素,按照Spring的Bean规则解析元素 //bean定义解析入口 else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { processBeanDefinition(ele, delegate); } else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // recurse doRegisterBeanDefinitions(ele); } }
这里是对不同的元素进行解析,由于我们的目标是bean,于是进入processBeanDefinition
/** * 处理给定的bean元素,分析bean定义 * 并在注册处注册。 * 解析bean资源 */ protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { //BeanDefinitionHolder是对BeanDefinition的封装,包括BeanDefinition,beanName,aliases //对Document对象中<Bean>元素的解析由BeanDefinitionParserDelegate实现 //next BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // Register the final decorated instance. //向Spring IoC容器注册解析得到的Bean定义, //这是Bean定义向IoC容器注册的入口 ,实际上是放到一个Map里面 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)); } }
在解析过后,BeanDefinition便注册进去了。回到obtainFreshBeanFactory()还有一点要说明的是,由于DefaultListableBeanFactory是ConfigurableListableBeanFactory的实现类,所以getBeanFactory()返回的是AbstractRefreshableApplicationContext中DefaultListableBeanFactory类型的数据。
这下bean工厂基本上是获取到了,再经过一些后续处理后就可以用来获取bean了。
也许有些地方表述的不够好或者错了,还多体谅。下一节将整理关于getBean的流程分析。
- Spring IOC 容器源码分析 - 创建单例 bean 的过程
- 分析spring源码第五(三)篇:Spring中Bean的解析、加载、创建 过程总结
- 【spring源码分析】加载bean过程(1)
- Spring源码分析:非懒加载的单例Bean初始化过程(上)
- Spring IOC源码分析(七):IOC容器的设计实现与bean对象的创建流程
- Spring源码分析:非懒加载的单例Bean初始化过程(下)
- Spring IOC 容器源码分析 - 创建原始 bean 对象
- 【Spring源码分析】非懒加载的单例Bean初始化过程(下篇)
- 【Spring源码分析】非懒加载的单例Bean初始化过程(上篇)
- 2、Spring的LocalSessionFactoryBean创建过程源码分析
- spring源码分析 加载bean过程
- 【spring源码分析】加载bean过程(2)
- Spring Ioc 源码分析之Bean的加载和构造
- Spring Ioc创建之BeanFactory创建源码分析
- spring IOC核心源码:容器创建过程
- Spring源码--IoC容器创建过程
- spring源码学习之路---深度分析IOC容器初始化过程(四)
- 【Spring源码分析】非懒加载的单例Bean初始化前后的一些操作
- 深入研究Spring-IoC:源码分析容器创建
- Spring源码分析之Bean的加载流程