在Spring中容器的实现类并不是唯一的,Spring框架中提供了多个容器的实现。主要分为两套体系:一套是早期的BeanFactory体系,还有一个就是ApplicationContext,也被成为服务上下文,它继承了BeanFactory,除了BeanFactory的功能外还提供了事务,AOP,国际化的消息源以及应用程序事务处理等企业级服务。
1.BeanFactory
首先我们来看一下BeanFacotry源码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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90public interface BeanFactory {
/**
* 主要是用于FactoryBean的转义进行定义,因为当使用FactoryBean创建Bean时,使用名字进行检索获取的是FactoryBean所创建的Bean对象
* 而需要使用转义来获取FactoryBean本身。
*/
String FACTORY_BEAN_PREFIX = "&";
/**
* 根据bean的名称获取容器中的实例,这个bean可以是单例bean也可以是原型bean。
* 当无法获取指定名称的bean将会抛出NoSuchBeanDefinitionException异常。
*/
Object getBean(String name) throws BeansException;
/**
* 根据bean的名称和bean的类型来获取容器中的bean实例,这个bean可以是单例bean也可以是原型bean。
* 当获取的bean不是所需类型,则抛出BeanNotOfRequiredTypeException异常。可以简单理解成增加了安全验证机制。
*/
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
/**
* 根据名称来获取bean,第二个参数args可以用来给bean进行赋值。复制的方式有两种,构造方法和工厂方法。
* 但是通过这种方式获取的bean必须为原型bean而不是单例bean。
*/
Object getBean(String name, Object... args) throws BeansException;
/**
* 根据bean类型来获取容器中的bean,当bean不唯一是抛出NoUniqueBeanDefinitionException异常。
* 当没有获得对应bean时抛出NoSuchBeanDefinitionException异常。
*/
<T> T getBean(Class<T> requiredType) throws BeansException;
/**
* 该方法与getBean(String name, Class<T> requiredType)方法类似
*/
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
/**
* 获取指定bean的提供者,可以简单理解为,假设一个bean是通过FactoryBean生成,此处将返回创建该bean的FactoryBean。
* 调用这个方法返回的是一个对象的实例。此接口通常用于封装一个泛型工厂,在每次调用的时候返回一些目标对象新的实例。
* ObjectFactory和FactoryBean是类似的,只不过FactoryBean通常被定义为BeanFactory中的服务提供者(SPI)实例,
* 而ObjectFactory通常是以API的形式提供给其他的bean。简单的来说,ObjectFactory一般是提供给开发者使用的,
* FactoryBean一般是提供给BeanFactory使用的。
*/
<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
/**
* 同上
*/
<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
/**
* 判断当前容器是否存在某种名称的bean
*/
boolean containsBean(String name);
/**
* 判断bean是否为单例bean
*/
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
/**
* 判断bean是否为原型bean
*/
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
/**
* 根据bean名称,判断是否有指定类型匹配
*/
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
/**
* 同上
*/
boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
/**
* 获取bean的类型,对于FactoryBean,返回Factory.getObjectType()返回的类型。
*/
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
/**
* 返回给定bean的别名(如果有的话),在getBean调用中,所有这些别名都指向同一个bean。
* 如果给定的名称是别名,则返回相应的原始bean名称和其他别名。
*/
String[] getAliases(String name);
}
2、ApplicationContext
ApplicationContext为BeanFactory的子类,它不仅包含BeanFactory的所有功能,还对其进行了扩展。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
38public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
/**
* 返回当前应用程序上下文的唯一ID
*/
String getId();
/**
* 返回当前应用程序上下文所属应用程序的名称
*/
String getApplicationName();
/**
* 获取当前应用程序上下文的具象化类名
*/
String getDisplayName();
/**
* 获取当前应用上下文第一次加载时的时间戳
*/
long getStartupDate();
/**
* 获取父级应用程序上下文,如果没有则返回null
*/
ApplicationContext getParent();
/**
* 通过getAutowireCapableBeanFactory这个方法将 AutowireCapableBeanFactory这个接口暴露给外部使用,
* AutowireCapableBeanFactory这个接口一般在applicationContext的内部是较少使用的,
* 它的功能主要是为了装配applicationContext管理之外的Bean。
*/
AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
}
ApplicationContext本身实现的方法较少,也比较简单,但是他通过继承六个接口来实现了Bean相关的方法等常用功能。
ApplicationContext有多种实现类,其中最重要的两类是ConfigurableApplicationContext和WebApplicationContext。
2.1、ConfigurableApplicationContext
1 | public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable { |
2.2、WebApplicationContext
WebApplicationCOntext是专门为Web应用所准备的,其允许从相对于Web根目录的路径中加载配置文件完成初始化。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
52public interface WebApplicationContext extends ApplicationContext {
/**
* 整个Web应用上下文是作为属性放置在ServletContext中的,该常量就是应用上下文在ServletContext属性列表中的key
*/
String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
/**
* request作用域,只要发出一个请求就会创建一个request,它的作用域:尽在当前请求中有效。
* 用处:常用于服务器间同一请求不同页面之间的参数传递,常应用于表单的控件值传递。
* 方法:request.setAttribute(); request.getAttribute(); request.removeAttribute(); request.getParameter().
*/
String SCOPE_REQUEST = "request";
/**
* 服务器会为每个会话创建一个session对象,所以session中的数据可供当前会话中所有servlet共享。
* 会话:用户打开浏览器会话开始,直到关闭浏览器会话才会结束。一次会话期间只会创建一个session对象。
* 用处:常用于web开发中的登陆验证界面(当用户登录成功后浏览器分配其一个session键值对)。
* 方法:session.setAttribute(); session.getAttribute(); session.removeAttribute();
*/
String SCOPE_SESSION = "session";
/**
* 作用范围:所有的用户都可以取得此信息,此信息在整个服务器上被保留。Application属性范围值,只要设置一次,则所有的网页窗口都可以取得数据。
* ServletContext在服务器启动时创建,在服务器关闭时销毁,一个JavaWeb应用只创建一个ServletContext对象,
* 所有的客户端在访问服务器时都共享同一个ServletContext对象;ServletContext对象一般用于在多个客户端间共享数据时使用;
*/
String SCOPE_APPLICATION = "application";
/**
* 在工厂中的bean名称
*/
String SERVLET_CONTEXT_BEAN_NAME = "servletContext";
/**
* ServletContext 初始化参数名称
*/
String CONTEXT_PARAMETERS_BEAN_NAME = "contextParameters";
/**
* 在工厂中 ServletContext 属性值环境bean的名称
*/
String CONTEXT_ATTRIBUTES_BEAN_NAME = "contextAttributes";
/**
* 用来获取 ServletContext 对象
*/
ServletContext getServletContext();
}
3、ApplicationContext和BeanFactory对比
1.ApplicationContext包含BeanFactory的所有特性,所以一般情况下我们都选择使用ApplicationContext,但是在一些限制情况下,如对于内存消耗要求比较严格的时候使用轻量的BeanFactory是更合理的选择。
2.在BeanFactory采用延迟加载的形式来注入Bean。只有使用到某个Bean是,才会对Bean进行加载实例化。而ApplicationContext则相反,它在容器启动时,一次性创建了所有Bean。这样我们就能在容器启动时发现Spring存在的配置错误。
3.BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。
功能/特点 | BeanFactory | ApplicationContext |
---|---|---|
Bean加载方式 | 延迟加载 | 容器启动时加载所有Bean |
Bean 实例化/装配 | 有 | 有 |
BeanPostProcessor 自动注册 | 没有 | 有 |
BeanFactoryPostProcessor 自动注册 | 没有 | 有 |
MessageSource 便捷访问(针对i18n) | 没有 | 有 |
ApplicationEvent 发布 | 没有 | 有 |
3、ApplicationContext准备启动
ps:ContextLoaderListener简介https://www.cnblogs.com/xunyi/p/10363290.html
在容器启动时会调用ContextLoaderListener中的contextInitialized()方法
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
27public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
public ContextLoaderListener() {
}
public ContextLoaderListener(WebApplicationContext context) {
super(context);
}
/**
* 初始化根web应用程序上下文
*/
public void contextInitialized(ServletContextEvent event) {
// 调用父类ContextLoader的方法进行web容器初始化
initWebApplicationContext(event.getServletContext());
}
/**
* 关闭根web应用程序上下文
*/
public void contextDestroyed(ServletContextEvent event) {
closeWebApplicationContext(event.getServletContext());
ContextCleanupListener.cleanupAttributes(event.getServletContext());
}
}
接下来我们查看父类的initWebApplicationListener1
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
36public class ContextLoader {
// ...
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
// ..
try {
// 将上下文存储在本地实例变量中,以确保在ServletContext关闭时可用。
if (this.context == null) {
// 通过 createWebApplicationContext 方法创建应用上下文
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
if (!cwac.isActive()) {
...
// 在该方法中调用上下文的 refresh 方法,refresh 就是启动上下文的入口
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
...
}
...
}
...
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
...
wac.refresh();
}
...
}
使用SpringBoot启动ApplicationContext时
首先我们可以看SpringBoot的启动类1
2
3
4
5
6
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
点击进入SpringApplication的run方法我们可以得知1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20public class SpringApplication {
public ConfigurableApplicationContext run(String... args) {
// ...
ConfigurableApplicationContext context = null;
try {
// ...
// 创建一个应用上下文
context = createApplicationContext();
// ...
// 进入refreshContext方法可以发现实际是调用refresh启动上下文方法
refreshContext(context);
// ...
}
catch (Throwable ex) {
// ...
}
// ...
return context;
}
}
通过对ContextLoaderListener和SpringApplication两个类的分析我们可以发现,实际最终他们都调用了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
64public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 预刷新,做一些准备工作。记录了启动时间戳,标记为活动,非关闭状态,并处理配置文件中的占位符
prepareRefresh();
// 解析配置文件,创建BeanFactory,对Bean进行包装成BeanDefinition
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 对BeanFactory进行增强处理,比如添加BeanPostProcessor,手动注册部分bean等。
prepareBeanFactory(beanFactory);
try {
// 钩子方法,BeanFactory创建后,对BeanFactory的自定义操作。
postProcessBeanFactory(beanFactory);
// 重点:调用了postProcessBeanDefinitionRegistry(registry);
// springboot中很多激活自动配置的注解都是通过这里导入的。
invokeBeanFactoryPostProcessors(beanFactory);
// 重点:从beanFactory中获取所有的BeanPostProcessor,优先进行getBean操作,实例化
registerBeanPostProcessors(beanFactory);
// 国际化支持
initMessageSource();
// 初始化ApplicationEventMulticaster。 如果上下文中未定义,则使用SimpleApplicationEventMulticaster。
// 这里是监听器的支持,监听器时间多播。
initApplicationEventMulticaster();
// 钩子方法,springBoot中的嵌入式tomcat就是通过此方法实现的
onRefresh();
// 监听器注册
registerListeners();
// 重点方法:完成容器中bean的实例化,及代理的生成等操作。
finishBeanFactoryInitialization(beanFactory);
// 完成此上下文的刷新,调用LifecycleProcessor的onRefresh()方法并发布
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();
}
}
}
总结: 容器是Spring中最为重要的概念之一,也是Spring大厦的基石。通过总结我们可以发现,Spring Bean的自动注入大大简化了操作,下一步我们将分析,Spring是如何通过Xml文件或者注解的方式将我们需要的Bean放入容器中的。