需求: 使用@autowired
注入一些对象,但发现不可以直接使用@Autowired
,因为方法是static
的,要使用该方法当前对象也必须是static
,正常情况下@Autowired
无法注入静态的bean
,于是发现项目中用到了springContextHolder
,通过使用
private T t= SpringContextHolder.getBean(T.class);
或者我们在刚开始学习的时候,会使用如下这种方式来获取Bean
。但是这样就会存在一个问题,因为它需要重新装载spring-core.xml
文件,并实例化上下文bean
,如果有些线程配置类也是在这个配置文件中,那么会造成做相同工作的的线程会被启两次。一次是 web
容器初始化时启动,另一次是上述代码显示的实例化了一次。这样就产生了冗余。下面就来说说解决方案。
ApplicationContext appContext = new ClassPathXmlApplicationContext("spring-core.xml");
T t = (T)appContext.getBean("t");
2
# 一、SpringContextHolder 工具类
自定义SpringContextHolder
工具类,全网统一模板。需要将该类注入到Spring IOC
中。因此路径很重要。
package com.zzx.utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
/**
* @author
* @date
*/
@Component
@Slf4j
public class SpringContextHolder implements ApplicationContextAware, DisposableBean {
private static ApplicationContext applicationContext = null;
/**
* 取得存储在静态变量中的ApplicationContext.
*/
public static ApplicationContext getApplicationContext() {
assertContextInjected();
return applicationContext;
}
/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
public static <T> T getBean(String name) {
assertContextInjected();
return (T) applicationContext.getBean(name);
}
/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
public static <T> T getBean(Class<T> requiredType) {
assertContextInjected();
return applicationContext.getBean(requiredType);
}
/**
* 检查ApplicationContext不为空.
*/
private static void assertContextInjected() {
if (applicationContext == null) {
throw new IllegalStateException("applicaitonContext属性未注入, 请在applicationContext" +
".xml中定义SpringContextHolder或在SpringBoot启动类中注册SpringContextHolder.");
}
}
/**
* 清除SpringContextHolder中的ApplicationContext为Null.
*/
public static void clearHolder() {
log.debug("清除SpringContextHolder中的ApplicationContext:"
+ applicationContext);
applicationContext = null;
}
@Override
public void destroy() throws Exception {
SpringContextHolder.clearHolder();
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (SpringContextHolder.applicationContext != null) {
log.warn("SpringContextHolder中的ApplicationContext被覆盖, 原有ApplicationContext为:" + SpringContextHolder.applicationContext);
}
SpringContextHolder.applicationContext = applicationContext;
}
}
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
# 二、ApplicationContextAware
接口
Spring
容器初始化的时候会检测容器中的所有Bean
,如果发现某个Bean
实现了ApplicationContextAware
接口,Spring
容器会在创建该Bean
之后,自动调用该Bean
的setApplicationContextAware()
方法,调用该方法时,会将容器本身作为参数传给该方法,该方法将Spring
传入的参数(容器本身)赋给该类对象的applicationContext
实例变量,因此接下来可以通过该applicationContext
实例变量来访问容器本身。
# 三、BeanFactory
BeanFactory
是Spring
的“心脏”。它就是Spring IoC
容器的真面目。Spring
使用BeanFactory
来实例化、配置和管理Bean
。BeanFactory
是IOC
容器的核心接口,它定义了IOC
的基本功能,我们看到它主要定义了getBean
方法。getBean
方法是IOC
容器获取bean
对象和引发依赖注入的起点。方法的功能是返回特定的名称的Bean
。BeanFactory
只能管理单例Singleton Bean
的生命周期。它不能管理原型prototype
非单例Bean
的生命周期。这是因为原型Bean
实例被创建之后便被传给了客户端,容器失去了对它们的引用。
BeanFactory
的源码:
public interface BeanFactory {
// 用来引用一个实例,或把它和工厂产生的Bean区分开,就是说,如果一个FactoryBean的名字为a,那么,&a会得到那个Factory 四个不同形式的getBean方法,获取实例
Object getBean(String var1) throws BeansException;
<T> T getBean(String var1, Class<T> var2) throws BeansException;
// 是否存在
Object getBean(String var1, Object... var2) throws BeansException;
<T> T getBean(Class<T> var1) throws BeansException;
<T> T getBean(Class<T> var1, Object... var2) throws BeansException;
<T> ObjectProvider<T> getBeanProvider(Class<T> var1);
<T> ObjectProvider<T> getBeanProvider(ResolvableType var1);
boolean containsBean(String var1);
// 是否为单实例
boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;
// 是否为原型(多实例)
boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;
// 名称、类型是否匹配
boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException;
// 获取类型
@Nullable
Class<?> getType(String var1) throws NoSuchBeanDefinitionException;
@Nullable
Class<?> getType(String var1, boolean var2) throws NoSuchBeanDefinitionException;
// 根据实例的名字获取实例的别名
String[] getAliases(String var1);
}
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
# 四、XmlBeanFactory
BeanFactory
这是一个典型的工厂模式的工厂接口。BeanFactory
最常见的实现类为XmlBeanFactory
,可以从classpath
或文件系统等获取资源。
File file = new File("fileSystemConfig.xml");
Resource resource = new FileSystemResource(file);
BeanFactory beanFactory = new XmlBeanFactory(resource);
Resource resource = new ClassPathResource("classpath.xml");
BeanFactory beanFactory = new XmlBeanFactory(resource);
2
3
4
5
6
我们通过在applicationContext.xml
中配置:
<bean id="car" class="spring.ioc.demo.Car" p:brand="xiaopeng" p:color="red" p:maxSpeed="520" />
通过XmlBeanFactory
实现启动Spring IoC
容器:
public static void main(String[] args) {
// ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
// Resource res = resolver.getResource("classpath:applicationContext.xml");
// BeanFactory factory = new XmlBeanFactory(res);
ApplicationContext factory=new ClassPathXmlApplicationContext("applicationContext.xml");
Car car = factory.getBean("car", Car.class);
}
2
3
4
5
6
7
8
【1】XmlBeanFactory
通过Resource
装载Spring
配置信息冰启动IoC
容器,然后就可以通过factory.getBean
从IoC
容器中获取Bean
了。
【2】通过BeanFactory
启动IoC
容器时,并不会初始化配置文件中定义的Bean
,初始化动作发生在第一个调用时。
【3】对于单实例singleton
的Bean
来说,BeanFactory
会缓存Bean
实例,所以第二次使用getBean
时直接从IoC
容器缓存中获取Bean
。
# 五、ApplicationContext
如果说BeanFactory
是Spring
的心脏,那么ApplicationContext
就是完整的躯体了,ApplicationContext
由BeanFactory
派生而来,提供了更多面向实际应用的功能。在BeanFactory
中,很多功能需要以编程的方式实现,而在ApplicationContext
中则可以通过配置实现。
BeanFactorty
接口提供了配置框架及基本功能,但是无法支持spring
的aop
功能和web
应用。而ApplicationContext
接口作为BeanFactory
的派生,因而提供BeanFactory
所有的功能。而且ApplicationContext
还在功能上做了扩展,相较于BeanFactorty
,ApplicationContext
还提供了以下的功能:
【1】MessageSource
提供国际化的消息访问;
【2】资源访问,如URL
和文件;
【3】事件传播特性,即支持aop
特性;
【4】载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web
层;
ApplicationContext
:是IOC
容器另一个重要接口, 它继承了BeanFactory
的基本功能, 同时也继承了容器的高级功能,如:MessageSource
(国际化资源接口)、ResourceLoader
(资源加载接口)、ApplicationEventPublisher
(应用事件发布接口)等。
# 六、总结
【1】BeanFactroy
采用的是延迟加载形式来注入Bean
的,即只有在使用到某个Bean
时(调用getBean()
),才对该Bean
进行加载实例化,这样,我们就不能发现一些存在的Spring
的配置问题。而ApplicationContext
则相反,它是在容器启动时,一次性创建了所有的Bean
。这样,在容器启动时,我们就可以发现Spring
中存在的配置错误。 相对于基本的BeanFactory
,ApplicationContext
唯一的不足是占用内存空间。当应用程序配置Bean
较多时,程序启动较慢。
【2】BeanFacotry
延迟加载,如果Bean
的某一个属性没有注入,BeanFacotry
加载后,直至第一次使用调用getBean
方法才会抛出异常;而ApplicationContext
则在初始化自身是检验,这样有利于检查所依赖属性是否注入;所以通常情况下我们选择使用ApplicationContext
。
【3】应用上下文则会在上下文启动后预载入所有的单实例Bean
。通过预载入单实例bean
,确保当你需要的时候,你就不用等待,因为它们已经创建好了。
【4】BeanFactory
和ApplicationContext
都支持eanPostProcessor
、BeanFactoryPostProcessor
的使用,但两者之间的区别是:BeanFactory
需要手动注册,而ApplicationContext
则是自动注册。Applicationcontext
比beanFactory
加入了一些更好使用的功能。而且 beanFactory
的许多功能需要通过编程实现而Applicationcontext
可以通过配置实现。比如后处理bean
,Applicationcontext
直接配置在配置文件即可而beanFactory
这要在代码中显示的写出来才可以被容器识别。
【5】beanFactory
主要是面对与spring
框架的基础设施,面对spring
自己。而Applicationcontex
主要面对与spring
使用的开发者。基本都会使用Applicationcontex
并非beanFactory
。