Spring源碼解析(一)

面試中問的最多的就是你看過xxx源碼嘛,我TM就想一jio過去,你工作中不是curd麼,CV大法麼,要看源碼幹什麼。對,一開始我jio得看源碼沒什麼用。面試官一開始叫我看源碼,我是拒絕的,我不能因爲你要問,我就要看啊,我得先試試,後來我試了之後發現,這效果duangduangduang的,誒呀,真香!
現在上主題,spring源碼的真香定理開課了。

ClassPathXmlApplicationContext applicationContext =new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
applicationContext.getBean("");

進入ClassPathXmlApplicationContext 有參構造

public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
	this(new String[] {configLocation}, true, null);
}
//	 進入this(new String[] {configLocation}, true, null)方法
public ClassPathXmlApplicationContext(
			String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
			throws BeansException {
		//1.初始化父類,設置PathMatchingResourcePatternResolver(資源查找器,主要是獲取資源文件的時候可以解析*,?等符號的路徑)
		super(parent);
		//2.設置本地的配置信息
		setConfigLocations(configLocations);
		//3.spring容器的初始化
		if (refresh) {
			refresh();
		}
}

真正調用的就是ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent),它做了三件大事。

1.初始化父類

設置PathMatchingResourcePatternResolver(資源查找器,主要是獲取資源文件的時候可以解析*,?等符號的路徑)
跟蹤源碼super(parent);

//這裏是初始化AbstractXmlApplicationContext
public AbstractXmlApplicationContext(@Nullable ApplicationContext parent) {
		super(parent);
}
//繼續跟蹤super(parent);
//初始化AbstractRefreshableConfigApplicationContext
public AbstractRefreshableConfigApplicationContext(@Nullable ApplicationContext parent) {
		super(parent);
}
//繼續跟蹤super(parent);
//初始化AbstractRefreshableApplicationContext
public AbstractRefreshableApplicationContext(@Nullable ApplicationContext parent) {
		super(parent);
}
//繼續跟蹤super(parent);
//初始化AbstractApplicationContext
public AbstractApplicationContext(@Nullable ApplicationContext parent) {
		//定義了一個資源查找器
		this();
		//ClassPathXmlApplicationContext 中的有參構造parent爲null,所以這裏啥也沒幹
		setParent(parent);
}

走到這裏是不是感覺要暈了,所以,我們來一張類繼承圖,看看
在這裏插入圖片描述
然後繼續跟蹤this()方法

//跟蹤this()
public AbstractApplicationContext() {
	//爲內部一個獲取資源的屬性賦值
	this.resourcePatternResolver = getResourcePatternResolver();
}
//getResourcePatternResolver()
protected ResourcePatternResolver getResourcePatternResolver() {
	//PathMatchingResourcePatternResolver這裏就是真正的資源查找器,獲取資源getResource方法就是用他的
	return new PathMatchingResourcePatternResolver(this);
}

資源查找器PathMatchingResourcePatternResolver,我們看一下他的類繼承圖
在這裏插入圖片描述
ResourceLoader是spring定義的一個資源加載器接口,ResourcePatternResolver是可以查找"classpath*:"的這種格式的,而PathMatchingResourcePatternResolver這個資源查找器就比較強大,可以加載"classpath*:applicationContext-*.xml"這種格式,也就是我們xml中配置的加載文件context:property-placeholde這個標籤中的通配符的資源文件,當然,既然是孫子類,兒子和爺爺的功能他也可以有的。說了一大堆,其實就是告訴你,設置了個資源查找器,你要調用getResource方法就是用他的。
AbstractApplicationContext這個中不是還有個方法麼,啥也沒幹的方法setParent(parent);,看看。

//就是判斷了下parent是不是空,是空就啥也沒幹
public void setParent(@Nullable ApplicationContext parent) {
		this.parent = parent;
		if (parent != null) {
			Environment parentEnvironment = parent.getEnvironment();
			if (parentEnvironment instanceof ConfigurableEnvironment) {
				getEnvironment().merge((ConfigurableEnvironment) parentEnvironment);
			}
		}
	}

2.設置本地的配置信息

跟蹤源碼setConfigLocations(configLocations);

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++) {
			//循環取出每一個path參數,在此處就一個"applicationContext.xml"
				this.configLocations[i] = resolvePath(locations[i]).trim();
			}
		}
		else {
			this.configLocations = null;
		}
}
//跟蹤resolvePath(locations[i])
protected String resolvePath(String path) {
	//兩部分,getEnvironment()創建環境對象StandardEnvironment,resolveRequiredPlaceholders(path)就是替換${}這樣的值,就像你xml中引入另外一個文件,然後你會用${}一樣,不過這裏是從環境變量中去替換佔位符
	return getEnvironment().resolveRequiredPlaceholders(path);
}
//跟蹤getEnvironment()
public ConfigurableEnvironment getEnvironment() {
		if (this.environment == null) {
			this.environment = createEnvironment();
		}
		return this.environment;
}
//繼續跟蹤createEnvironment()
//實例化一個StandardEnvironment
protected ConfigurableEnvironment createEnvironment() {
		return new StandardEnvironment();
}

這裏比較有意思的是,你以爲他就是實例化一個StandardEnvironment,啥也沒幹,其實不是,繼續跟蹤看一看,發現
StandardEnvironment沒有無參構造。

public class StandardEnvironment extends AbstractEnvironment {

	/** System environment property source name: {@value} */
	public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";

	/** JVM system properties property source name: {@value} */
	public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";
	
	@Override
	protected void customizePropertySources(MutablePropertySources propertySources) {
		propertySources.addLast(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
		propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
	}

}

沒有那就證明無參構造用的是父類AbstractEnvironment的,那來看看父類AbstractEnvironment的無參構造

private final MutablePropertySources propertySources = new MutablePropertySources(this.logger);

public AbstractEnvironment() {
		customizePropertySources(this.propertySources);
		if (logger.isDebugEnabled()) {
			logger.debug("Initialized " + getClass().getSimpleName() + " with PropertySources " + this.propertySources);
		}
}

無參構造又調用了customizePropertySources(this.propertySources);而propertySources就是 new MutablePropertySources(this.logger)。
這裏解釋一下MutablePropertySources,這個是什麼,這個是PropertySources的實現類,而PropertySources又是繼承了Iterable<PropertySource<?>>,PropertySource又是什麼?PropertySource是一個抽象類,它包含一個source和一個name。source可以是map或其他,通常是一組鍵值對。PropertySource有個實現類MapPropertySource,而MutablePropertySources包含了一個CopyOnWriteArrayList集合,用來包含多個PropertySource。

private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<PropertySource<?>>();

很抽象是不是,沒關係,用一個結構解釋。

