前言

随着SpringBoot的遍及,Spring的应用也越来越广,在某些场景下,咱们无奈通过注解或配置的模式间接获取到某个Bean。比方,在某一些工具类、设计模式实现中须要应用到Spring容器治理的Bean,此时就须要间接获取到对应的Bean。

本文为大家整顿汇总了常见的获取Bean的形式,并提供一些优劣剖析,不便大家在应用到时有更好的抉择。同时,也会为大家适当的遍及和拓展一些相干常识。

Spring的IoC容器

在Spring中,Bean的实例化、定位、配置应用程序中的对象及建设对象间的依赖关系,都是在IoC容器中进行的。因而,要在Spring中获取Bean,实质上就是从IoC容器当中获取Bean。

在Spring中,BeanFactory是IoC容器的理论代表者,该接口提供了IoC容器最基本功能。同时,Spring还提供了另外一种类型的容器:ApplicationContext容器。

ApplicationContext容器包含BeanFactory容器的所有性能(BeanFactory的子接口),提供了更多面向利用的性能,它提供了国际化反对和框架事件体系,更易于创立理论利用。

个别状况,咱们称BeanFactory为IoC容器,称ApplicationContext为利用上下文。但有时为了不便,也将ApplicationContext称为Spring容器。

通常不倡议应用BeanFactory,但BeanFactory 依然能够用于轻量级的应用程序,如挪动设施或基于applet的应用程序,其中它的数据量和速度是显著。

BeanFactory与ApplicationContext的区别

BeanFactory是Spring框架的基础设施,面向Spring自身。ApplicationContext则面向应用Spring框架的开发者,简直所有的利用场合都能够间接应用ApplicationContext,而非底层的BeanFactory。

另外,ApplicationContext的初始化和BeanFactory有一个重大的区别:

BeanFactory在初始化容器时,并未实例化Bean,直到第一次拜访某个Bean时才实例指标Bean。这样,咱们就不能发现一些存在的Spring的配置问题。如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次应用调用getBean办法才会抛出异样。

而ApplicationContext则在初始化利用上下文时就实例化所有单实例的Bean,绝对应的,ApplicationContext的初始化工夫会比BeanFactory长一些。

理解了上述的根本理论知识之后,咱们就能够尝试从IoC容器当中获取Bean对象了。

形式一:通过BeanFactory获取

通过BeanFactory来获取Bean。基于xml配置文件的时代,能够通过如下形式取得BeanFactory,再通过BeanFactory来取得对应的Bean。

BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));UserInfo userInfo = (UserInfo) beanFactory.getBean("userInfo");

有肯定编程年龄的程序员,应该对此还有一些印象。这种写法预计也只会呈现在古老的我的项目当中。鉴于xml模式配置文件曾经被基于注解模式所代替,同时XmlBeanFactory也被标注为废除。此种形式不举荐应用。

其实,不举荐的理由还有一个,在下面曾经提到,尽量不要应用BeanFactory,而应该应用ApplicationContext。

形式二:通过BeanFactoryAware获取

在下面的形式中,XmlBeanFactory曾经被废除,但能够通过其余形式来取得BeanFactory,而后再从BeanFactory中取得指定的Bean。获取BeanFactory实例最简略的形式就是实现BeanFactoryAware接口。

BeanFactoryAware接口源码:

public interface BeanFactoryAware extends Aware {  /**   * 初始化回调办法,Spring会主动将BeanFactory注入进去,接管之后即可应用BeanFactory   */  void setBeanFactory(BeanFactory beanFactory) throws BeansException;}

BeanFactoryAware属于org.springframework.beans.factory.Aware根标记接口,应用setter注入来在应用程序上下文启动期间获取对象。Aware接口是回调,监听器和观察者设计模式的混合,它示意Bean有资格通过回调形式被Spring容器告诉。

这里提供一个残缺的工具类:

@Componentpublic class BeanFactoryHelper implements BeanFactoryAware {  private static BeanFactory beanFactory;  /**   * 重写 BeanFactoryAware 接口的办法   * @param beanFactory :参数赋值给本地属性之后即可应用 BeanFactory   * @throws BeansException BeansException   */  @Override  public void setBeanFactory(BeanFactory beanFactory) throws BeansException {    BeanFactoryHelper.beanFactory = beanFactory;  }  /**   * 依据名称获取容器中的对象实例   * @param beanName :注入的实例必须曾经存在容器中,否则抛异样:NoSuchBeanDefinitionException   * @return Object   */  public static Object getBean(String beanName) {    return beanFactory.getBean(beanName);  }  /**   * 依据 class 获取容器中的对象实例   * @param requiredType :被注入的必须曾经存在容器中,否则抛异样:NoSuchBeanDefinitionException   * @param <T> Class   * @return 对象   */  public static <T> T getBean(Class<T> requiredType) {    return beanFactory.getBean(requiredType);  }  /**   * 判断 spring 容器中是否蕴含指定名称的对象   * @param beanName bean名称   * @return 是否存在   */  public static boolean containsBean(String beanName) {    return beanFactory.containsBean(beanName);  }  //其它需要皆可参考 BeanFactory 接口和它的实现类}

在上述工具类中,便是基于BeanFactoryAware的个性,取得了BeanFactory,而后再通过BeanFactory来取得指定的Bean。

该计划满足了获取Bean的根本需要,但同时具备应用BeanFactory的毛病。依据前文介绍的BeanFactory个性,可酌情应用。

下面提供了两种基于BeanFactory容器取得Bean的形式,上面则通过ApplicationContext来获取容器中的Bean,不同的是获取ApplicationContext的形式的区别。

形式三:启动获取ApplicationContext

在我的项目启动时先获取ApplicationContext对象,而后将其存储在一个中央,以便后续用到时进行应用。
这里提供两种场景的获取:

基于xml配置bean的模式,实用于比拟古老的我的项目,曾经很少应用了;
基于SpringBoot启动时获取ApplicationContext对象;

基于xml的模式实现:

// 其中applicationContext.xml 为配置容器的xml,不过当初个别很少应用了ApplicationContext ac = new FileSystemXmlApplicationContext("applicationContext.xml");

这里等于间接初始化容器,并且取得容器的援用。这种形式实用于采纳Spring框架的独立应用程序,须要程序通过配置文件手工初始化Spring的状况。目前大多数Spring我的项目曾经不再采纳xml配置,很少应用了。

基于SpringBoot启动实现:

@SpringBootApplicationpublic class ExampleApplication {    public static void main(String[] args) {        // 启动时,保留上下文,并保留为动态        ConfigurableApplicationContext ac = SpringApplication.run(ExampleApplication.class, args);        SpringContextUtil.setAc(ac);    }}

对应的SpringContextUtil类如下:

public class SpringContextUtil1 {    private static ApplicationContext ac;    public static <T>  T getBean(String beanName, Class<T> clazz) {        T bean = ac.getBean(beanName, clazz);        return bean;    }    public static void setAc(ApplicationContext applicationContext){        ac = applicationContext;    }}

两种形式都是在启动Spring我的项目时,间接获取到ApplicationContext的援用,而后将其存储到工具类当中。在应用时,则从工具类中获取ApplicationContext容器,进而从中取得Bean对象。

形式四:通过继承ApplicationObjectSupport

此种形式仍旧是先取得ApplicationContext容器,而后从中获取Bean对象,只不过是基于继承ApplicationObjectSupport类实现的。

具体实现代码:

@Componentpublic class SpringContextUtil extends ApplicationObjectSupport {  public <T> T getBean(Class<T> clazz) {    ApplicationContext ac = getApplicationContext();    if(ac == null){      return null;    }    return ac.getBean(clazz);  }}

留神,这里的SpringContextUtil类须要实例化。

形式五:通过继承WebApplicationObjectSupport

WebApplicationObjectSupport是ApplicationObjectSupport的一个实现类,提供了Web相干的反对。实现原理与ApplicationObjectSupport一样。

具体实现代码如下:

@Componentpublic class SpringContextUtil extends WebApplicationObjectSupport {  public <T> T getBean(Class<T> clazz) {    ApplicationContext ac = getApplicationContext();    if(ac == null){      return null;    }    return ac.getBean(clazz);  }}

对照基于ApplicationObjectSupport的实现,除了继承对象不同外,没有其余区别,都是基于getApplicationContext办法来获取。

形式六:通过WebApplicationContextUtils

Spring提供了工具类WebApplicationContextUtils,通过该类可获取WebApplicationContext对象。
具体实现代码如下:

public class SpringContextUtil2 {  public static <T> T getBean(ServletContext request, String name, Class<T> clazz){    WebApplicationContext webApplicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(request);    // 或者    WebApplicationContext webApplicationContext1 = WebApplicationContextUtils.getWebApplicationContext(request);//        webApplicationContext1.getBean(name, clazz)    T bean = webApplicationContext.getBean(name, clazz);    return bean;  }}

这个办法很常见于SpringMVC构建的Web我的项目中,实用于Web我的项目的B/S构造。

形式七:通过ApplicationContextAware

通过实现ApplicationContextAware接口,在Spring容器启动时将ApplicationContext注入进去,从而获取ApplicationContext对象,这种办法也是常见的获取Bean的一种形式,举荐应用。

具体实现代码如下:

@Componentpublic class SpringContextUtil3 implements ApplicationContextAware {  private static ApplicationContext ac;  @Override  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {    ac = applicationContext;  }  public static <T> T getBean(Class<T> clazz) {    T bean = ac.getBean(clazz);    return bean;  }}

这种形式与后面通过BeanFactoryAware取得BeanFactory的思路统一。

形式八:通过ContextLoader

应用ContextLoader提供的getCurrentWebApplicationContext办法,也是罕用的获取WebApplicationContext的一种办法。

具体实现代码如下:

WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();wac.getBean(beanID);

该办法常见于SpringMVC实现的Web我的项目中。该形式是一种不依赖于Servlet,不须要注入的形式。然而须要留神一点,在服务器启动时和Spring容器初始化时,不能通过该办法获取Spring容器。

形式九:通过BeanFactoryPostProcessor

Spring工具类,不便在非Spring治理环境中获取Bean。

@Componentpublic final class SpringUtils implements BeanFactoryPostProcessor{        /** Spring利用上下文环境 */    private static ConfigurableListableBeanFactory beanFactory;    @Override    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException{        SpringUtilsS.beanFactory = beanFactory;    }    /**     * 获取对象     *     * @param name     * @return Object 一个以所给名字注册的bean的实例     * @throws BeansException     *     */    @SuppressWarnings("unchecked")    public static <T> T getBean(String name) throws BeansException{        return (T) beanFactory.getBean(name);    }    /**     * 获取类型为requiredType的对象     *     * @param clz     * @return     * @throws BeansException     *     */    public static <T> T getBean(Class<T> clz) throws BeansException{        T result = (T) beanFactory.getBean(clz);        return result;    }    /**     * 如果BeanFactory蕴含一个与所给名称匹配的bean定义,则返回true     *     * @param name     * @return boolean     */    public static boolean containsBean(String name){        return beanFactory.containsBean(name);    }    /**     * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异样(NoSuchBeanDefinitionException)     *     * @param name     * @return boolean     * @throws NoSuchBeanDefinitionException     *     */    public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException{        return beanFactory.isSingleton(name);    }    /**     * @param name     * @return Class 注册对象的类型     * @throws NoSuchBeanDefinitionException     *     */    public static Class<?> getType(String name) throws NoSuchBeanDefinitionException{        return beanFactory.getType(name);    }    /**     * 如果给定的bean名字在bean定义中有别名,则返回这些别名     *     * @param name     * @return     * @throws NoSuchBeanDefinitionException     *     */    public static String[] getAliases(String name) throws NoSuchBeanDefinitionException{        return beanFactory.getAliases(name);    }    /**     * 获取aop代理对象     *      * @param invoker     * @return     */    @SuppressWarnings("unchecked")    public static <T> T getAopProxy(T invoker){        return (T) AopContext.currentProxy();    }}

其中ConfigurableListableBeanFactory接口,也属于BeanFactory的子接口。

小结

在本文中介绍了9种从Spring容器中获取Bean的办法,尽管每种形式实现各有不同,但从实质上来讲,无非就是通过BeanFactory或ApplicationContext获取Bean,只不过获取BeanFactory或ApplicationContext容器的形式不同而已。

那么,你是否意识到,学习一项技术或一个实现形式,只有把握住它的基本,无论模式如何变动,都万变不离其宗。而这里“宗”就是IoC容器。