0%

SpringIOC容器的启动流程(一)

前言:Spring做为JAVA企业级应用中最为热门的框架,为开发人员提供了一系列方便易用的组件,使开发者可以集中于业务逻辑的开发,而不必关心太多底层环境的部署。我第一次接触这个框架是做校内的一个web项目,当时只是简单机械的运用了spring-boot 中的一些功能,正式开始了解框架背后的原理是今年暑假实习。一开始,我被Spring官方晦涩的文档所困住,理不清思绪,跨不出学习的第一步。

稻圣和夫曾经说过,真理之布由一根纱线织成,把事情看得越单纯,就越接近真相,也就越接近真理。一句话概括,Spring是一个轻量级控制反转(IoC)和面向切面(AOP)的容器框架。因此,我们不妨从Spring框架最核心的两个思想IOC,AOP开始,慢慢探寻框架背后的秘密。

本篇博客将以一个简单的Spring应用程序为例,介绍Spring的IOC容器启动流程

IOC容器启动步骤概括

  1. 定位配置文件(xml或configuration Class)
  2. 加载配置文件获取BeanDefinition
  3. 注册BeanDefinition
  4. 实例化Bean

简单的Spring程序

我们首先来看一个简单的HelloWorld程序,调用的我们的Renderer并渲染相应的消息

  • 主程序
    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class HelloWordApplication {
    public static void main(String args[]){
    ApplicationContext ctx= new ClassPathXmlApplicationContext("/app-context.xml");
    MessageRenderer renderer=ctx.getBean("renderer",MessageRenderer.class);
    renderer.renderer();

    }

    }

相关接口和Bean

  • 消息提供方
1
2
3
4
5
6
7
public class HelloWordMessageProvider implements MessageProvider {

@Override
public String getMessage() {
return "HelloWord!";
}
}
1
2
3
4
public interface MessageProvider {
String getMessage();

}
  • 渲染消息方
1
2
3
4
5
6
public interface MessageRenderer {
void renderer();
void setMessageProvider(MessageProvider messageProvider);
MessageProvider getMessageProvider();

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class StandardMessageRenderer implements MessageRenderer {

private MessageProvider messageProvider;

@Override
public void renderer() {
if (messageProvider==null){
throw new RuntimeException("error");
}
System.out.println(messageProvider.getMessage());
}

@Override
public void setMessageProvider(MessageProvider messageProvider) {
this.messageProvider = messageProvider;
}

@Override
public MessageProvider getMessageProvider() {
return this.messageProvider;
}
}

从源码分析IOC容器启动

首先我们来看一看主程序里的这一行

1
ApplicationContext ctx= new ClassPathXmlApplicationContext("/app-context.xml");

短短的一行,确已经完成了IOC容器的启动,我们一会再去探寻这个构造函数到底做了什么,先来看看ApplicationContext究竟是什么
"ApplicationContext"

BeanFactory vs ApplicationContext

BeanFactory

BeanFactory是我们的根容器,Bean的中央注册中心,提供了依赖注入,ApplicationContext继承了BeanFactory,一般我们不会直接操作BeanFactory,而都是直接和ApplicationContext打交道

java doc上给出的BeanFactory的定义

The root interface for accessing a Spring bean container. This is the basic client view of a bean container; further interfaces such as ListableBeanFactory and ConfigurableBeanFactory are available for specific purposes.

这边给出一个例子演示一下如何直接通过BeanFactory操纵我们的Bean MessageRenderer

1
2
3
4
5
6
7
8
9
10
public class XmlConfigWithBeanFactory {
public static void main(String[] args) {
//
DefaultListableBeanFactory factory=new DefaultListableBeanFactory();
XmlBeanDefinitionReader rdr=new XmlBeanDefinitionReader(factory);
rdr.loadBeanDefinitions(new ClassPathResource("/app-context.xml"));
MessageRenderer renderer=factory.getBean("renderer",MessageRenderer.class);
renderer.renderer();
}
}

DefaultListableBeanFactory是实现了ListableBeanFactory接口的一个类
1.初始化factory
2.使用XmlBeanDefinitionReader从Xml文件读取BeanDefinition信息
3.利用我们的factory获取我们定义好的Bean

ApplicationContext

ApplicationContext是BeanFactory的扩展,除了依赖注入外,其还提供了

  1. Easier integration with Spring’s AOP features
  2. Message resource handling (for use in internationalization)
  3. Event publication
  4. Application-layer specific contexts such as the WebApplicationContext for use in web applications.

源码解析

1
ApplicationContext ctx= new ClassPathXmlApplicationContext("/app-context.xml");

ClassPathXmlApplicationContext简单来说就是根据xml文件来获取我们的Bean的定义,并将Bean注册到我们的容器中

根据源码追踪我们会看到

1
2
3
public ClassPathXmlApplicationContext(String... configLocations) throws BeansException {
this(configLocations, true, (ApplicationContext)null);
}

这里就是我们真正的构造函数

1
2
3
4
5
6
7
8
9
10
11
12
public ClassPathXmlApplicationContext(String[] paths, Class<?> clazz, @Nullable ApplicationContext parent) throws BeansException {
super(parent);
Assert.notNull(paths, "Path array must not be null");
Assert.notNull(clazz, "Class argument must not be null");
this.configResources = new Resource[paths.length];

for(int i = 0; i < paths.length; ++i) {
this.configResources[i] = new ClassPathResource(paths[i], clazz);
}

this.refresh();
}
  1. 首先我们将父容器设为null(关于父子容器这块到时候单独研究下)
  2. set我们的BeanDefinition
  3. 调用refresh(),容器启动的真正入口

容器启动的入口: refresh

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
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();
}
}
}

拆解一下,概括一下这个函数做的事情

graph TD

A[prepareRefresh:容器启动前的准备工作]-->B[prepareBeanFactory:容器核心初始化]
B-->C[invokeBeanFactoryPostProcessor:初始化BeanPostProcesor Bean]
C-->D[finishBeanFactoryInitializatio:创建Singleton的Bean]
D-->E[finishRefresh]

后记

就先写到这里吧:>,下一篇写一下SpringBoot启动MVC的流程吧,嗷呜,感觉给自己挖了一个大坑