		Map<String,Object> map1=new HashMap<>();
		map1.put("systemProperties","我是Java進程變量");
		Map<String,Object> map2=new HashMap<>();
		map2.put("systemEnvironment","我是系統環境變量");
		PropertySource source1=new MapPropertySource("person",map1);
		PropertySource source2=new MapPropertySource("person",map2);
		List<PropertySource> list =new ArrayList<PropertySource>();
		list.add(source1);
		list.add(source2);

上圖這個List list就相當於MutablePropertySources,相當於但是不等於,只是模擬讓你稍微理解一下。

customizePropertySources(this.propertySources)方法AbstractEnvironment是空實現,啥也沒有,所以這樣是調用的StandardEnvironment的,也就是上面StandardEnvironment 唯一的一個方法

@Override
	protected void customizePropertySources(MutablePropertySources propertySources) {
		propertySources.addLast(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
		propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
	}

因爲上面我模擬了一下數據結構,那這裏猜一下也知道是首先向propertySources添加一組屬性,來自Java進程變量(getSystemProperties()內是System.getProperties()方法);接着向propertySources再添加一組屬性,來自系統環境變量(getSystemEnvironment()內是System.getenv()方法);
getSystemProperties和getSystemEnvironment方法中有個相同的細節需要注意,在獲取進程變量或者系統環境變量的時候,都有可能因爲安全限制拋出異常,這時候就返回一個ReadOnlySystemAttributesMap的實現類,外部調用get方法的時候,再去嘗試獲取進程變量或者系統環境變量對應的值,取不到則返回null

@Override
	@SuppressWarnings({"unchecked", "rawtypes"})
	public Map<String, Object> getSystemProperties() {
		try {
			return (Map) System.getProperties();
		}
		catch (AccessControlException ex) {
			return (Map) new ReadOnlySystemAttributesMap() {
				@Override
				@Nullable
				protected String getSystemAttribute(String attributeName) {
					try {
						return System.getProperty(attributeName);
					}
					catch (AccessControlException ex) {
						if (logger.isInfoEnabled()) {
							logger.info("Caught AccessControlException when accessing system property '" +
									attributeName + "'; its value will be returned [null]. Reason: " + ex.getMessage());
						}
						return null;
					}
				}
			};
		}
	}

	@Override
	@SuppressWarnings({"unchecked", "rawtypes"})
	public Map<String, Object> getSystemEnvironment() {
		if (suppressGetenvAccess()) {
			return Collections.emptyMap();
		}
		try {
			return (Map) System.getenv();
		}
		catch (AccessControlException ex) {
			return (Map) new ReadOnlySystemAttributesMap() {
				@Override
				@Nullable
				protected String getSystemAttribute(String attributeName) {
					try {
						return System.getenv(attributeName);
					}
					catch (AccessControlException ex) {
						if (logger.isInfoEnabled()) {
							logger.info("Caught AccessControlException when accessing system environment variable '" +
									attributeName + "'; its value will be returned [null]. Reason: " + ex.getMessage());
						}
						return null;
					}
				}
			};
		}
	}

系統環境搞定了,接下來是處理佔位符了,跟蹤一下resolveRequiredPlaceholders(path)

	public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
		return this.propertyResolver.resolveRequiredPlaceholders(text);
	}
//this.propertyResolver
private final ConfigurablePropertyResolver propertyResolver =
			new PropertySourcesPropertyResolver(this.propertySources);
//new PropertySourcesPropertyResolver(this.propertySources)就是設置了下環境變量的propertySources
	public PropertySourcesPropertyResolver(@Nullable PropertySources propertySources) {
		this.propertySources = propertySources;
	}	

這裏PropertySourcesPropertyResolver的類繼承圖看一看
在這裏插入圖片描述
PropertyResolver實現這個類的接口具有解析PropertySource、根據PropertySource轉換文本中的佔位符的能力
一段代碼說明

		Map<String,Object> map1=new HashMap<>();
		map1.put("name","zhangsan");
		PropertySource source=new MapPropertySource("person",map1);
		MutablePropertySources sources = new MutablePropertySources();
		sources.addLast(source);
		PropertyResolver resolver = new PropertySourcesPropertyResolver(sources);
		System.out.println(resolver.containsProperty("name"));//輸出 zhangsan
		System.out.println(resolver.resolvePlaceholders("My name is ${name} "));//輸出My name is zhangsan

接下來看resolveRequiredPlaceholders(text)方法,因爲PropertySourcesPropertyResolver沒有實現這個方法,所以在父類AbstractPropertyResolver中找到了

@Override
	public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
		if (this.strictHelper == null) {
			this.strictHelper = createPlaceholderHelper(false);
		}
		return doResolvePlaceholders(text, this.strictHelper);
	}
//this.strictHelper
private PropertyPlaceholderHelper strictHelper;
//createPlaceholderHelper(false)定義一個PropertyPlaceholderHelper,並傳參數用於判斷是否忽略不能解析的變量
private PropertyPlaceholderHelper createPlaceholderHelper(boolean ignoreUnresolvablePlaceholders) {
		return new PropertyPlaceholderHelper(this.placeholderPrefix, this.placeholderSuffix,
				this.valueSeparator, ignoreUnresolvablePlaceholders);
	}
//doResolvePlaceholders(text, this.strictHelper)
private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
		//找到字符串中的佔位符,調用PropertyResolver.getPropertyAsRawString方法,從環境變量中取出佔位符對應的值,用環境變量的值替換佔位符
		return helper.replacePlaceholders(text, this::getPropertyAsRawString);
}

helper.replacePlaceholders(text, this::getPropertyAsRawString);作用其實就是找到字符串中的佔位符,調用PropertyResolver.getPropertyAsRawString方法,從環境變量中取出佔位符對應的值,用環境變量的值替換佔位符,比如classpath*:applicationContext-${profile}.xml替換爲系統中profile的值
getPropertyAsRawString方法就是在propertySources中找值:

