今天看了一下《spring技术内幕》,看了下spring IOC容器的加载过程,但是里面的代码很杂,就自己用源码的测试用例debug了一下看了下过程
测试用例
@Test public void testSingleConfigLocation() throws IOException { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(FQ_SIMPLE_CONTEXT); assertTrue(ctx.containsBean("someMessageSource")); LazyBean bean = ctx.getBean(LazyBean.class); Environment environment =ctx.getEnvironment(); String[] profiles =environment.getDefaultProfiles(); for(String s :profiles){ System.out.println(s); } CollectionBean bean2 = (CollectionBean) ctx.getBean("collection"); SingletonBean bean3 = ctx.getBean(SingletonBean.class); SingletonBean bean4 = ctx.getBean(SingletonBean.class); if (bean != null) { System.out.println(bean.getTestFiled()); } if (bean2 != null) { System.out.println(bean2.getList()); } if (bean3 != null) { bean3.process(); } if (bean4 != null) { bean4.process(); } System.out.println(ctx.getBeanDefinitionCount()); /* * System.out.println(ctx.getBean("lazyBean")); * System.out.println(ctx.getBean("lazyBean")); */ ctx.close(); }
加载过程
- 首先会调用父类AbstractApplicationContext的静态初始化块
static { // Eagerly load the ContextClosedEvent class to avoid weird classloader issues // on application shutdown in WebLogic 8.1. (Reported by Dustin Woods.) ContextClosedEvent.class.getName(); }
大概就是说为了避免一些奇怪的问题会首先发布一个ContextClosedEvent事件,然后按照正常的初始化依次初始化父类。
- 调用构造方法中的refresh方法,这个方法也是上下文初始化的开始
public ClassPathXmlApplicationContext( String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException { super(parent); setConfigLocations(configLocations); if (refresh) { refresh(); } }
- refresh方法会调用AbstractRefreshableApplicationContext中的refreshBeanFactory方法创建一个DefaultListableBeanFactory实例,作为上下文的容器
protected final void refreshBeanFactory() throws BeansException { if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory); loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } }
- 创建了容器之后就要从xml文件中加载和注册bean的定义,上面的loadBeanDefinition方法在AbstractXmlApplicationContext中的实现会被调用
@Override protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // Create a new XmlBeanDefinitionReader for the given BeanFactory. 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. initBeanDefinitionReader(beanDefinitionReader); loadBeanDefinitions(beanDefinitionReader); } /** * Initialize the bean definition reader used for loading the bean * definitions of this context. Default implementation is empty. * <p>Can be overridden in subclasses, e.g. for turning off XML validation * or using a different XmlBeanDefinitionParser implementation. * @param reader the bean definition reader used by this context * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader#setDocumentReaderClass */ protected void initBeanDefinitionReader(XmlBeanDefinitionReader reader) { reader.setValidating(this.validating); } /** * Load the bean definitions with the given XmlBeanDefinitionReader. * <p>The lifecycle of the bean factory is handled by the {@link #refreshBeanFactory} * method; hence this method is just supposed to load and/or register bean definitions. * @param reader the XmlBeanDefinitionReader to use * @throws BeansException in case of bean registration errors * @throws IOException if the required XML document isn‘t found * @see #refreshBeanFactory * @see #getConfigLocations * @see #getResources * @see #getResourcePatternResolver */ protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { Resource[] configResources = getConfigResources(); if (configResources != null) { reader.loadBeanDefinitions(configResources); } String[] configLocations = getConfigLocations(); if (configLocations != null) { reader.loadBeanDefinitions(configLocations); } }
- 这两个方法会用来加载并注册BeanDefiniton,它们又会使用XmlBeanDefinitionReader来加载xml中的bean的定义,bean定义的解析会使用DefaultBeanDefinitionDocumentReader,它又会使用BeanDefinitionParserDelegate类委托解析bean的定义
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//委托BeanDefinitionParserDelegate进行解析 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // Register the final decorated instance. 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注册到上下文中的过程,我之前没找到这个方法很奇怪loadBeanDefinition返回的都是int我就在想,BeanDefinition哪去了。静态方法会调用BeanDefinitionRegistry
接口的registerBeanDefinition方法。而DefaultListableBeanFactory实现了这个接口,
public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. String beanName = definitionHolder.getBeanName(); 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就被注册到了ClassPathXmlApplicationContext中,还有很多细节没贴出来,自己debug一下很有意思的