0


Spring获取Bean的9种方式

.markdown-body { line-height: 1.75; font-weight: 400; font-size: 16px; overflow-x: hidden; color: rgba(37, 41, 51, 1) }
.markdown-body h1, .markdown-body h2, .markdown-body h3, .markdown-body h4, .markdown-body h5, .markdown-body h6 { line-height: 1.5; margin-top: 35px; margin-bottom: 10px; padding-bottom: 5px }
.markdown-body h1 { font-size: 24px; line-height: 38px; margin-bottom: 5px }
.markdown-body h2 { font-size: 22px; line-height: 34px; padding-bottom: 12px; border-bottom: 1px solid rgba(236, 236, 236, 1) }
.markdown-body h3 { font-size: 20px; line-height: 28px }
.markdown-body h4 { font-size: 18px; line-height: 26px }
.markdown-body h5 { font-size: 17px; line-height: 24px }
.markdown-body h6 { font-size: 16px; line-height: 24px }
.markdown-body p { line-height: inherit; margin-top: 22px; margin-bottom: 22px }
.markdown-body img { max-width: 100% }
.markdown-body hr { border-top: 1px solid rgba(221, 221, 221, 1); border-right: none; border-bottom: none; border-left: none; margin-top: 32px; margin-bottom: 32px }
.markdown-body code { border-radius: 2px; overflow-x: auto; background-color: rgba(255, 245, 245, 1); color: rgba(255, 80, 44, 1); font-size: 0.87em; padding: 0.065em 0.4em }
.markdown-body code, .markdown-body pre { font-family: Menlo, Monaco, Consolas, Courier New, monospace }
.markdown-body pre { overflow: auto; position: relative; line-height: 1.75 }
.markdown-body pre>code { font-size: 12px; padding: 15px 12px; margin: 0; word-break: normal; display: block; overflow-x: auto; color: rgba(51, 51, 51, 1); background: rgba(248, 248, 248, 1) }
.markdown-body a { text-decoration: none; color: rgba(2, 105, 200, 1); border-bottom: 1px solid rgba(209, 233, 255, 1) }
.markdown-body a:active, .markdown-body a:hover { color: rgba(39, 91, 140, 1) }
.markdown-body table { display: inline-block !important; font-size: 12px; width: auto; max-width: 100%; overflow: auto; border: 1px solid rgba(246, 246, 246, 1) }
.markdown-body thead { background: rgba(246, 246, 246, 1); color: rgba(0, 0, 0, 1); text-align: left }
.markdown-body tr:nth-child(2n) { background-color: rgba(252, 252, 252, 1) }
.markdown-body td, .markdown-body th { padding: 12px 7px; line-height: 24px }
.markdown-body td { min-width: 120px }
.markdown-body blockquote { color: rgba(102, 102, 102, 1); padding: 1px 23px; margin: 22px 0; border-left: 4px solid rgba(203, 203, 203, 1); background-color: rgba(248, 248, 248, 1) }
.markdown-body blockquote:after { display: block; content: "" }
.markdown-body blockquote>p { margin: 10px 0 }
.markdown-body ol, .markdown-body ul { padding-left: 28px }
.markdown-body ol li, .markdown-body ul li { margin-bottom: 0 }
.markdown-body ol li .task-list-item, .markdown-body ul li .task-list-item { list-style: none }
.markdown-body ol li .task-list-item ol, .markdown-body ol li .task-list-item ul, .markdown-body ul li .task-list-item ol, .markdown-body ul li .task-list-item ul { margin-top: 0 }
.markdown-body ol ol, .markdown-body ol ul, .markdown-body ul ol, .markdown-body ul ul { margin-top: 3px }
.markdown-body ol li { padding-left: 6px }
.markdown-body .contains-task-list { padding-left: 0 }
.markdown-body .task-list-item { list-style: none }
@media (max-width: 720px) { .markdown-body h1 { font-size: 24px } .markdown-body h2 { font-size: 20px } .markdown-body h3 { font-size: 18px } }

前言

随着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。

BeanFactorybeanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
UserInfo userInfo = (UserInfo) beanFactory.getBean("userInfo");复制代码

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

其实,不推荐的理由还有一个,在上面已经提到,尽量不要使用BeanFactory,而应该使用ApplicationContext。

方式二:通过BeanFactoryAware获取

在上面的方式中,XmlBeanFactory已经被废弃,但可以通过其他方式来获得BeanFactory,然后再从BeanFactory中获得指定的Bean。获取BeanFactory实例最简单的方式就是实现BeanFactoryAware接口。

BeanFactoryAware接口源码:

publicinterfaceBeanFactoryAwareextendsAware {
​
  /**
   * 初始化回调方法,Spring会自动将BeanFactory注入进去,接收之后即可使用BeanFactory
   */voidsetBeanFactory(BeanFactory beanFactory)throws BeansException;
}
复制代码

BeanFactoryAware属于

org.springframework.beans.factory.Aware

根标记接口,使用setter注入来在应用程序上下文启动期间获取对象。Aware接口是回调,监听器和观察者设计模式的混合,它表示Bean有资格通过回调方式被Spring容器通知。

这里提供一个完整的工具类:

