一切从BeanFactory开始
spring的helloWorld
<bean id="helloBean" class="com.HelloWorld"></bean>
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
HelloWorld obj = (HelloWorld) ac.getBean("helloBean");
obj.printHello();
几行代码完成了SpringBeanFactory加载和生成Bean的过程,在ClassPathXmlApplicationContext里面右击鼠标,点击Diagrams->show Diagrams(IDEA),就如下图所示
Object getBean(String name) throws BeansException;
前面的ac.getBean其实就是对BeanFactory接口的实现,所以说,ClassPathXmlApplicationContext就是一个BeanFactory,也就是一个IOC容器.
IOC容器的创建
helloWorld很简单,背后也蕴涵着spring的诸多内容。为此我们有两个疑问
- 容器初始化过程
- getBean的时候如何加载
从xml到Java Bean 第一步肯定是解析xml,通过debug来看下源码
- 构造器
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
- 实际执行
// parent=null,configLocations={"applicationContext.xml"},refresh=true
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
super(parent):设置好 Bean 资源加载器
- super(parent)最终执行如下代码(在AbstractApplicationContext.class):
/**
* Create a new AbstractApplicationContext with no parent.
*/
public AbstractApplicationContext() {
this.resourcePatternResolver = getResourcePatternResolver();
}
- getResourcePatternResolver如下
protected ResourcePatternResolver getResourcePatternResolver() {
return new PathMatchingResourcePatternResolver(this);
}
- new PathMatchingResourcePatternResolver(this)如下
public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {
Assert.notNull(resourceLoader, "ResourceLoader must not be null");
this.resourceLoader = resourceLoader;
}
所以this.resourceLoader是指向ClassPathXmlApplicationContext的实例.
- 调试折回到 AbstractApplicationContext.class类的时候,处理了parent :
setParent(parent);
setConfigLocations(configLocations):设置 Bean
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++) {
this.configLocations[i] = resolvePath(locations[i]).trim();
}
}
else {
this.configLocations = null;
}
}
这个图就这么一句核心this.configLocations[i] = resolvePath(locations[i]).trim()
- this.configLocations[i] = resolvePath(locations[i]).trim()
protected String resolvePath(String path) {
return getEnvironment().resolveRequiredPlaceholders(path);
}
- getEnvironment()
public ConfigurableEnvironment getEnvironment() {
if (this.environment == null) {
this.environment = createEnvironment();
}
return this.environment;
}
protected ConfigurableEnvironment createEnvironment() {
return new StandardEnvironment();
}
- resolveRequiredPlaceholders(path)
public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
return this.propertyResolver.resolveRequiredPlaceholders(text);
}
- resolveRequiredPlaceholders(text)如下;参数text="application.xml"
public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
if (this.strictHelper == null) {
this.strictHelper = createPlaceholderHelper(false);
}
return doResolvePlaceholders(text, this.strictHelper);
}
第一次加载strictHelper是为null的,初始化它需要以下参数
public static final String PLACEHOLDER_PREFIX = "${";
public static final String PLACEHOLDER_SUFFIX = "}";
public static final String VALUE_SEPARATOR = ":";
- doResolvePlaceholders(text, this.strictHelper);
private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
return helper.replacePlaceholders(text, this::getPropertyAsRawString);
}
由于我传递的参数是"application.mxl" ,并没有用到 "${" ,所以在解析占位符的代码中直接return了,并没有进行解析,也就是返回了"application.mxl"
protected String parseStringValue(
String value, PlaceholderResolver placeholderResolver, Set<String> visitedPlaceholders) {
StringBuilder result = new StringBuilder(value);
int startIndex = value.indexOf(this.placeholderPrefix);
while (startIndex != -1) {
...省略解析代码
}
else {
startIndex = -1;
}
}
return result.toString();
}
结果就是 this.configLocations[0]="application.xml" ,
refresh()
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
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();
}
}
}
refresh 的作用类似于对 IOC 容器的重启, 在新建立好的容器中对容器进行初始化,对 BeanDefinition资源进行载入,由于篇幅内容较长,所以在下一篇讲解