//getPropertyAsRawString AbstractPropertyResolver空實現,所以看子類PropertySourcesPropertyResolver
protected String getPropertyAsRawString(String key) {
		return getProperty(key, String.class, false);
	}
//getProperty(key, String.class, false)
//找到佔位符key對應的value
	@Nullable
	protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
		if (this.propertySources != null) {
			for (PropertySource<?> propertySource : this.propertySources) {
				if (logger.isTraceEnabled()) {
					logger.trace("Searching for key '" + key + "' in PropertySource '" +
							propertySource.getName() + "'");
				}
				Object value = propertySource.getProperty(key);
				if (value != null) {
					if (resolveNestedPlaceholders && value instanceof String) {
						value = resolveNestedPlaceholders((String) value);
					}
					logKeyFound(key, propertySource, value);
					return convertValueIfNecessary(value, targetValueType);
				}
			}
		}
		if (logger.isDebugEnabled()) {
			logger.debug("Could not find key '" + key + "' in any property source");
		}
		return null;
	}

replacePlaceholders方法就是替換:

	public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {
		Assert.notNull(value, "'value' must not be null");
		return parseStringValue(value, placeholderResolver, new HashSet<>());
	}
	
	//parseStringValue(value, placeholderResolver, new HashSet<>())
		protected String parseStringValue(
			String value, PlaceholderResolver placeholderResolver, Set<String> visitedPlaceholders) {

		StringBuilder result = new StringBuilder(value);

		int startIndex = value.indexOf(this.placeholderPrefix);
		while (startIndex != -1) {
			int endIndex = findPlaceholderEndIndex(result, startIndex);
			if (endIndex != -1) {
				String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);
				String originalPlaceholder = placeholder;
				if (!visitedPlaceholders.add(originalPlaceholder)) {
					throw new IllegalArgumentException(
							"Circular placeholder reference '" + originalPlaceholder + "' in property definitions");
				}
				 //這裏有迭代操作,確保處理完字符串中所有的佔位符
				placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
				 // 這裏實際上會調用PropertySourcesPropertyResolver.getPropertyAsRawString方法,propVal的值就是從環境變量中取得的值
				String propVal = placeholderResolver.resolvePlaceholder(placeholder);
				if (propVal == null && this.valueSeparator != null) {
					int separatorIndex = placeholder.indexOf(this.valueSeparator);
					if (separatorIndex != -1) {
						String actualPlaceholder = placeholder.substring(0, separatorIndex);
						String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());
						propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);
						if (propVal == null) {
							propVal = defaultValue;
						}
					}
				}
				if (propVal != null) {
					// Recursive invocation, parsing placeholders contained in the
					// previously resolved placeholder value.
					propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);
					result.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);
					if (logger.isTraceEnabled()) {
						logger.trace("Resolved placeholder '" + placeholder + "'");
					}
					startIndex = result.indexOf(this.placeholderPrefix, startIndex + propVal.length());
				}
				else if (this.ignoreUnresolvablePlaceholders) {
					// Proceed with unprocessed value.
					startIndex = result.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());
				}
				else {
					throw new IllegalArgumentException("Could not resolve placeholder '" +
							placeholder + "'" + " in value \"" + value + "\"");
				}
				visitedPlaceholders.remove(originalPlaceholder);
			}
			else {
				startIndex = -1;
			}
		}

		return result.toString();
	}

總結一下
1.初始化AbstractXmlApplicationContext,AbstractRefreshableConfigApplicationContext,AbstractRefreshableApplicationContext,AbstractApplicationContext,在AbstractApplicationContext設置屬性值ResourcePatternResolver資源獲取的類爲PathMatchingResourcePatternResolver,主要是爲了我們獲取資源的時候可以解析通配符。
2.在AbstractRefreshableConfigApplicationContext中實例化一個StandardEnvironment,在StandardEnvironment其父類AbstractEnvironment 中有個屬性MutablePropertySources分別存放了系統環境和java環境的鍵值對。給AbstractEnvironment屬性ConfigurablePropertyResolver初始化爲PropertySourcesPropertyResolver類,在其父類AbstractPropertyResolver中創建PropertyPlaceholderHelper,然後PropertyPlaceholderHelper中在用環境變量的值替換佔位符

欲知spring初始化如何,請聽下回分解…

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章