@ComponentpublicclassBeanFactoryHelperimplementsBeanFactoryAware {
​
  privatestaticBeanFactory beanFactory;
​
  /**
   * 重写 BeanFactoryAware 接口的方法
   * @param beanFactory :参数赋值给本地属性之后即可使用 BeanFactory
   * @throws BeansException BeansException
   */@OverridepublicvoidsetBeanFactory(BeanFactory beanFactory) throws BeansException {
    BeanFactoryHelper.beanFactory = beanFactory;
  }
  /**
   * 根据名称获取容器中的对象实例
   * @param beanName :注入的实例必须已经存在容器中,否则抛异常:NoSuchBeanDefinitionException
   * @returnObject
   */publicstaticObjectgetBean(String beanName) {
    return beanFactory.getBean(beanName);
  }
  /**
   * 根据 class 获取容器中的对象实例
   * @param requiredType :被注入的必须已经存在容器中,否则抛异常:NoSuchBeanDefinitionException
   * @param <T> Class
   * @return 对象
   */publicstatic <T> T getBean(Class<T> requiredType) {
    return beanFactory.getBean(requiredType);
  }
  /**
   * 判断 spring 容器中是否包含指定名称的对象
   * @param beanName bean名称
   * @return 是否存在
   */publicstaticbooleancontainsBean(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,不过现在一般很少使用了
ApplicationContextac = new FileSystemXmlApplicationContext("applicationContext.xml");复制代码

这里等于直接初始化容器,并且获得容器的引用。这种方式适用于采用Spring框架的独立应用程序,需要程序通过配置文件手工初始化Spring的情况。目前大多数Spring项目已经不再采用xml配置,很少使用了。

基于SpringBoot启动实现:

@SpringBootApplicationpublicclassExampleApplication {
​
    publicstaticvoidmain(String[] args) {
        // 启动时,保存上下文,并保存为静态
        ConfigurableApplicationContext ac = SpringApplication.run(ExampleApplication.class, args);
        SpringContextUtil.setAc(ac);
    }
}
复制代码

对应的SpringContextUtil类如下:

publicclassSpringContextUtil1 {
​
    privatestaticApplicationContext ac;
​
    publicstatic <T>  T getBean(String beanName, Class<T> clazz) {
        T bean = ac.getBean(beanName, clazz);
        return bean;
    }
​
    publicstaticvoidsetAc(ApplicationContext applicationContext){
        ac = applicationContext;
    }
}
复制代码

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

方式四:通过继承ApplicationObjectSupport

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

具体实现代码:

@Component
public classSpringContextUtilextendsApplicationObjectSupport{
  public <T> T getBean(Class<T> clazz) {
    ApplicationContext ac = getApplicationContext();
    if(ac == null){
      returnnull;
    }
    return ac.getBean(clazz);
  }
}
复制代码

注意,这里的SpringContextUtil类需要实例化。

方式五:通过继承WebApplicationObjectSupport

WebApplicationObjectSupport是ApplicationObjectSupport的一个实现类,提供了Web相关的支持。实现原理与ApplicationObjectSupport一样。

具体实现代码如下:

@Component
public classSpringContextUtilextendsWebApplicationObjectSupport{
  public <T> T getBean(Class<T> clazz) {
    ApplicationContext ac = getApplicationContext();
    if(ac == null){
      returnnull;
    }
    return ac.getBean(clazz);
  }
}
复制代码

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

方式六:通过WebApplicationContextUtils

Spring提供了工具类WebApplicationContextUtils,通过该类可获取WebApplicationContext对象。

具体实现代码如下:

publicclassSpringContextUtil2 {
  publicstatic <T> T getBean(ServletContext request, String name, Class<T> clazz){
    WebApplicationContextwebApplicationContext= WebApplicationContextUtils.getRequiredWebApplicationContext(request);
    // 或者WebApplicationContextwebApplicationContext1= WebApplicationContextUtils.getWebApplicationContext(request);
//        webApplicationContext1.getBean(name, clazz)Tbean= webApplicationContext.getBean(name, clazz);
    return bean;
  }
}
复制代码

这个方法很常见于SpringMVC构建的Web项目中,适用于Web项目的B/S结构。

方式七:通过ApplicationContextAware

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

具体实现代码如下:

@ComponentpublicclassSpringContextUtil3implementsApplicationContextAware {
​
  privatestatic ApplicationContext ac;
​
  @OverridepublicvoidsetApplicationContext(ApplicationContext applicationContext)throws BeansException {
    ac = applicationContext;
  }
​
  publicstatic <T> T getBean(Class<T> clazz) {
    Tbean= ac.getBean(clazz);
    return bean;
  }
​
}
复制代码

这种方式与前面通过BeanFactoryAware获得BeanFactory的思路一致。

方式八:通过ContextLoader

使用ContextLoader提供的getCurrentWebApplicationContext方法,也是常用的获取WebApplicationContext的一种方法。

具体实现代码如下:

WebApplicationContextwac = ContextLoader.getCurrentWebApplicationContext();
wac.getBean(beanID);复制代码

该方法常见于SpringMVC实现的Web项目中。该方式是一种不依赖于Servlet,不需要注入的方式。但是需要注意一点,在服务器启动时和Spring容器初始化时,不能通过该方法获取Spring容器。

方式九:通过BeanFactoryPostProcessor

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

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

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

小结

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

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

来源:https://juejin.cn/post/7187939675707801637

标签: 前端

本文转载自: https://blog.csdn.net/china_coding/article/details/128860192
版权归原作者 程序员小明1024 所有, 如有侵权,请联系我们删除。

“Spring获取Bean的9种方式”的评论:

还没有评论