文章目录
Spring源码系列:
前言
正文
方法1:prepareRefresh
方法2:obtainFreshBeanFactory
方法3:refreshBeanFactory
方法4:createBeanFactory
方法5:new DefaultListableBeanFactory
方法6:customizeBeanFactory
方法7:loadBeanDefinitions
方法8:loadBeanDefinitions
方法9:loadBeanDefinitions
方法10:doLoadBeanDefinitions
方法11:doLoadBeanDefinitions
方法12:registerBeanDefinitions
总结
Spring源码系列:
Spring IOC源码:简单易懂的Spring IOC 思路介绍
Spring IOC源码:核心流程介绍
Spring IOC源码:ApplicationContext刷新前准备工作
Spring IOC源码:obtainFreshBeanFactory 详解(上)
Spring IOC源码:obtainFreshBeanFactory 详解(中)
Spring IOC源码:obtainFreshBeanFactory 详解(下)
Spring IOC源码:<context:component-scan>源码详解
Spring IOC源码:invokeBeanFactoryPostProcessors 后置处理器详解
Spring IOC源码:registerBeanPostProcessors 详解
Spring IOC源码:实例化前的准备工作
Spring IOC源码:finishBeanFactoryInitialization详解
Spring IoC源码:getBean 详解
Spring IoC源码:createBean( 上)
Spring IoC源码:createBean( 中)
Spring IoC源码:createBean( 下)
Spring IoC源码:finishRefresh 完成刷新详解
前言
上篇文章我们讲解了refresh()方法前的准备工作,主要是初始化一些缓存容器、环境属性,还有对配置文件路径进行解析,查找和替换占位符等。Spring IOC源码:ApplicationContext刷新前准备工作
这节文章介绍refresh方法中的prepareRefresh()跟obtainFreshBeanFactory()方法。prepareRefresh比较简单主要是设置一下上下文的状态、开始时间等。obtainFreshBeanFactory是一个比较核心的方法,主要解析配置文件,封装成BeanDefinition,并放入上下文缓存中。
几个主要的缓存:
beanDefinitionNames缓存:所有被加载到 BeanFactory 中的 bean 的 beanName 集合,后面实例化遍历此集合。
beanDefinitionMap缓存:所有被加载到 BeanFactory 中的 bean 的 beanName 和 BeanDefinition 映射,后续通过beanName获取。
aliasMap缓存:所有被加载到 BeanFactory 中的 bean 的 beanName 和别名映射。
正文
方法1:prepareRefresh
protected void prepareRefresh() { //设置当前上下文状态 this.startupDate = System.currentTimeMillis(); this.closed.set(false); this.active.set(true); if (logger.isDebugEnabled()) { if (logger.isTraceEnabled()) { logger.trace("Refreshing " + this); } else { logger.debug("Refreshing " + getDisplayName()); } } //初始化当前上下文属性的上下文,该方法内容为空,主要提供给子类进行实现 initPropertySources(); //验证所有标记为必需的属性都是可解析的 getEnvironment().validateRequiredProperties(); //下面是初始化监听器和事件源集合 // Store pre-refresh ApplicationListeners... 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<>(); }
initPropertySources拓展方法案例:
@Override protected void initPropertySources() { System.out.println("扩展initPropertySource"); //这里添加了一个name属性到Environment里面,以方便我们在后面用到 getEnvironment().getSystemProperties().put("name","bobo"); //这里要求Environment中必须包含username属性,如果不包含,则抛出异常 getEnvironment().setRequiredProperties("username"); }
方法2:obtainFreshBeanFactory
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { refreshBeanFactory(); return getBeanFactory(); }
重点在于refreshBeanFactory方法,见方法3
方法3:refreshBeanFactory
protected final void refreshBeanFactory() throws BeansException { //判断一下当前上下文是否有实例化beanFactory,存在则销毁,后面会重新构建 if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { //实例化一个beanFactory DefaultListableBeanFactory beanFactory = createBeanFactory(); //设置ID beanFactory.setSerializationId(getId()); // 定制beanFactory,设置相关属性,包括是否允许覆盖同名称的不同定义的对象以及循环依赖.属于拓展方法 customizeBeanFactory(beanFactory); // 初始化documentReader,并进行XML文件读取及解析,默认命名空间的解析,自定义标签的解析 //封装BeanDefinition对象用于后续实例化初始化等工作做准备 loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } }
createBeanFactory(),实例化BeanFactory工厂对象,见方法4
customizeBeanFactory(beanFactory),设置相关属性,包括是否允许覆盖同名称的不同定义的对象以及循环依赖. 见方法
loadBeanDefinitions(beanFactory),见方法7详解
方法4:createBeanFactory
protected DefaultListableBeanFactory createBeanFactory() { return new DefaultListableBeanFactory(getInternalParentBeanFactory()); }
new DefaultListableBeanFactory:见方法5
getInternalParentBeanFactory:获取父类内部的BeanFactory,如果该父类实现了ConfigurableApplicationContext ,则返回其BeanFactory属性,不然返回父类本身。这里因为父类也是AbstractApplicationContext的子类,而AbstractApplicationContext又实现了BeanFactoty,所以本身也是一个BeanFactoty。
protected BeanFactory getInternalParentBeanFactory() { return (getParent() instanceof ConfigurableApplicationContext ? ((ConfigurableApplicationContext) getParent()).getBeanFactory() : getParent()); }
方法5:new DefaultListableBeanFactory
实例化一个BeanFactory时,会调用多层父类的构造方法。
调用父类AbstractAutowireCapableBeanFactory有参构造方法时,会父类的BeanFactory设置到当前属性值中
public AbstractAutowireCapableBeanFactory(@Nullable BeanFactory parentBeanFactory) { this(); setParentBeanFactory(parentBeanFactory); }
调用父类AbstractAutowireCapableBeanFactory无参构造方法时,会设置忽略些属性值
public AbstractAutowireCapableBeanFactory() { super(); //忽略属性注入时,不设置该值。一般是通过postProcessor进行注入的 ignoreDependencyInterface(BeanNameAware.class); ignoreDependencyInterface(BeanFactoryAware.class); ignoreDependencyInterface(BeanClassLoaderAware.class); }
方法6:customizeBeanFactory
这个方法可以在子类中进行拓展,设置其属性值,案例如下:l
@Override protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) { super.setAllowBeanDefinitionOverriding(false); super.setAllowCircularReferences(false); super.customizeBeanFactory(beanFactory); }
方法7:loadBeanDefinitions
这个方法比较复杂,主要是用来解析配置、封装等;
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // 将BeanDefinition封装成XmlBeanDefinitionReader,后续对其操作都是使用XmlBeanDefinitionReader XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); //给其设置些属性 beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); //提供拓展方法,供子类进行拓展,可以对beanDefinitionReader进行一些初始化操作 initBeanDefinitionReader(beanDefinitionReader); //解析配置 loadBeanDefinitions(beanDefinitionReader); }
loadBeanDefinitions(beanDefinitionReader);见方法8
方法8:loadBeanDefinitions
这里我们可以看都有两个配置文件格式,这些都是在进入refresh前进行占位符替换,赋值的。不管是哪种格式,最终都是以Resource格式进行的,String数组在后续步骤中也会转为Resource;接下来我们看下reader.loadBeanDefinitions(configLocations),见方法9
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { //获取配置文件路径,refresh方法前做的操作,这里有两个类型一种是Resource一种是String Resource[] configResources = getConfigResources(); if (configResources != null) { reader.loadBeanDefinitions(configResources); } String[] configLocations = getConfigLocations(); if (configLocations != null) { reader.loadBeanDefinitions(configLocations); } }
方法9:loadBeanDefinitions
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) throws BeanDefinitionStoreException { return loadBeanDefinitions(location, null); }
再接下看loadBeanDefinitions(location, null);
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException { //获取上下文的资源加载器 ResourceLoader resourceLoader = getResourceLoader(); if (resourceLoader == null) { throw new BeanDefinitionStoreException( "Cannot load bean definitions from location [" + location + "]: no ResourceLoader available"); } //判断加载器类型 if (resourceLoader instanceof ResourcePatternResolver) { // Resource pattern matching available. try { //将传进来的配置文件路径进行解析,判断路径中是否含有classPath*: 或war:等,对其解析成多个Resource Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); //解析处理 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 { // 通过路径封装成一个Resource 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; } }
封装完Resource后,继续进行配置文件的解析操作 loadBeanDefinitions(resources);
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { return loadBeanDefinitions(new EncodedResource(resource)); } public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { Assert.notNull(encodedResource, "EncodedResource must not be null"); if (logger.isTraceEnabled()) { logger.trace("Loading XML bean definitions from " + encodedResource); } //加载获取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 { //获取到当前Resource的输入流 InputStream inputStream = encodedResource.getResource().getInputStream(); try { //封装成InputSource,方便对其类型进行解析 InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } //在Spring中看到do开头的,代表是真正的逻辑处理的地方了 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(); } } }
doLoadBeanDefinitions(inputSource, encodedResource.getResource()); 见方法10
方法10:doLoadBeanDefinitions
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { //根据inputSource和resource加载XML文件,并封装成Document Document doc = doLoadDocument(inputSource, resource); //解析并封装和注册成BeanDefinition int count = registerBeanDefinitions(doc, resource); if (logger.isDebugEnabled()) { logger.debug("Loaded " + count + " bean definitions from " + resource); } return count; } catch (BeanDefinitionStoreException ex) { throw ex; } catch (SAXParseException ex) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex); } catch (SAXException ex) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", ex); } catch (ParserConfigurationException ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, ex); } catch (IOException ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, ex); } catch (Throwable ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, ex); } }
doLoadDocument(inputSource, resource);见方法11
registerBeanDefinitions(doc, resource);见方法12
方法11:doLoadBeanDefinitions
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception { // getValidationModeForResource(resource): 获取XML配置文件的验证模式 // documentLoader.loadDocument: 加载XML文件,解析封装成 Document return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler, getValidationModeForResource(resource), isNamespaceAware()); } protected int getValidationModeForResource(Resource resource) { //获取当前的验证模式标识 int validationModeToUse = getValidationMode(); //如果为指定则返回指定的 if (validationModeToUse != VALIDATION_AUTO) { return validationModeToUse; } //解析出配置文件的验证模式 int detectedMode = detectValidationMode(resource); //如果检测出的验证模式不为 VALIDATION_AUTO, 则返回检测出来的验证模式 if (detectedMode != VALIDATION_AUTO) { return detectedMode; } // Hmm, we didn't get a clear indication... Let's assume XSD, // since apparently no DTD declaration has been found up until // detection stopped (before finding the document's root tag). return VALIDATION_XSD; } public int getValidationMode() { return this.validationMode; } protected int detectValidationMode(Resource resource) { if (resource.isOpen()) { throw new BeanDefinitionStoreException( "Passed-in Resource [" + resource + "] contains an open stream: " + "cannot determine validation mode automatically. Either pass in a Resource " + "that is able to create fresh streams, or explicitly specify the validationMode " + "on your XmlBeanDefinitionReader instance."); } InputStream inputStream; try { inputStream = resource.getInputStream(); } catch (IOException ex) { throw new BeanDefinitionStoreException( "Unable to determine validation mode for [" + resource + "]: cannot open InputStream. " + "Did you attempt to load directly from a SAX InputSource without specifying the " + "validationMode on your XmlBeanDefinitionReader instance?", ex); } try { //解析检查出当前的验证标识 return this.validationModeDetector.detectValidationMode(inputStream); } catch (IOException ex) { throw new BeanDefinitionStoreException("Unable to determine validation mode for [" + resource + "]: an error occurred whilst reading from the InputStream.", ex); } }
一般Spring常用的验证文件是声明方法有两种DTD和XSD,DTD在Spring中已经停止使用了
DTD:(Document type Definition )文档类型定义,是一种xml约束模式语言,是xml文件的验证机制,属于xml文件组成的一部分。保证xml文档格式正确的有效方法。
XSD:(XML Schemas Definition),描述了XML文档的结构。可以用一个指定的XML Schema来验证某个XML文档,以检查XML文档是否符合要求。
获取各个参数完成后,进入加载Document步骤
@Override public Document loadDocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception { //创建DocumentBuilderFactory 工厂对象 DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware); if (logger.isTraceEnabled()) { logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]"); } //创建解析器 DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler); // 使用DocumentBuilder解析inputSource返回Document对象 return builder.parse(inputSource); }
方法12:registerBeanDefinitions
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { //通过反射创建默认的DefaultBeanDefinitionDocumentReader,其为BeanDefinitionDocumentReader的子类 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); //记录统计前BeanDefinition的加载个数 int countBefore = getRegistry().getBeanDefinitionCount(); //封装BeanDefinition对象,并加入缓存集合中 documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; }
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));执行前需要获取参数值,先看下该方法逻辑createReaderContext(resource);
//创建XmlReaderContext ,用于解析配置文件 public XmlReaderContext createReaderContext(Resource resource) { return new XmlReaderContext(resource, this.problemReporter, this.eventListener, this.sourceExtractor, this, getNamespaceHandlerResolver()); } //获取命名空间 public NamespaceHandlerResolver getNamespaceHandlerResolver() { if (this.namespaceHandlerResolver == null) { this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver(); } return this.namespaceHandlerResolver; } //实例化默认的命名空间 protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() { ClassLoader cl = (getResourceLoader() != null ? getResourceLoader().getClassLoader() : getBeanClassLoader()); return new DefaultNamespaceHandlerResolver(cl); }
命名空间记录了加载配置文件的解析器类路径,我们在解析文件的时候,不同的xml文件解析可能不一样的,那如果使用不同的处理器来解析呢,这里就需要用到命名空间了,根据配置文件的命名空间来找到命名空间文件,该文件记录了解析该配置文件的多个解析器类路径。Spring需要用这些来解析处理,所以我们需要先获取。
接下来进入documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; doRegisterBeanDefinitions(doc.getDocumentElement()); }
看到do开头,就证明是真正开始核心处理的地方doRegisterBeanDefinitions 见方法13
protected void doRegisterBeanDefinitions(Element root) { BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createDelegate(getReaderContext(), root, parent); //校验root节点的命名空间是否为默认的命名空间(默认命名空间http://www.springframework.org/schema/beans) 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; } } } //拓展方法,没有实现,主要留给子类拓展使用 preProcessXml(root); parseBeanDefinitions(root, this.delegate); //拓展方法,没有实现,主要留给子类拓展使用 postProcessXml(root); this.delegate = parent; }
profile属性主要用于环境开发,比如xml文件中不同环境dev、sit、uat、prod设置不同的配置。
进入 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; if (delegate.isDefaultNamespace(ele)) { //默认命名空间节点的处理,例如: <bean id="test" class="" /> parseDefaultElement(ele, delegate); } else { // 自定义命名空间节点的处理,例如:<context:component-scan/>、<aop:aspectj-autoproxy/> delegate.parseCustomElement(ele); } } } } else { //非默认命名空间走其它逻辑 delegate.parseCustomElement(root); } }
这里首先会判断当前配置的命名空间,如果不是默认的,则走自定义的逻辑,如 <context:component-scan /> 、<aop:aspectj-autoproxy />。否则是默认命令空间,则获取其子元素遍历判断,调用其默认处理逻辑,如 。
parseDefaultElement(ele, delegate) 和 delegate.parseCustomElement(ele) 方法是解析 bean 定义的两个核心方法,在后续文章后会对其进行讲解。
总结
这节内容主要讲解了配置文件解析前的处理及解析处理过程:
1.创建一个新的 BeanFactory:DefaultListableBeanFactory。
2.根据 web.xml 中 contextConfigLocation 配置的路径,读取 Spring配置文件,验证配置文件的内容,并封装成 Resource。
3.根据 Resource 加载 XML 配置文件,并解析成 Document 对象 。
4.拿到 Document 中的根节点,遍历根节点和所有子节点。
对于xml文件有默认处理方法和自定义处理方法,Spring会根据命名空间判断选择不同的对应逻辑。