spring源码---IOC:xml式启动

目录

 一,基于xml的配置启动

1.super(paremt)  

2.setConfigLocations(configLocations)   

3. refresh()   


参考:《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类的继承图,

两个值得重要的顶级接口:BeanFactoryResourceLoader

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个功能。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值