Spring容器获取Bean的9种方式 | 京东云技术团队

这篇具有很好参考价值的文章主要介绍了Spring容器获取Bean的9种方式 | 京东云技术团队。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1 前言

随着SpringBoot的普及,Spring的使用也越来越广,在某些场景下,我们无法通过注解或配置的形式直接获取到某个Bean。比如,在某一些工具类、设计模式实现中需要使用到Spring容器管理的Bean,此时就需要直接获取到对应的Bean。

本文为大家整理汇总了常见的获取Bean的方式,并提供一些优劣分析,方便大家在使用到时有更好的选择。同时,也会为大家适当的普及和拓展一些相关知识。

2 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的应用程序,其中它的数据量和速度是显著。

2.1 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对象了。

3 Bean获取方式

3.1 方式一:通过BeanFactory获取

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

BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
User user = (User) beanFactory.getBean("user");

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

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

3.2 方式二 :通过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容器通知。

示例如下:

@Component
public 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的方式的区别。

3.3 方式三:启动获取ApplicationContext

在项目启动时先获取ApplicationContext对象,然后将其存储在一个地方,以便后续用到时进行使用。

这里提供两种场景的获取:

1.基于xml配置bean的形式,适用于比较古老的项目,已经很少使用了;

2.基于SpringBoot启动时获取ApplicationContext对象;

基于xml的形式实现:

// 其中applicationContext.xml 为配置容器的xml,不过现在一般很少使用了
ApplicationContext ac = new FileSystemXmlApplicationContext("applicationContext.xml");

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

基于SpringBoot启动实现:

@SpringBootApplication
public class ExampleApplication {

