前言java
本文轉自「天河聊技術」微信公衆號web
接着上次的spring上下文加載過程的源碼解析spring
正文安全
上次主要介紹到刷新beanFactory,找到這個方法org.springframework.context.support.AbstractRefreshableApplicationContext#refreshBeanFactory的這行代碼微信
@Override protected final void refreshBeanFactory() throws BeansException { // 若是存在beanFactory if (hasBeanFactory()) { // 銷燬bean destroyBeans(); // 關閉beanFactory closeBeanFactory(); }
bean銷燬介紹完了,找到這行代碼app
closeBeanFactory();
@Override protected final void closeBeanFactory() { synchronized (this.beanFactoryMonitor) { if (this.beanFactory != null) { this.beanFactory.setSerializationId(null); this.beanFactory = null; } } }
操做beanFactory是同步的。ide
找到這行代碼,建立banFactoryui
DefaultListableBeanFactory beanFactory = createBeanFactory();
protected DefaultListableBeanFactory createBeanFactory() { return new DefaultListableBeanFactory(getInternalParentBeanFactory()); }
@Nullable protected BeanFactory getInternalParentBeanFactory() { // 若是父上下文是配置上下文 return (getParent() instanceof ConfigurableApplicationContext) ? ((ConfigurableApplicationContext) getParent()).getBeanFactory() : getParent(); }
返回到這個方法this
org.springframework.context.support.AbstractRefreshableApplicationContext#refreshBeanFactoryspa
customizeBeanFactory(beanFactory);
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) { // 是否容許bean定義覆蓋 if (this.allowBeanDefinitionOverriding != null) { beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } // 是否容許bean之間循環引用 if (this.allowCircularReferences != null) { beanFactory.setAllowCircularReferences(this.allowCircularReferences); } }
返回到這個方法
org.springframework.context.support.AbstractRefreshableApplicationContext#refreshBeanFactory
loadBeanDefinitions(beanFactory);
這一行代碼,加載bean定義。
進入到這個方法
org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.support.DefaultListableBeanFactory)經過XmlBeanDefinitionReader加載bean定義。
@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.使用該上下文的資源加載環境配置bean定義閱讀器。 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); // 加載bean定義 loadBeanDefinitions(beanDefinitionReader); }
// 加載資源 beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
public ResourceEntityResolver(ResourceLoader resourceLoader) { super(resourceLoader.getClassLoader()); this.resourceLoader = resourceLoader; }
進入調用了父類構造器的方法
public DelegatingEntityResolver(@Nullable ClassLoader classLoader) { // 建立DTD解析器 this.dtdResolver = new BeansDtdResolver(); // 建立schema解析器 this.schemaResolver = new PluggableSchemaResolver(classLoader); }
能夠看到解析的是這個DTD
public class BeansDtdResolver implements EntityResolver { // dtd解析 private static final String DTD_EXTENSION = ".dtd"; private static final String DTD_NAME = "spring-beans";
public PluggableSchemaResolver(@Nullable ClassLoader classLoader) { this.classLoader = classLoader; this.schemaMappingsLocation = DEFAULT_SCHEMA_MAPPINGS_LOCATION; }
org.springframework.beans.factory.xml.PluggableSchemaResolver
// spring.schemas解析,這個路徑下面的schemas都會被解析 META-INF/spring.schemas public static final String DEFAULT_SCHEMA_MAPPINGS_LOCATION = "META-INF/spring.schemas";
從這個地方加載的schema
找到這個方法
org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.support.DefaultListableBeanFactory)的這行代碼
// 初始化bean定義 initBeanDefinitionReader(beanDefinitionReader);
// 加載bean定義 loadBeanDefinitions(beanDefinitionReader);
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); } }
// 獲取配置文件路徑 String[] configLocations = getConfigLocations();
@Nullable protected String[] getConfigLocations() { return (this.configLocations != null ? this.configLocations : getDefaultConfigLocations()); }
咱們看下configLocations變量在哪裏賦值的
public void setConfigLocation(String location) { setConfigLocations(StringUtils.tokenizeToStringArray(location, CONFIG_LOCATION_DELIMITERS)); }
咱們看下location變量在哪裏賦值的
找到了這個方法org.springframework.web.context.ContextLoader#configureAndRefreshWebApplicationContext
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM); if (configLocationParam != null) { wac.setConfigLocation(configLocationParam); }
public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation";
是從servlet容器啓動時初始化的參數配置中獲取的
返回到這個方法
org.springframework.context.support.AbstractRefreshableConfigApplicationContext#setConfigLocation
public void setConfigLocation(String location) { setConfigLocations(StringUtils.tokenizeToStringArray(location, CONFIG_LOCATION_DELIMITERS)); }
看下這個常量org.springframework.context.ConfigurableApplicationContext#CONFIG_LOCATION_DELIMITERS
String CONFIG_LOCATION_DELIMITERS = ",; \t\n";
bean定義配置文件能夠是,或;或空格或換行符分開均可以。
進入到這個方法
org.springframework.context.support.AbstractRefreshableConfigApplicationContext#setConfigLocations
public void setConfigLocations(@Nullable String... locations) { if (locations != null) { 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上下文中 this.configLocations[i] = resolvePath(locations[i]).trim();
bean定義配置文件中若是有佔位符就替換佔位符
protected String resolvePath(String path) { return getEnvironment().resolveRequiredPlaceholders(path); }
@Override public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException { if (this.strictHelper == null) { this.strictHelper = createPlaceholderHelper(false); } // 替換佔位符 return doResolvePlaceholders(text, this.strictHelper); }
返回到這個方法
org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.xml.XmlBeanDefinitionReader)的這一行代碼
// 加載bean定義 reader.loadBeanDefinitions(configLocations);
@Override public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException { Assert.notNull(locations, "Location array must not be null"); int counter = 0; for (String location : locations) { counter += loadBeanDefinitions(location); } return counter; }
org.springframework.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions(java.lang.String)
@Override public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException { return loadBeanDefinitions(location, null); }
org.springframework.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions(java.lang.String, java.util.Set<org.springframework.core.io.Resource>)
這段代碼
// 從classpath下面加載 if (resourceLoader instanceof ResourcePatternResolver) { // Resource pattern matching available. try { Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); int loadCount = loadBeanDefinitions(resources); if (actualResources != null) { for (Resource resource : resources) { actualResources.add(resource); } } if (logger.isDebugEnabled()) { logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]"); } return loadCount; } catch (IOException ex) { throw new BeanDefinitionStoreException( "Could not resolve bean definition resource pattern [" + location + "]", ex); } }
找到這行代碼
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
進入到這個方法
org.springframework.core.io.support.PathMatchingResourcePatternResolver#getResources
@Override public Resource[] getResources(String locationPattern) throws IOException { Assert.notNull(locationPattern, "Location pattern must not be null"); // 配置路徑以classpath*:開頭 if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) { // a class path resource (multiple resources for same name possible) if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) { // a class path resource pattern 從classpath*路徑下匹配的的jar,war包等文件中查找配置文件 return findPathMatchingResources(locationPattern); } else { // all class path resources with the given name 從classpath下面加載jar、war文件中的配置文件 return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length())); } } else { // Generally only look for a pattern after a prefix here, // and on Tomcat only after the "*/" separator for its "war:" protocol. int prefixEnd = (locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 : locationPattern.indexOf(':') + 1); if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) { // a file pattern return findPathMatchingResources(locationPattern); } else { // a single resource with the given name return new Resource[] {getResourceLoader().getResource(locationPattern)}; } } }
返回到這個方法
org.springframework.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions(java.lang.String, java.util.Set<org.springframework.core.io.Resource>)
遞歸調用 int loadCount = loadBeanDefinitions(resources);
@Override public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException { Assert.notNull(resources, "Resource array must not be null"); int counter = 0; for (Resource resource : resources) { counter += loadBeanDefinitions(resource); } return counter; }
org.springframework.beans.factory.xml.XmlBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.Resource)
@Override public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { return loadBeanDefinitions(new EncodedResource(resource)); }
org.springframework.beans.factory.xml.XmlBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.support.EncodedResource)
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.getResource()); } // resourcesCurrentlyBeingLoaded threadLocal保證線程安全 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 { InputStream inputStream = encodedResource.getResource().getInputStream(); try { InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } // 加載bean定義 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(); } } }
找到這行代碼
// 加載bean定義 return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
內容太多,下篇文章接着解析。
最後
本次介紹到這裏,以上內容僅供參考。你們從spring源碼解析第一篇文章開始看,更容易點,不然可讀性可能不臺好,由於spring源碼體系太龐大,不太好歸類,按每篇文章的篇幅不太好拆分。