面試中問的最多的就是你看過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初始化如何,請聽下回分解…