面试中问的最多的就是你看过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";@Overrideprotected 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 唯一的一个方法

@Overrideprotected 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@Nullableprotected 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@Nullableprotected 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)就是设置了下环境变量的propertySourcespublic 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"));//输出 zhangsanSystem.out.println(resolver.resolvePlaceholders("My name is ${name} "));//输出My name is zhangsan

接下来看resolveRequiredPlaceholders(text)方法,因为PropertySourcesPropertyResolver没有实现这个方法,所以在父类AbstractPropertyResolver中找到了

@Overridepublic 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@Nullableprotected <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初始化如何,请听下回分解…
Spring源码解析(二)

Spring源码解析(一)相关推荐

  1. Spring源码解析 - AbstractBeanFactory 实现接口与父类分析

    2019独角兽企业重金招聘Python工程师标准>>> 我们先来看类图吧: 除了BeanFactory这一支的接口,AbstractBeanFactory主要实现了AliasRegi ...

  2. Spring源码解析:自定义标签的解析过程

    2019独角兽企业重金招聘Python工程师标准>>> spring version : 4.3.x Spring 中的标签分为默认标签和自定义标签两类,上一篇我们探究了默认标签的解 ...

  3. Spring 源码解析 -- SpringWeb过滤器Filter解析

    简介 在上几篇文章中探索了请求处理相关的代码,本篇开始探索请求处理前的一些操作代码,如Filter.本篇探索Filter初始化.请求处理等相关代码. 前言 说先简单的定义相关的测试代码: 启动类: i ...

  4. Spring源码解析 -- SpringWeb请求参数获取解析

    Spring源码解析 – SpringWeb请求参数获取解析 简介 在文章:Spring Web 请求初探中,我们看到最后方法反射调用的相关代码,本篇文章就探索其中的参数是如何从请求中获取的 概览 方 ...

  5. Spring源码解析 -- SpringWeb请求映射Map初始化

    简介 在上篇文章中,大致解析了Spring如何将请求路径与处理方法进行映射,但映射相关的初始化对于我们来说还是一团迷雾 本篇文章就来探索下,请求路径和处理方法的映射,是如何进行初始化的 概览 基于上篇 ...

  6. Spring 源码解析 -- SpringWeb请求映射解析

    Spring 源码解析 – SpringWeb请求映射解析 简介 基于上篇请求路径初步探索,了解到了一个请求到具体处理方法的大致路径,本篇就继续探索,看下路径是如何匹配到处理方法的 概览 基于上篇:S ...

  7. Spring源码解析【完整版】--【bilibili地址:https://www.bilibili.com/video/BV1oW41167AV】

    [本文为bilibili视频雷丰阳的Spring源码解析的完整版总结文章,其中文章前面大部分为他人博文的搬运,后面补充了其未总结的部分] 一.Java的注解 1. 注解的概念 注释:用文字描述程序,给 ...

  8. Spring源码解析-bean实例化

    Spring源码解析-bean实例化 ​ 本文介绍Spring创建 bean 过程中的第一个步骤:实例化 bean. 1. Bean实例化源码 ​ 虽然实例化Bean有多种方式(包括静态工厂和工厂实例 ...

  9. 源码解析:Spring源码解析笔记(五)接口设计总览

    本文由colodoo(纸伞)整理 QQ 425343603 Java学习交流群(717726984) Spring解析笔记 启动过程部分已经完成,对启动过程源码有兴趣的朋友可以作为参考文章. 源码解析 ...

  10. 人人都能看懂的Spring源码解析,Spring如何解决循环依赖

    人人都能看懂的Spring源码解析,Spring如何解决循环依赖 原理解析 什么是循环依赖 循环依赖会有什么问题? 如何解决循环依赖 问题的根本原因 如何解决 为什么需要三级缓存? Spring的三级 ...

最新文章

  1. Linu基础:磁盘存储和文件管理
  2. JAVA线程池的简单实现及优先级设置
  3. verilog对YCrCb转换灰度设计及仿真
  4. 数据库中的二级索引_普通索引_辅助索引
  5. SQL SERVER 中 GO 的用法2
  6. 苹果CMS10 资源网模板
  7. 通过ANT实现jmeter批量执行脚本、生成报告、发送邮件全套build.xml文件
  8. Java基础-TreeSet与Java自定义类型的排序
  9. 7z001怎么解压在安卓手机上面_安卓zip文件压缩RAR解压手机下载-安卓zip文件压缩RAR解压v1.0最新版下载...
  10. JavaWeb — 解决请求前与请求后中文乱码的问题
  11. Retinex算法,图像色彩增强之python实现——MSR,MSRCR,MSRCP,autoMSRCR
  12. 串口485接法图_RS-485 2线和4线的接法
  13. 仿QQ登录界面UI设计
  14. GEEer成长日记六:Sentinel-2计算逐日NDVI时间序列
  15. 塔顶分凝器全凝器区别_(单选)在相同的回流比和塔顶蒸汽组成下,采用分凝器+全凝器的二元连续精馏塔与仅采用全凝器的塔相比,()...
  16. 《程序设计基础》 第十章 函数与程序结构 6-5 递归求简单交错幂级数的部分和 (15 分)
  17. Java的静态类详解
  18. binutils java_一起编制binutils和gcc的配方?
  19. 下载mmcv + mmdet 步骤(针对大部分Windows安装)
  20. 2022年整理最详细的java面试题、掌握这一套八股文、面试基础不成问题[吐血整理、纯手撸]

热门文章

  1. 运用ssis组件实现邮件发送,内容来源于数据库表
  2. 一张图带你复习《数字信号处理》、《数字电路》、《电磁场理论》
  3. Securing Services with Spring Cloud Gateway
  4. nginx 5xx 状态码分析
  5. Simulink代码生成基础体验教程
  6. eclipse查看ftl文件
  7. 偏最小二乘回归(PLSR)算法原理
  8. 恒压供水程序 三菱plc恒压供水程序,威纶触摸屏程序
  9. 【MATLAB生信分析】MATLAB生物信息分析工具箱(二)
  10. PHP微信公众号服务器配置