目录
2.setConfigLocations(configLocations)
参考:《spring 5 核心原理》 (此文为读书笔记)
Spring的使用,有两种基本的方式,一种是基于xml的,另外一种是基于java配置类的,而这两种都可以使用注解进行依赖注入。 Spring的几个核心功能:IOC DI AOP MVC 是如何实现的呢?
Spring核心的东西是Bean。一个类定义为bean,然后交给spring去管理,Spring如何找到这些自定义的bean,Spring如何去存放这些bean,如何去实例化,如何在想要使用的时候,成功的注入,Spring什么时候去销毁这些bean。等等问题,在源码中找寻答案!
一,基于xml的配置启动
测试代码demo:
public static void main( String[] args )
{
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("classpath:spring-config.xml");
User user=(User)applicationContext.getBean("user");
user.setName("123");
System.out.println( user.getName() );
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.myProject.Myspring.User"/>
</beans>
在main方法里面实例化ClassPathXMlApplicationContext 类并给定xml的位置,然后就可以使用Spring上下文进行getBean操作了。
1.进入调用的构造方法: ClassPathXmlApplicationContext类的继承图,
两个值得重要的顶级接口:BeanFactory和ResourceLoader
2.类的字段信息:存在xml信息的字段
private Resource[] configResources;
构造方法:configLocation字段就是我们main()方法中传入的".....xml"地址值,第一个方法,只是一个入口,它调用了下面的构造方法。
//1.xml启动-调用的应该是这里的构造,构造方法又调用其他的构造方法。我们继续寻找。
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
//2.xml启动,通过ctrl+左键,来到这里,然后发现了 最重要的refresh启动方法。 第一个是初始化父类,然后设置配置文件的信息。
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
//这个类 就是层层递进进行初始化。
super(parent);
//这个是父类的方法, 它的作用就是 把配置文件,存入配置文件组中,判断是否有空格,把文件名字格式化一下。
setConfigLocations(configLocations); //这里只是把配置文件的名字,装载好了,还没有开始解析。
//我们的重头戏。 所以前面两步都是初始化准备过程。!
if (refresh) {
refresh();
}
}
第二个构造方法中,主要是三个方法调用: 下面我们将详细讲解这三个方法的源码。
第一个: 初始化父类
第二个:初始化配置文件
第三个: 初始化容器
1.super(paremt)
父类初始化逆顺序:parent=null
1.AbstractXmlApplicationContext:super(parent)
2.AbstractRefreshableConfigApplicationContext:super(parent)
3.AbstractRefreshableApplicationContext:super(parent)
4.AbstractApplicationContext: this(); setParent(parent);--------->这个类,内容很多,各种字段,在这个时候,都需要初始化
//patternResolver初始化
public AbstractApplicationContext() {
this.resourcePatternResolver = getResourcePatternResolver();
}
//这个资源解析器 是一个接口
public interface ResourcePatternResolver extends ResourceLoader {
String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
Resource[] getResources(String locationPattern) throws IOException;
}
实现结构:
applicationContext继承了Beanfactory和ResourceLoader(梳理一下),PathMatchingResourcePatternResolve的作用是在refresh()第二步中,解析xml->resource->称为document的时候使用!
protected ResourcePatternResolver getResourcePatternResolver() {
return new PathMatchingResourcePatternResolver(this);
}
父类初始化就先停在这里。大概呢,就是给父类AbstractApplicationContext准备ResourceResolver。
2.setConfigLocations(configLocations)
进入的第一个方法:位于AbstraceRefreshableConfigApplicationContext中,把传入的多个xml,解析成为一个一个单独的。
!!!(如果你的xml中,没有使用特殊的匹配符号,那么这里的解析,就没有用处)
//获得传入的xml路径,创建一个配置位置组,然后把解析结果放进去。
public void setConfigLocations(@Nullable String... locations) {
if (locations != null) {
this.configLocations = new String[locations.length];
for (int i = 0; i < locations.length; i++) {
//此处进行解析,深入观察一下。这里深入之后,发现各种方法调用,然后到了一个解析字符串的地方,过于复杂,先!暂停
this.configLocations[i] = resolvePath(locations[i]).trim();
}
}
else {
this.configLocations = null;
}
}
这里都是入口:方法还是本类的方法;
protected String resolvePath(String path) {
return getEnvironment().resolveRequiredPlaceholders(path);
}
跳转到了AbstraceApplicationContext的方法,实例化了一个StanderEnvironment类,调用解析:
public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
return this.propertyResolver.resolveRequiredPlaceholders(text);
}
直接跳转到方法执行:位于AbstractPropertyResolver,这里也是入口,重点还是do...里面
public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
if (this.strictHelper == null) {
this.strictHelper = createPlaceholderHelper(false);
}
return doResolvePlaceholders(text, this.strictHelper);
}
private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
return helper.replacePlaceholders(text, this::getPropertyAsRawString);
}
public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {
Assert.notNull(value, "'value' must not be null");
return parseStringValue(value, placeholderResolver, new HashSet<>());
}
经过三个入口,终于到了正文了:
先看一下传入的值情况:
//------------PropertyPlaceHolderHelper
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;
placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
// Now obtain the value for the fully resolved key...
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);
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 {
}
visitedPlaceholders.remove(originalPlaceholder);
}
else {
startIndex = -1;
}
}
return result.toString();
}
返回的结果:没有变化,这里是对里面的${}进行处理。
//这里新出现了 Environment类,不知道这个是干什么的,百度一下:处理占位符的, 下面这个链接说的清楚。
而且这个environment类 也是AbstractApplicationContext的。
参考连接:https://blog.csdn.net/shenchaohao12321/article/details/80390457
最终,也就是把xml路径 放入了AbstractRefreshableConfigApplicationContext的configLocations数组里面!
3. refresh()
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//1.调用容器准备刷新的方法,获取容器的当前时间,同时给容器设置同步标识。(上锁吗?)
prepareRefresh();
//2.告诉子类启动refreshBeanFactory()方法,bean定义资源文件的载入 从子类的refreBeanFactory()方法启动。
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
//3.为beanFactory 配置容器特性,例如 类加载器,事件处理器。
prepareBeanFactory(beanFactory);
try {
//4.为容器的某些子类指定 特殊的Post事件处理器。
postProcessBeanFactory(beanFactory);
//5.调用所以注册的beanFacotryPostProcessor 的bean。
invokeBeanFactoryPostProcessors(beanFactory);
//6.为BeanFactory 注册Post事件处理器
registerBeanPostProcessors(beanFactory);
//7.初始化信息源,和国际化相关
initMessageSource();
//8.初始化容器事件传播器。
initApplicationEventMulticaster();
//9.调用子类的 某些特殊的 Bean的初始化方法。
onRefresh();
//10.为事件传播器注册事件监听器
registerListeners();
//11.初始化 所有剩余的单列 Bean
finishBeanFactoryInitialization(beanFactory); //【重点】 延时加载。
//12.初始化容器的生命周期事件处理器,并发布容器的生命周期事件。
finishRefresh();
}
catch (BeansException ex) {
//13.销毁已创建的Bean
destroyBeans();
//14.取消刷新操作,重置容器的同步标识
cancelRefresh(ex);
throw ex;
}
finally {
//15.重设公共缓存
resetCommonCaches();
}
}
}
这里一看 就是主操作了,核心啊,14个功能。