优质博文: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;}@Overridepublic void destroy() throws Exception {SpringContextHolder.clearHolder();}@Overridepublic 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;// 获取类型@NullableClass<?> getType(String var1) throws NoSuchBeanDefinitionException;@NullableClass<?> 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。