不被Spring管理的对象也能进行依赖注入吗?
作者:程序员马丁
在线博客:https://open8gu.com
大话面试,技术同学面试必备的八股文小册,以精彩回答应对深度问题,助力你在面试中拿个offer。
面试话术
一般情况下,我们总是在被 Spring 容器管理的 Bean 里面使用依赖注入,不过在一些特定的情况下,我们也会需要在不被 Spring 管理的对象中访问 Spring 容器,或者进行依赖注入。
针对这种需求,我们一般有两种方案:
- 将 Spring 容器(比如
ApplicationContext
或者BeanFactory
)注入到静态工具类的静态成员变量中,通过这个来获取 Bean。 - 使用
AutowireCapableBeanFactory
手动进行依赖注入。
1. 上下文注入到静态工具类
简单的来说,这种方案需要先把一个工具类声明为 Spring 的 Bean,然后让 Spring 通过回调接口(比如 ApplicationContextAware
)或者 Setter 方法/构造器注入把上下文注入到一个静态成员变量里面。
比如:
@Bean // 将工具类也声明为 Bean,交给 Spring 管理
public SpringUtils implements ApplicationContextAware {
// 静态成员变量
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
SpringUtils.applicationContext = applicationContext;
}
public static <T> T getBean(Class<T> beanType) {
return applicationContext.getBean(beanType);
}
}
这种方案是比较常见的,我们也可以在一些开源项目里面找到这些案例,比如 Hippo4j 的 ApplicationContextHolder
,或者 Hutool 的 SpringUtil
。
这种方案的优点在于可以在任何地方自由的从 Spring 容器获取 Bean,但是缺点在于太过自由,当滥用时会因为绕开的 Spring 的各种依赖检查,从而导致 Bean 之间的依赖关系变得混乱。
2. 使用 AutowireCapableBeanFactory
Spring 的 BeanFactory
有一个下级接口 AutowireCapableBeanFactory
,它专门用于提供各种依赖注入功能,在项目中,我们可以通过依赖注入直接得到它,也可以在获取 ApplicationContext
后通过 getAutowireCapableBeanFactory
方法拿到。
当获取到 AutowireCapableBeanFactory
后,就可以通过下述两种方法手动进行依赖注入:
- 通过
autowireBean
方法手动的对特定对象进行 Setter 方法注入和属性注入。 - 通过
createBean
方法调用指定类的构造方法,在这个过程中 Spring 会进行构造器注入。
这两种方式都支持对不被 Spring 管理的对象进行依赖注入。
比如:
@Component
public class Test {
// 注入 AutowireCapableBeanFactory
// 也可以注入 ApplicationContext 后再调用 getAutowireCapableBeanFactory 获取
@Autowired
private AutowireCapableBeanFactory beanFactory;
public void run() {
// 方案一:在创建对象后,手动进行依赖注入
Bean bean1 = new Bean(); // 手动创建一个不被 Spring 管理的对象
beanFactory.autowireBean(bean1); // 手动触发依赖注入
// 方案二:通过 BeanFactory 手动创建对象,并在这个过程中完成构造器注入
Bean bean2 = beanFactory
.createBean(Bean.class, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, false);
}
// 不由 Spring 管理
public static Bean {
@Autowired
private Depend;
}
}
相比起第一种方案,第二种方案没有绕过 Spring 的依赖检查,并且依然支持 @Autowried
等机制,因此某种程度上来说要更加方便且优雅。
关于
BeanFactory
的类体系,请参见:✅ 什么是 BeanFactory?