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的赋值。
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方法来获取。
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容器。
作者:京东零售 曾登均
来源:京东云开发者社区
Spring容器获取Bean的9种方式的更多相关文章
- Spring容器装配Bean的三种方式
欢迎查看Java开发之上帝之眼系列教程,如果您正在为Java后端庞大的体系所困扰,如果您正在为各种繁出不穷的技术和各种框架所迷茫,那么本系列文章将带您窥探Java庞大的体系.本系列教程希望您能站在上帝 ...
- 普通Java类获取spring 容器的bean的5种方法
方法一:在初始化时保存ApplicationContext对象方法二:通过Spring提供的工具类获取ApplicationContext对象方法三:继承自抽象类ApplicationObjectSu ...
- Spring获取bean的几种方式
工作中需要对一个原本加载属性文件的工具类修改成对数据库的操作当然,ado层已经写好,但是需要从Spring中获取bean,然而,工具类并没有交给Spring来管理,所以需要通过方法获取所需要的bean ...
- Spring获取ApplicationContext方式,和读取配置文件获取bean的几种方式
转自:http://chinazhaokeke.blog.163.com/blog/static/109409055201092811354236 Spring获取ApplicationContex ...
- Spring在代码中获取bean的几种方式
方法一:在初始化时保存ApplicationContext对象 方法二:通过Spring提供的utils类获取ApplicationContext对象 方法三:继承自抽象类ApplicationObj ...
- Spring在代码中获取bean的几种方式(转:http://www.dexcoder.com/selfly/article/326)
方法一:在初始化时保存ApplicationContext对象 方法二:通过Spring提供的utils类获取ApplicationContext对象 方法三:继承自抽象类ApplicationObj ...
- Spring在代码中获取bean的几种方式(转)
获取spring中bean的方式总结: 方法一:在初始化时保存ApplicationContext对象 ApplicationContext ac = new FileSystemXmlApplica ...
- spring 获取bean的几种方式
1.读取xml文件的方式,这种在初学入门的时候比较适用 . ApplicationContext applicationContext = new ClassPathXmlApplicationCon ...
- Spring为IOC容器注入Bean的五种方式
一 @Import导入组件,id默认是组件的全类名 //类中组件统一设置.满足当前条件,这个类中配置的所有bean注册才能生效: @Conditional({WindowsCondition.clas ...
- Spring获取bean的一种方式
随便一百度,网上一大把,并且还不止一种.所以这里就只记录目前用的一种好了. 实现ApplicationContextAware接口 即可: import org.springframework.bea ...
随机推荐
- MordernC++之左值(引用)与右值(引用)
左值与右值 C++中左值与右值的概念是从C中继承而来,一种简单的定义是左值能够出现再表达式的左边或者右边,而右值只能出现在表达式的右边. int a = 5; // a是左值,5是右值 int b = ...
- 从桌面和应用内 Activity的启动流程
1.APP还没有被打开过从桌面启动 <1>首先桌面进程会像AMS服务发送startActivity的请求,AMS从system_service中去拿----一次IPC通信 <2> ...
- 家用wife密码设置
1.在浏览器上面输入ip地址:http://192.168.1.1/或http://192.168.0.1/出现路由器登陆窗口输入用户名跟密码.用户名默认一般为:admin,密码为空或为:admin ...
- 常见API使用
String类 字符串相关的类 Java程序中的所有字符串文字(例如"abc")都实现为此类的实例 字符串是不变的 他们的值在创建后无法更改 int length() 返回字符串对 ...
- 33-webpack详细配置output
const { resolve } = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin') module. ...
- [Pytorch框架] 1.1、Pytorch简介
文章目录 1.1 Pytorch 简介 1.1.1 PyTorch的由来 1.1.2 Torch是什么? 1.1.3 重新介绍 PyTorch 1.1.4 对比PyTorch和Tensorflow 1 ...
- 2021牛客OI赛前集训营-提高组(第二场)第三题 树数树题解
题目描述 牛牛有一棵 \(n\) 个点的有根树,根为 \(1\). 我们称一个长度为 \(m\) 的序列 \(a\) 是好的,当且仅当: \(\forall i \in (1,m]\),\(a_i\) ...
- i < sqrt(n) 和 i*i < n 那一种写法更加高效?
这两种写法效率依赖处理器.编译器和标准库.一般来说循环内的重复操作的性能差于循环外的单次操作. 参考文献 Which is more efficient to use in a for loop, i ...
- vue全家桶进阶之路35:Vue3 传递参数query和params
在 Vue.js 3.x 中,可以通过路由的 params 和 query 属性来传递参数. 通过 params 传递参数 我们可以在路由跳转时通过 params 传递参数.具体方法如下: // 在组 ...
- ET介绍——组件式设计(优化版的ECS)
组件式设计 在代码复用和组织数据方面,面向对象可能是大家第一反应.面向对象三大特性继承,封装,多态,在一定程度上能解决不少代码复用,数据复用的问题.不过面向对象不是万能的,它也有极大的缺陷: 1. 数 ...