Skip to main content

不被Spring管理的对象也能进行依赖注入吗?

作者:程序员马丁

在线博客:https://open8gu.com

note

大话面试,技术同学面试必备的八股文小册,以精彩回答应对深度问题,助力你在面试中拿个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?