ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-bean.xml");
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
//设置Bean资源加载器
super(parent);
//设置配置路径
setConfigLocations(configLocations);
if (refresh) {
//初始化容器
refresh();
}
}
通过调用ClassPathXmlApplicationContext来构造一个spring的IOC容器,该构造函数主要做了三件事:
- 设置资源加载器和合并父类的环境变量
- 解析并设置配置文件的路径
- 初始化容器
spring使用PropertyPlaceholderHelper.replacePlaceholders来解析可能包含占位符${..}配置文件名称的
ApplicationContext bf = new ClassPathXmlApplicationContext("${prefix}-${config}.xml");
spring在读取文件,可以动态的将${..}替换成properties所配置的值,但是不能采用将配置文件引入spring的配置文件的形式
<context:property-placeholder location="classpath:spring.properties"/>
这张方式是读取不到的,因为spring读取配置文件要在读取属性文件之前,为此,要采用在系统属性里添加
Properties properties = System.getProperties();
properties.setProperty("config", "beans");
properties.setProperty("prefix", "spring");
ApplicationContext bf = new ClassPathXmlApplicationContext("${prefix}-${config}.xml");
PropertyPlaceholderHelper.replacePlaceholders又委托给parseStringValue来完成配置文件名解析,parseStringValue采用递归从内到外一层层解析占位符
protected String parseStringValue(
String value, PlaceholderResolver placeholderResolver, @Nullable Set<String> visitedPlaceholders) {
//查找${所在下标
int startIndex = value.indexOf(this.placeholderPrefix);
if (startIndex == -1) {
return value;
}
StringBuilder result = new StringBuilder(value);
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 == null) {
visitedPlaceholders = new HashSet<>(4);
}
if (!visitedPlaceholders.add(originalPlaceholder)) {
throw new IllegalArgumentException(
"Circular placeholder reference '" + originalPlaceholder + "' in property definitions");
}
// 递归解析placeholder key.
placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
// 根据key=placeholder从properties中获取值
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) {
//递归解析从properties中获取的值所包含的占位符${}
propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);
//将${..}替换成从properties获取到的值
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();
}
接下来查看初始化容器
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
//准备刷新的上下文环境
prepareRefresh();
//初始化BeanFactory,并进行XML文件读取
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
//对BeanFactory进行各种功能填充
prepareBeanFactory(beanFactory);
try {
//子类覆盖方法做额外的处理
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// 激活各种BeanFactory处理器
invokeBeanFactoryPostProcessors(beanFactory);
//注册拦截Bean创建的Bean处理器,这里只是注册,真正的调用是在getBean时候
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
//为上下文初始化Message源,即不同语言的消息体 ,国际化处
initMessageSource();
// 初始化应用消息广播器,并放入“applicationEventMulticaster”bean中
initApplicationEventMulticaster();
// 留给子类来初始化其它的Bean
onRefresh();
// 在所有注册的bean中查找Listener bean,注册到消息广播器中
registerListeners();
// 初始化剩下的单实例(非惰性的)
finishBeanFactoryInitialization(beanFactory);
// 完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知别人
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
contextRefresh.end();
}
}
(1)prepareRefresh
protected void prepareRefresh() {
// Switch to active.
this.startupDate = System.currentTimeMillis();
this.closed.set(false);
this.active.set(true);
if (logger.isDebugEnabled()) {
if (logger.isTraceEnabled()) {
logger.trace("Refreshing " + this);
}
else {
logger.debug("Refreshing " + getDisplayName());
}
}
// 初始化上下文环境中的任何占位符属性资源
initPropertySources();
//验证需要的属性文件是否都已经放入环境中
getEnvironment().validateRequiredProperties();
// Store pre-refresh ApplicationListeners...
if (this.earlyApplicationListeners == null) {
this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
}
else {
// Reset local application listeners to pre-refresh state.
this.applicationListeners.clear();
this.applicationListeners.addAll(this.earlyApplicationListeners);
}
// Allow for the collection of early ApplicationEvents,
// to be published once the multicaster is available...
this.earlyApplicationEvents = new LinkedHashSet<>();
}
prepareRefresh完成的功能:
- 调用initPropertySources方法初始化资源配置,比如web环境下的application.properties读取
- 验证需要的属性文件是否都已经放入环境中
- 对
earlyApplicationListeners
和earlyApplicationEvents早期事件
进行初始化,为的是当事件广播器applicationEventMulticaster
创建好了之后,才会将早期的事件广播出去
(2) ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory()
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
//为了序列化指定id,如果需要的话,让这个BeanFactory从id反序列化到BeanFactory对象
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
//初始化DodumentReader,并进行XML文件读取及解析
loadBeanDefinitions(beanFactory);
this.beanFactory = beanFactory;
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
obtainFreshBeanFactory主要功能:
- 创建DefaultListableBeanFactory
- 指定序列号id
- 对IOC容器进行定制化,设置允许循环依赖的存在等
- 调入载入bean定义的方法
在初始化了DefaultListableBeanFactory和XmlBeanDefinitionReader后就可以进行配置文件的读取了
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 创建XmlBeanDefinitionReader,即创建Bean读取器,并通过回调设置到容器中去,容器使用该读取器读取Bean定义资源
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// 配置上下文的环境,资源加载器、解析器
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// 允许子类自行初始化(比如校验机制),并提供真正的加载方法
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
读取配置文件,需要首先获得资源对象Resource,spring实际调用DefaultResourceLoader.getResource来获得资源对象
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
for (ProtocolResolver protocolResolver : getProtocolResolvers()) {
Resource resource = protocolResolver.resolve(location, this);
if (resource != null) {
return resource;
}
}
if (location.startsWith("/")) {
//从类路径的方式,获得资源对象
return getResourceByPath(location);
}
else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
}
else {
try {
// Try to parse the location as a URL...
//尝试采用url的方式获取资源对象
URL url = new URL(location);
return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
}
catch (MalformedURLException ex) {
// No URL -> resolve as resource path.
return getResourceByPath(location);
}
}
}
接下来开始调用XmlBeanDefinitionReader.doLoadBeanDefinitions开始加载bean定义
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
//获得xml的文档对象
Document doc = doLoadDocument(inputSource, resource);
//注册bean定义
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
...
}
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//使用DefaultBeanDefinitionDocumentReader实例化BeanDefinitionDocumentReader
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//记录统计前BeanDefinition的加载个数
int countBefore = getRegistry().getBeanDefinitionCount();
加载及注册bean
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
//记录本次加载的BeanDefinition个数
return getRegistry().getBeanDefinitionCount() - countBefore;
}
在DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions开始做真正的解析工作
protected void doRegisterBeanDefinitions(Element root) {
// Any nested <beans> elements will cause recursion in this method. In
// order to propagate and preserve <beans> default-* attributes correctly,
// keep track of the current (parent) delegate, which may be null. Create
// the new (child) delegate with a reference to the parent for fallback purposes,
// then ultimately reset this.delegate back to its original (parent) reference.
// this behavior emulates a stack of delegates without actually necessitating one.
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
//处理profile属性,用于区分生产环境和开发环境,
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
// We cannot use Profiles.of(...) since profile expressions are not supported
// in XML config. See SPR-12458 for details.
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
//解析前处理,留给子类实现
preProcessXml(root);
parseBeanDefinitions(root, this.delegate);
//解析后处理,留给子类实现
postProcessXml(root);
this.delegate = parent;
}
处理了profile后就可以进行XML的读取了,跟踪代码进入parseBeanDefinitions(root, this.delegate)
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
//解析默认的标签
parseDefaultElement(ele, delegate);
}
else {
//解析自定义的标签
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
对于根节点或者子节点如果是默认命名空间的话则采用parseDefaultElement方法进行解析,否则使用delegate.parseCustomElement方法对自定义命名空间进行解析。而判断是否默认命名空间还是自定义命名空间的办法其实是使用node.getNamespaceURI()获取命名空间,并与Spring中固定的命名空间http://www.springframework.org/schema/beans进行比对。如果一致则认为是默认,否则就认为是自定义。
跟踪解析默认标签的代码
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
//解析<import>
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
//解析<alias>
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
//<Bean>,则会解析并注册BeanDefinition
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
跟踪processBeanDefinition函数
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
1. 首先委托BeanDefinitionDelegate类的parseBeanDefinitionElement方法进行元素解析,返回BeanDefinitionHolder类型的实例bdHolder,经过这个方法后,bdHolder实例已经包含我们配置文件中配置的各种属性了,例如class、name、id、alias之类的属性。
2. 当返回的bdHolder不为空的情况下若存在默认标签的子节点下再有自定义属性,还需要再次对自定义标签进行解析。
3. 解析完成后,需要对解析后的bdHolder进行注册,同样,注册操作委托给了Bean- DefinitionReaderUtils的registerBeanDefinition方法。
4. 最后发出响应事件,通知相关的监听器,这个bean已经加载完成了
注册bean到Map<String, BeanDefinition> beanDefinitionMap
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// 使用beanName作为唯一标识,注册到Map<String, BeanDefinition> beanDefinitionMap
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// 注册所有的别名
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}