您的位置:首页 > 编程语言 > Java开发

Spring源码学习第二节

2017-05-16 10:36 218 查看
IOC容器初始化过程

发起是上一节课,的refresh()来启动的。启动会进行,BeanDefinition的Resouce定位、载入和注册三个基本过程。(spring把三个过程分开了,在不同的模块完成,然后组合起来,这样就可以更加灵活的剪裁和扩展)。

1.Resource定位,它由ResourceLoader通过统一的Resource接口完成。

具体实现在上一节写过代码。获得配置文件的resource,通过reader将resource和factory结合。这样提现出applicationContext的好处了。有具体的FileSystemXmlApplicationContext这就是读取系统文件等。

由于已经继承了AbstractXmlApplicationContext类(最上层是Loader),所以具备了resourceLoader读入BeanDefinition的能力。


FileSystemXmlApplicationContext的构造器允许传入string (Resource的文件路径)或数组(多个),已经其他IOC。refresh参数,是指定是否刷新将beanDefinition载入。

protected Resource **getResourceByPath**(String path) {
if (path != null && path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}


这个方法是在 BeanDefinitionReader类的loadBeanDefinition方法(使用的是模板模式,具体实现是由子类来完成的,也就是这里的getResourceByPath方法实现的)中调用的。

那么FileSystemXmlApplicationContext类是在父类AbstractRefreshableAppicationContext类中,定义BeanDefinitionReader从而完成bean读入。

AbstractRefreshableAppicationContext的 refreshBeanFactory方法。它被FileSystemXmlApplicationContext构造方法中refresh()调用。

protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {                                           //如果已经建立了BeanFactory,则销毁并关闭beanFactory
destroyBeans();
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();  //通过此方法构建了一个ioc容器来供applicationContext使用
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);    //载入BeanDefinition 启动
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}


createBeanFactory,getInternalParentBeanFactory是在AbstractAppicationContext实现获取双亲的IOC容器。

protected DefaultListableBeanFactory createBeanFactory() {
return new DefaultListableBeanFactory(getInternalParentBeanFactory());
}


BeanDefinitionReader是顶层父类,重载了很多loadBeanDefinitions方法。实现是在AbstractBeanDefinitionReader类。但是最终调用的还是一个方法。

public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
ResourceLoader resourceLoader = getResourceLoader();   //使用的是DefaultResourceLoader
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
}
//这里对Resource的路径解析。比如设定的各种ant格式的路径定义,得到需要的resource集合
if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.
try {
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
int loadCount = loadBeanDefinitions(resources);
if (actualResources != null) {
for (Resource resource : resources) {
actualResources.add(resource);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
}
return loadCount;
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]", ex);
}
}
else {
// 同样是完成resource定位
Resource resource = resourceLoader.getResource(location);
int loadCount = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
}
return loadCount;
}
}
getResource方法  看DefaultResourceLoader是如何实现的。
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
if (location.startsWith("/")) {
return getResourceByPath(location);
}   //如果路径带有classpath,进行下面的处理
else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
return new ClassPathResou
4000
rce(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
}
else {
try {
// Try to parse the location as a URL...   处理URL定位
URL url = new URL(location);
return new UrlResource(url);
}
catch (MalformedURLException ex) {
// No URL -> resolve as resource path.   如果都不是就交给子类实现 getResourceByPath方法  我们这里就是FileSystemXXX.实现
return getResourceByPath(location);
}}}


2.BeanDefinition的载入,将用户定义好的bean表示成IOC的数据结构(BeanDefinition)。

通过HashMap来维护的。

以DefaultListableBeanFactory为例,看IOC如何载入。

IOC初始化是调用refresh(),该方法是在父类,AbstractAppicationContext类中实现的。其中包含:BeanFactory更新、MessageSource和PostProcessor的注册。

public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();

// 子类中启动refreshBeanFactory()的地方
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);

try {
// 设置BeanFactory的后置处理
postProcessBeanFactory(beanFactory);

// 调用BeanFactory的后处理器,这些后处理器是在Bean定义中向容器注册
invokeBeanFactoryPostProcessors(beanFactory);

// 注册Bean的后处理器,在Bean创建过程中调用
registerBeanPostProcessors(beanFactory);

// Initialize message source for this context.
initMessageSource();

// Initialize event multicaster(机制) for this context.
initApplicationEventMulticaster();

// Initialize other special beans in specific context subclasses.初始化其他的特殊bean
onRefresh();

// Check for listener beans and register them.
registerListeners();

// Instantiate all remaining (non-lazy-init) singletons. 初始化所有的单件
finishBeanFactoryInitialization(beanFactory);

// Last step: publish corresponding event.  发布容器事件,结束refresh过程
finishRefresh();
}

catch (BeansException ex) {
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;
}
}
}


之后看,AbstractRefreshableApplicationContext的refreshBeanFactory()方法(该方法在前面提到过)

protected final void refreshBeanFactory() throws BeansException {
//是否已经存在IOC容器,如果存在销毁关闭。保证refresh之后是新的,更新bean
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
//创建IOC容器
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
//启动对BeanDefinition的载入
loadBeanDefinitions(beanFactory);  //看参数,下面介绍存在一个重载方法,才是真正使用的loadBeanDefinition
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}


上面讲的loadBeanDefinitions方法,是在AbstractXmlApplicationContext中实现,(存在2个方法,重载,另一个),初始化了读取器XmlBeanDefinitionReader。

该方法就是实现了reader(beanfactory),然后将beanDefinition设置到Factory

最终还是reader获取了resource然后进行一个配置的。所以最后的载入还是看Reader

AbstractBeanDefinitionReader ,已经为BeanDefinition做好了准备。我们调用的就是这个类的loadBeanDefinitions方法。

public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {  //这里是resource...,是多个文件。
Assert.notNull(resources, "Resource array must not be null");
int counter = 0;
for (Resource resource : resources) {
//这里的load方法,就是后面我们在子类重写的方法
counter += loadBeanDefinitions(resource);
}
return counter;
}


具体的读入方式,看子类。比如XmlBeanDefinitionReader。就继承了上面的Reader。并重写了方法。

public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}


继续调用的就是读入xml的流然后进行处理了。之后是由BeanDefinitionParserDelegate完成。

之后就是如何读取xml然后进行处理的流程。如何按照spring的Bean语义要求进行解析转化成内部数据结构,代码较多。这我放在下一节中

3.向IOC容器注册BeanDefinition。调用BeanDefinitionRegistry接口来实现的。注入到一个Map中,进行管理。

不包含bean依赖注入的实现。它与载入是两个独立的过程。

依赖注入一般发生在第一次通过getBean获取Bean,或者设置lazyinit属性。(是否想起了hibernate的懒加载?)

此时BeanDefinition信息以及在容器简历自己的数据结构,和表示。但还不能在容器直接使用,需要注册。

在DefaultListableBeanFactory中,定义了MAP

/* Map of bean definition objects, keyed by bean name /

private final Map
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: