ApplicationContextAware 类
优质博文:IT-BLOG-CN
需求: 使用@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");
一、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;
}
}
二、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);
}
四、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);
我们通过在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);
}
【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
。