面试中问的最多的就是你看过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),它做了三件大事。
设置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);
}
}
}
补充说明,这里父类定义大量的模板,让子类实现,父类层层传递到子类 知道某个子类重载了抽象方法。这里应用到了职责链设计模式和模板设计模式。
跟踪源码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
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中在用环境变量的值替换占位符