共计 8612 个字符,预计需要花费 22 分钟才能阅读完成。
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 容器。
作者:京东批发 曾登均
起源:京东云开发者社区