    public static void main(String[] args) {
        // 启动时,保存上下文,并保存为静态
        ConfigurableApplicationContext ac = SpringApplication.run(ExampleApplication.class, args);
        SpringContextUtil.setApplicationContext(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 setApplicationContext(ApplicationContext applicationContext){
        ac = applicationContext;
    }
}

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

3.4 方式四:通过继承ApplicationObjectSupport

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

具体实现代码:

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

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

ApplicationObjectSupport类图入下,我们看到它实现了ApplicationContextAware接口,在Spring容器初始化过程中回调方法setApplicationContext来完成ApplicationContext的赋值。

Spring容器获取Bean的9种方式 | 京东云技术团队,硬核干货,spring,京东云,java,后端

3.5 方式五:通过继承WebApplicationObjectSupport

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

具体实现代码如下:

@Component
public 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方法来获取。

Spring容器获取Bean的9种方式 | 京东云技术团队,硬核干货,spring,京东云,java,后端

3.6 方式六:通过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结构。

3.7 方式七:通过ApplicationContextAware

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

具体实现代码如下:

@Component
public 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的思路一致。其本质和四、五两种方式一样。

3.8 方式八:通过ContextLoader

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

具体实现代码如下:

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

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

3.9 方式九:通过BeanFactoryPostProcessor

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

@Component
public 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的子接口。

4 总结

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

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

作者:京东零售 曾登均

来源:京东云开发者社区文章来源地址https://www.toymoban.com/news/detail-522960.html

到了这里,关于Spring容器获取Bean的9种方式 | 京东云技术团队的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Mysql DDL执行方式-pt-osc介绍 | 京东云技术团队

    大家好,接着上次和大家一起学习了《MySQL DDL执行方式-Online DDL介绍》,那么今天接着和大家一起学习另一种MySQL DDL执行方式之 pt-soc 。 在MySQL使用过程中,根据业务的需求对表结构进行变更是个普遍的运维操作,这些称为DDL操作。常见的DDL操作有在表上增加新列或给某个列添

    2024年02月06日
    浏览(36)
  • 一种实现Spring动态数据源切换的方法 | 京东云技术团队

    不在现有查询代码逻辑上做任何改动,实现dao维度的数据源切换(即表维度) 节约bdp的集群资源。接入新的宽表时,通常uat验证后就会停止集群释放资源,在对应的查询服务器uat环境时需要查询的是生产库的表数据(uat库表因为bdp实时任务停止,没有数据落入),只进行服务

    2024年02月10日
    浏览(58)
  • Spring——Bean注入几种方式(放入容器)

    个人博客: 全是干货,相信不会让你失望 1.XML方式注入 在现在这个Springboot横行的年代,以XML来注入的方式可能已经不多见了,因为压根用不着,但毕竟是注入方式之一也得提一提,这种方式就是依赖于XML的解析来获取我们需要注入的Bean对象 常见的方式有:set方法注入、构

    2024年02月01日
    浏览(44)
  • 将Bean注入Spring容器的五种方式

    将bean放入Spring容器中有哪些方式? 我们知道平时在开发中使用Spring的时候,都是将对象交由Spring去管理,那么将一个对象加入到Spring容器中,有哪些方式呢,下面我就来总结一下 这种方式其实也是我们最常用的一种方式,@Configuration用来声明一个配置类,然后使用 @Bean 注解

    2024年02月05日
    浏览(35)
  • 一文带你了解Spring中存入Bean和获取Bean的方式

    上图中就是五大类注解对应的层,通过源码可以看到其他四个注解都基于@Conponent Spring既然是一个包含众多工具方法的IoC容器,它是一个控制反转的容器,所以就需要将Bean对象存入到容器中,需要用的时候从容器中获取Bean对象,下面我们来介绍下存入Bean对象。 配置了 Bean 的

    2024年02月16日
    浏览(41)
  • 【JavaEE】Spring中注解的方式去获取Bean对象

    【JavaEE】Spring的开发要点总结(3) 在前面的代码里,我们获取Bean对象也比较麻烦: 本文章就是为了更方便地去获取Bean对象~ 对象装配 也叫 对象注入 那么有没有对应的注解去实现这个功能呢? Spring提供的三种实现方法: 属性注入 构造方法注入 Setter注入 而这种非明文获取

    2024年02月15日
    浏览(39)
  • spring(1):基于XML获取Bean对象以及各种依赖注入方式

    1.1 根据id获取 1.2 根据类型获取 1.3 根据id和类型获取 注意: 当根据类型获取bean时,要求IOC容器中指定类型的bean有且只能有一个。 例如以下XML文件,当IOC容器中一共配置了两个,根据类型获取时会抛出异常。 根据类型来获取bean时,在满足bean唯一性的前提下,其实只是看:

    2024年01月25日
    浏览(46)
  • 京东搜索EE链路演进 | 京东云技术团队

    搜索系统中容易存在头部效应,中长尾的优质商品较难获得充分的展示机会,如何破除系统的马太效应,提升展示结果的丰富性与多样性,助力中长尾商品成长是电商平台搜索系统的一个重要课题。其中,搜索EE系统在保持排序结果基本稳定的基础上,通过将优质中长尾商品

    2024年02月10日
    浏览(46)
  • 基于AIGC的京东购物助手的技术方案设想 | 京东云技术团队

    随着AIGC的爆火,ChatGPT,GPT-4的发布,我作为一个算法工作者,深感AI发展的迅猛。最近,OpenAI的插件和联网功能陆续向用户公开,我也在第一时间试用了这些最新的功能。在OpenAI的插件市场上,我被一个可以帮助分析食谱,并生成购物清单的功能所吸引。我开始思考,如果我

    2024年02月12日
    浏览(56)
  • 技术赋能-混流编排功能,助力京东618直播重保 | 京东云技术团队

    每每到618、双11这样的大型活动的时候,每天都有几个重要的大v或者品牌直播需要保障。 以往的重点场次监播方式是这么造的: 对每路直播的源流、各档转码流分别起一个ffplay播放窗口,再手动调整尺寸在显示器桌面进行布局,排到一屏里来监播。 这样做的缺点: 操作复杂

    2024年02月08日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包