面試中問的最多的就是你看過xxx源碼嘛鸠窗,我TM就想一jio過去裕循,你工作中不是curd么株婴,CV大法么,要看源碼干什么根穷。對(duì)乍迄,一開始我jio得看源碼沒什么用管引。面試官一開始叫我看源碼,我是拒絕的闯两,我不能因?yàn)槟阋獑柸彀椋揖鸵窗。业孟仍囋囇牵髞砦以嚵酥蟀l(fā)現(xiàn)重慢,這效果duangduangduang的,誒呀逊躁,真香似踱!
現(xiàn)在上主題
ClassPathXmlApplicationContext applicationContext =new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
applicationContext.getBean("");
進(jìn)入ClassPathXmlApplicationContext 有參構(gòu)造
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
// 進(jìn)入this(new String[] {configLocation}, true, null)方法
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
//1.初始化父類,設(shè)置PathMatchingResourcePatternResolver(資源查找器,主要是獲取資源文件的時(shí)候可以解析*核芽,囚戚?等符號(hào)的路徑)
super(parent);
//2.設(shè)置本地的配置信息
setConfigLocations(configLocations);
//3.spring容器的初始化
if (refresh) {
refresh();
}
}
真正調(diào)用的就是ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent),它做了三件大事轧简。
1.初始化父類
設(shè)置PathMatchingResourcePatternResolver(資源查找器驰坊,主要是獲取資源文件的時(shí)候可以解析*,哮独?等符號(hào)的路徑)
跟蹤源碼super(parent);
//這里是初始化AbstractXmlApplicationContext
public AbstractXmlApplicationContext(@Nullable ApplicationContext parent) {
super(parent);
}
//繼續(xù)跟蹤super(parent);
//初始化AbstractRefreshableConfigApplicationContext
public AbstractRefreshableConfigApplicationContext(@Nullable ApplicationContext parent) {
super(parent);
}
//繼續(xù)跟蹤super(parent);
//初始化AbstractRefreshableApplicationContext
public AbstractRefreshableApplicationContext(@Nullable ApplicationContext parent) {
super(parent);
}
//繼續(xù)跟蹤super(parent);
//初始化AbstractApplicationContext
public AbstractApplicationContext(@Nullable ApplicationContext parent) {
//定義了一個(gè)資源查找器
this();
//ClassPathXmlApplicationContext 中的有參構(gòu)造parent為null,所以這里啥也沒干
setParent(parent);
}
走到這里是不是感覺要暈了,所以良蒸,我們來一張類繼承圖遥巴,看看
然后繼續(xù)跟蹤this()方法
//跟蹤this()
public AbstractApplicationContext() {
//為內(nèi)部一個(gè)獲取資源的屬性賦值
this.resourcePatternResolver = getResourcePatternResolver();
}
//getResourcePatternResolver()
protected ResourcePatternResolver getResourcePatternResolver() {
//PathMatchingResourcePatternResolver這里就是真正的資源查找器,獲取資源getResource方法就是用他的
return new PathMatchingResourcePatternResolver(this);
}
資源查找器PathMatchingResourcePatternResolver悴务,我們看一下他的類繼承圖
ResourceLoader是spring定義的一個(gè)資源加載器接口浆竭,ResourcePatternResolver是可以查找"classpath:"的這種格式的,而PathMatchingResourcePatternResolver這個(gè)資源查找器就比較強(qiáng)大惨寿,可以加載"classpath:applicationContext-*.xml"這種格式邦泄,也就是我們xml中配置的加載文件<context:property-placeholde>這個(gè)標(biāo)簽中的通配符的資源文件,當(dāng)然裂垦,既然是孫子類顺囊,兒子和爺爺?shù)墓δ芩部梢杂械摹Uf了一大堆蕉拢,其實(shí)就是告訴你特碳,設(shè)置了個(gè)資源查找器,你要調(diào)用getResource方法就是用他的晕换。
AbstractApplicationContext這個(gè)中不是還有個(gè)方法么午乓,啥也沒干的方法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.設(shè)置本地的配置信息
跟蹤源碼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++) {
//循環(huán)取出每一個(gè)path參數(shù),在此處就一個(gè)"applicationContext.xml"
this.configLocations[i] = resolvePath(locations[i]).trim();
}
}
else {
this.configLocations = null;
}
}
//跟蹤resolvePath(locations[i])
protected String resolvePath(String path) {
//兩部分夷家,getEnvironment()創(chuàng)建環(huán)境對(duì)象StandardEnvironment蒸其,resolveRequiredPlaceholders(path)就是替換${}這樣的值,就像你xml中引入另外一個(gè)文件库快,然后你會(huì)用${}一樣摸袁,不過這里是從環(huán)境變量中去替換占位符
return getEnvironment().resolveRequiredPlaceholders(path);
}
//跟蹤getEnvironment()
public ConfigurableEnvironment getEnvironment() {
if (this.environment == null) {
this.environment = createEnvironment();
}
return this.environment;
}
//繼續(xù)跟蹤createEnvironment()
//實(shí)例化一個(gè)StandardEnvironment
protected ConfigurableEnvironment createEnvironment() {
return new StandardEnvironment();
}
這里比較有意思的是,你以為他就是實(shí)例化一個(gè)StandardEnvironment义屏,啥也沒干靠汁,其實(shí)不是蜂大,繼續(xù)跟蹤看一看,發(fā)現(xiàn)
StandardEnvironment沒有無參構(gòu)造蝶怔。
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()));
}
}
沒有那就證明無參構(gòu)造用的是父類AbstractEnvironment的县爬,那來看看父類AbstractEnvironment的無參構(gòu)造
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);
}
}
無參構(gòu)造又調(diào)用了customizePropertySources(this.propertySources);而propertySources就是 new MutablePropertySources(this.logger)。
這里解釋一下MutablePropertySources添谊,這個(gè)是什么财喳,這個(gè)是PropertySources的實(shí)現(xiàn)類,而PropertySources又是繼承了Iterable<PropertySource<?>>斩狱,PropertySource又是什么耳高?PropertySource是一個(gè)抽象類,它包含一個(gè)source和一個(gè)name所踊。source可以是map或其他泌枪,通常是一組鍵值對(duì)。PropertySource有個(gè)實(shí)現(xiàn)類MapPropertySource秕岛,而MutablePropertySources包含了一個(gè)CopyOnWriteArrayList集合碌燕,用來包含多個(gè)PropertySource。
private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<PropertySource<?>>();
很抽象是不是继薛,沒關(guān)系修壕,用一個(gè)結(jié)構(gòu)解釋。
Map<String,Object> map1=new HashMap<>();
map1.put("systemProperties","我是Java進(jìn)程變量");
Map<String,Object> map2=new HashMap<>();
map2.put("systemEnvironment","我是系統(tǒng)環(huán)境變量");
PropertySource source1=new MapPropertySource("person",map1);
PropertySource source2=new MapPropertySource("person",map2);
List<PropertySource> list =new ArrayList<PropertySource>();
list.add(source1);
list.add(source2);
上圖這個(gè)List<PropertySource> list就相當(dāng)于MutablePropertySources遏考,相當(dāng)于但是不等于慈鸠,只是模擬讓你稍微理解一下。
customizePropertySources(this.propertySources)方法AbstractEnvironment是空實(shí)現(xiàn)灌具,啥也沒有青团,所以這樣是調(diào)用的StandardEnvironment的,也就是上面StandardEnvironment 唯一的一個(gè)方法
@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()));
}
因?yàn)樯厦嫖夷M了一下數(shù)據(jù)結(jié)構(gòu)咖楣,那這里猜一下也知道是首先向propertySources添加一組屬性督笆,來自Java進(jìn)程變量(getSystemProperties()內(nèi)是System.getProperties()方法);接著向propertySources再添加一組屬性诱贿,來自系統(tǒng)環(huán)境變量(getSystemEnvironment()內(nèi)是System.getenv()方法)娃肿;
getSystemProperties和getSystemEnvironment方法中有個(gè)相同的細(xì)節(jié)需要注意,在獲取進(jìn)程變量或者系統(tǒng)環(huán)境變量的時(shí)候瘪松,都有可能因?yàn)榘踩拗茠伋霎惓O套鳎@時(shí)候就返回一個(gè)ReadOnlySystemAttributesMap的實(shí)現(xiàn)類,外部調(diào)用get方法的時(shí)候宵睦,再去嘗試獲取進(jìn)程變量或者系統(tǒng)環(huán)境變量對(duì)應(yīng)的值,取不到則返回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;
}
}
};
}
}
系統(tǒng)環(huán)境搞定了墅诡,接下來是處理占位符了壳嚎,跟蹤一下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)就是設(shè)置了下環(huán)境變量的propertySources
public PropertySourcesPropertyResolver(@Nullable PropertySources propertySources) {
this.propertySources = propertySources;
}
這里PropertySourcesPropertyResolver的類繼承圖看一看
PropertyResolver實(shí)現(xiàn)這個(gè)類的接口具有解析PropertySource桐智、根據(jù)PropertySource轉(zhuǎn)換文本中的占位符的能力
一段代碼說明
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)方法,因?yàn)镻ropertySourcesPropertyResolver沒有實(shí)現(xiàn)這個(gè)方法烟馅,所以在父類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)定義一個(gè)PropertyPlaceholderHelper说庭,并傳參數(shù)用于判斷是否忽略不能解析的變量
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) {
//找到字符串中的占位符,調(diào)用PropertyResolver.getPropertyAsRawString方法郑趁,從環(huán)境變量中取出占位符對(duì)應(yīng)的值刊驴,用環(huán)境變量的值替換占位符
return helper.replacePlaceholders(text, this::getPropertyAsRawString);
}
helper.replacePlaceholders(text, this::getPropertyAsRawString);作用其實(shí)就是找到字符串中的占位符,調(diào)用PropertyResolver.getPropertyAsRawString方法寡润,從環(huán)境變量中取出占位符對(duì)應(yīng)的值捆憎,用環(huán)境變量的值替換占位符,比如classpath*:applicationContext-${profile}.xml替換為系統(tǒng)中profile的值
getPropertyAsRawString方法就是在propertySources中找值:
//getPropertyAsRawString AbstractPropertyResolver空實(shí)現(xiàn)梭纹,所以看子類PropertySourcesPropertyResolver
protected String getPropertyAsRawString(String key) {
return getProperty(key, String.class, false);
}
//getProperty(key, String.class, false)
//找到占位符key對(duì)應(yīng)的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);
// 這里實(shí)際上會(huì)調(diào)用PropertySourcesPropertyResolver.getPropertyAsRawString方法,propVal的值就是從環(huán)境變量中取得的值
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();
}
總結(jié)一下
1.初始化AbstractXmlApplicationContext变抽,AbstractRefreshableConfigApplicationContext础拨,AbstractRefreshableApplicationContext,AbstractApplicationContext绍载,在AbstractApplicationContext設(shè)置屬性值ResourcePatternResolver資源獲取的類為PathMatchingResourcePatternResolver诡宗,主要是為了我們獲取資源的時(shí)候可以解析通配符。
2.在AbstractRefreshableConfigApplicationContext中實(shí)例化一個(gè)StandardEnvironment击儡,在StandardEnvironment其父類AbstractEnvironment 中有個(gè)屬性MutablePropertySources分別存放了系統(tǒng)環(huán)境和java環(huán)境的鍵值對(duì)僚焦。給AbstractEnvironment屬性ConfigurablePropertyResolver初始化為PropertySourcesPropertyResolver類,在其父類AbstractPropertyResolver中創(chuàng)建PropertyPlaceholderHelper曙痘,然后PropertyPlaceholderHelper中在用環(huán)境變量的值替換占位符
欲知spring初始化如何芳悲,請(qǐng)聽下回分解....