当前位置: 首页 > 科技观察

Spring容器获取bean有几种方式

时间:2023-03-23 11:29:21 科技观察

前言随着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仍然可以用于轻量级应用程序,例如移动设备或基于小程序的应用程序,其数据量和速度非常重要。BeanFactory和ApplicationContextBeanFactory的区别在于Spring框架的基础设施,面向Spring本身。ApplicationContext适用于使用Spring框架的开发人员。几乎所有的应用程序都可以直接使用ApplicationContext而不是底层的BeanFactory。另外,ApplicationContext和BeanFactory的初始化有一个很大的区别:BeanFactory在初始化容器时不实例化bean,直到第一次访问bean时才实例化目标bean。这样一来,我们就无法发现一些现有的Spring配置问题。如果Bean的某个属性没有被注入,BeanFacotry加载后,直到第一次调用getBean方法才会抛出异常。ApplicationContext在初始化应用程序上下文时实例化所有单实例bean。相应的,ApplicationContext的初始化时间会比BeanFactory长。了解了以上基础理论知识后,我们就可以尝试从IoC容器中获取Bean对象了。方法一:通过BeanFactory获取Bean通过BeanFactory获取Bean。基于xml配置文件时代,可以通过以下方法获取BeanFactory,然后通过BeanFactory获取对应的Bean。BeanFactorybeanFactory=newXmlBeanFactory(newClassPathResource("applicationContext.xml"));UserInfouserInfo=(UserInfo)beanFactory.getBean("userInfo");有一定编程年龄的程序员应该对这个还是有一些印象的。这种写法估计只会出现在古代项目中。鉴于xml形式的配置文件已经被基于注解的形式所取代,XmlBeanFactory也被标记为obsolete。不推荐使用此方法。其实不推荐还有一个原因。上面说了,尽量不要用BeanFactory,而用ApplicationContext。方法二:通过BeanFactoryAware获取上面的方法中已经舍弃了XmlBeanFactory,但是可以通过其他方式获取BeanFactory,然后从BeanFactory中获取指定的Bean。获取BeanFactory实例的最简单方法是实现BeanFactoryAware接口。BeanFactoryAware接口源码:publicinterfaceBeanFactoryAwareextendsAware{/***初始化回调方法,Spring会自动将BeanFactory注入其中,接收后即可使用BeanFactory*/voidsetBeanFactory(BeanFactorybeanFactory)throwsBeansException;}BeanFactoryAware属于org.spring框架。beans.factory.Aware根标记接口,在应用程序上下文启动期间使用setter注入获取对象。Aware接口是回调、侦听器和观察者设计模式的混合体,它表示一个bean有资格通过回调被Spring容器通知。这里提供了一个完整的工具类:@ComponentpublicclassBeanFactoryHelperimplementsBeanFactoryAware{privatestaticBeanFactorybeanFactory;/***重写BeanFactoryAware接口的方法*@parambeanFactory:将参数赋值给本地属性后就可以使用BeanFactory*@throwsBeansExceptionBeansException*/@OverridepublicvoidsetBeanFactory(BeanFactorybeanFactory)throwsBeansException{BeanFactoryHelper.bean工厂=bean工厂;}/***根据名称获取容器中的对象实例*@parambeanName:注入的实例必须已经存在于容器中,否则会抛出异常:NoSuchBeanDefinitionException*@returnObject*/publicstaticObjectgetBean(字符串beanName){returnbeanFactory.getBean(beanName);/***根据class获取容器中的对象实例*@paramrequiredType:注入的对象必须已经存在于容器中,否则抛出异常:NoSuchBeanDefinitionException*@paramClass*@returnobject*/publicstaticTgetBean(ClassrequiredType){returnbeanFactory.getBean(requiredType);}/***判断spring容器是否包含指定名称的对象*@parambeanNamebean名称*@return是否存在*/publicstaticbooleancontainsBean(StringbeanName){returnbeanFactory.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=newFileSystemXmlApplicationContext("applicationContext.xml");这里相当于直接初始化容器,获取容器的引用。该方法适用于使用Spring框架的单机应用程序,需要程序通过配置文件手动初始化Spring。目前大部分Spring项目都不再使用xml配置,也很少有人使用。基于SpringBoot启动的实现:@SpringBootApplicationpublicclassExampleApplication{publicstaticvoidmain(String[]args){//启动时保存上下文,保存为staticConfigurableApplicationContextac=SpringApplication.run(ExampleApplication.class,args);SpringContextUtil.setAc(ac);}}对应的SpringContextUtil类如下:publicclassSpringContextUtil1{privatestaticApplicationContextac;publicstaticTgetBean(StringbeanName,Classclazz){Tbean=ac.getBean(beanName,clazz);返回豆;}publicstaticvoidsetAc(ApplicationContextapplicationContext){ac=applicationContext;}}这两个方法都是在启动Spring项目时直接获取ApplicationContext的引用,然后存放到工具类中。使用时从工具类中获取ApplicationContext容器,再从中获取Bean对象。方法四:通过继承ApplicationObjectSupport该方法仍然是先获取ApplicationContext容器,然后从中获取Bean对象,但是是基于继承ApplicationObjectSupport类实现的。具体实现代码:@ComponentpublicclassSpringContextUtilextendsApplicationObjectSupport{publicTgetBean(Classclazz){ApplicationContextac=getApplicationContext();如果(ac==null){返回null;}返回ac.getBean(clazz);}}注意这里的SpringContextUtil类需要实例化。方法五:通过继承WebApplicationObjectSupportWebApplicationObjectSupport是ApplicationObjectSupport的一个实现类,提供Web相关的支持。实现原理同ApplicationObjectSupport。具体实现代码如下:@ComponentpublicclassSpringContextUtilextendsWebApplicationObjectSupport{publicTgetBean(Classclazz){ApplicationContextac=getApplicationContext();如果(ac==null){返回null;}返回ac.getBean(clazz);}}和基于ApplicationObjectSupport的实现相比,除了继承的对象不同,没有其他区别,都是基于getApplicationContext方法获取的。方法六:通过WebApplicationContextUtilsSpring提供了工具类WebApplicationContextUtils,通过它可以获取WebApplicationContext对象。具体实现代码如下://或WebApplicationContextwebApplicationContext1=WebApplicationContextUtils.request);//webApplicationContext1.getBean(name,clazz)Tbean=webApplicationContext.getBean(name,clazz);返回豆;}}这种方法在SpringMVC构建的Web项目中很常见,适用于B/S结构的Web项目。方法七:使用ApplicationContextAware实现ApplicationContextAware接口,在Spring容器启动获取ApplicationContext对象时将ApplicationContext注入其中。这种方式也是比较常用的Bean获取方式,推荐使用。具体实现代码如下:@ComponentpublicclassSpringContextUtil3implementsApplicationContextAware{privatestaticApplicationContextac;@OverridepublicvoidsetApplicationContext(ApplicationContextapplicationContext)throwsBeansException{ac=applicationContext;}publicstaticTgetBean(Classclazzbean=ac.getBean(clazz);returnbean;}}这个方法和通过BeanFactoryAware获取BeanFactory的思路是一致的。方法八:使用getCurrentWebApplicationContext方法ContextLoader通过ContextLoader提供,也是常用的获取WebApplicationContext的方法,具体实现代码如下:通过SpringMVC,该方法是一种不依赖Servlet,不需要注入的方法,但是需要注意的是,在服务器启动和Spring容器初始化时,无法通过该方法获取到Spring容器方法9:通过BeanFactoryPostProcessorSpring工具类,方便在非Spring管理环境下获取bean。@ComponentpublicfinalclassSpringUtilsimplementsBeanFactoryPostProcessor{/**Spring应用上下环境*/privatestaticConfigurableListableBeanFactorybeanFactory;@OverridepublicvoidpostProcessBeanFactory(ConfigurableListableBeanFactorybeanFactory)throwsBeansException{SpringUtilsS.beanFactory=beanFactory;}/***获取对象**@paramname*@returnObject使用给定名称注册的bean实例*@throwsBeansException**/@SuppressWarnings("unchecked")publicstaticTgetBean(Stringname)抛出BeansException{返回(T)beanFactory。getBean(名字);}/***获取类型为requiredType的对象**@paramclz*@return*@throwsBeansException**/publicstaticTgetBean(Classclz)throwsBeansException{Tresult=(T)beansFactory.getBean(clz);返回结果;}/***如果BeanFactory包含与给定名称匹配的bean定义,则返回true**@param名称*@returnboolean*/publicstaticbooleancontainsBean(Stringname){returnbeanFactory.containsBean(name);}/***判断以给定名称注册的bean定义是单例还是原型如果没有找到给定名称对应的bean定义,将抛出异常(NoSuchBeanDefinitionException)**@paramname*@returnboolean*@throwsNoSuchBeanDefinitionException**/publicstaticbooleanisSingleton(Stringname)throwsNoSuchBeanDefinitionException{returnbeanFactory.是单身人士(姓名);}/***@paramname*@returnClass注册对象的类型*@throwsNoSuchBeanDefinitionException**/publicstaticClassgetType(Stringname)throwsNoSuchBeanDefinitionException{returnbeanFactory.getType(name);}/***如果给定的bean名称在bean定义中有别名,则返回这些别名**@paramname*@return*@throwsNoSuchBeanDefinitionException**/publicstaticString[]getAliases(Stringname)throwsNoSuchBeanDefinitionException{returnbeanFactory.getAliases(名字);}/***获取aop代理对象**@paraminvoker*@return*/@SuppressWarnings("unchecked")publicstaticTgetAopProxy(Tinvoker){return(T)AopContext.currentProxy();其中ConfigurableListableBeanFactory接口也属于BeanFactory的子接口,实现方式不同,但本质上无非是通过BeanFactory或者ApplicationContext获取Bean,只是获取BeanFactory或者ApplicationContext容器的方式不同而已。那么,你是否意识到,学习一门技术或一种实现方法,只要掌握了它的基本原理,无论形式如何变化,它都是不变的。而这里的“Zong”就是IoC容器。

最新推荐
猜你喜欢