首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

SpringBoot项目启动成功后,bean实例化成功,但是用的时候注入失败,我们应该怎么解决?

文章目录

一、业务背景

二、一个自动注入失败的真实案例

三、获取Spring容器bean实例化对象的各种方法

四、最后总结

一、业务逻辑

在 SpringBoot 项目启动成功后,所有加了组件注解的单例Bean都会被实例化到Spring容器里。

在常规情况下,我们只需要在业务里使用@Autowired注解自动注入Bean,然后就可以非常方便地使用容器里的bean实例操作业务了。

但是总有一些特殊情况,通过@Autowired自动注入的方式是获取不到容器里的Bean实例的,取出来的bean实例是null。

比如说在Http请求的 Filter 过滤器里,在需要动态获取Bean时。这几种情况,我们是无法通过@Autowired自动注入的方式获取Bean的。

二、一个自动注入失败的真实案例

在Spring Boot项目里,一般都有用户登录。用户使用手机号登录,在登录的后台处理中,需要对手机号进行验证,验证手机号是否在系统里注册过,这就涉及到查询数据库。

这一操作大部分都是在 Filter 过滤器的子类里做的,当我们使用 @Autowired 自动注入的方式获取容器里的bean实例时,会发现我们获取的始终是null,也就是说在 Filter 子类里是获取不到Spring容器里bean的实例的。

为什么在 Filter 里获取不到容器里的bean实例呢?

我在一个项目的Filter子类里,根据被继承的父类和接口一直向上追溯,在 GenericFilterBean 这个抽象类中发现只有 ServletContext 的成员变量。

Filter过滤器是 Servlet 规范的一部分,用于在请求进入Servlet容器之前,或响应离开容器之前,对请求/响应进行预处理或后续处理。

通常情况下,Filter 是在Servlet容器启动时被实例化和初始化的,与Spring容器的生命周期不同,因此我们无法在Filter里获取Spring容器里的Bean。

在 Filter 过滤器里获取不到Bean的实例化对象,那该怎么办呢?

SpringBoot 框架提供了一系列接口,可以帮助我们在这种特殊情况下获取容器的Bean实例。

二、获取Spring容器bean实例化对象的各种方法

1、善用启动类

在项目入口main方法里,会启动Spring上下文,这个时候可以把Spring上下文存储成静态变量,然后在需要的时候调用即可。

import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.context.ApplicationContext;import org.springframework.context.ConfigurableApplicationContext;/**

项目入口 @author @date 2024年3月27日 @Description

*/@SpringBootApplicationpublic class SpringApp {

public static void main(String[] args) { // 项目启动时,保存上下文,并保存为静态 ConfigurableApplicationContext applicationContext = SpringApplication.run(SpringApp.class, args); // 打印上下文对象实例 System.out.println(applicationContext); // 自定义Spring容器工具类 SpringContextUtil.setApplicationContext(applicationContext); // 打印上下文对象实例 System.out.println(SpringContextUtil.ac); }}

自定义Spring容器工具类:

/**

自定义Spring容器工具类 @author @date @Description

*/public class SpringContextUtil{

static ApplicationContext ac; public static void setApplicationContext(ApplicationContext applicationContext) { ac = applicationContext; } /** * 通过Class获取Bean实例 * @param clazz * @return */ public static <T> T getBean(Class<T> clazz) { return ac.getBean(clazz); } /** * 通过beanName 和 class获取bean实例 * @param beanName * @param clazz * @return */ public static <T> T getBean(String beanName, Class<T> clazz) { return ac.getBean(beanName, clazz);}}

有了自定义Spring容器工具类,在无法通过注入获取bean实例的时候,我们就可以通过工具类来获取相应的bean实例了。

2、实现ApplicationContextAware接口

import org.springframework.beans.BeansException;import org.springframework.context.ApplicationContext;import org.springframework.context.ApplicationContextAware;import org.springframework.stereotype.Component;

@Componentpublic class ApplicationContextRegister implements ApplicationContextAware {

static ApplicationContext ac; /** * 设置spring上下文 * @param applicationContext spring上下文 * @throws BeansException */ @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { ac = applicationContext; } /** * 通过Class获取Bean实例 * @param clazz * @return */ public static <T> T getBean(Class<T> clazz) { return ac.getBean(clazz); } /** * 通过beanName 和 class获取bean实例 * @param beanName * @param clazz * @return */ public static <T> T getBean(String beanName, Class<T> clazz) { return ac.getBean(beanName, clazz);}}

ApplicationContextAware接口是一个函数式接口,在这个接口里有一个ApplicationContext的形参,实现类就是通过它获取到的Spring上下文的。

3、实现BeanFactoryAware接口

import org.springframework.beans.factory.BeanFactory;import org.springframework.beans.factory.BeanFactoryAware;import org.springframework.stereotype.Component;

@Componentpublic class ApplicationContextRegister2 implements BeanFactoryAware {

static BeanFactory bf; @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { bf = beanFactory; } /** * 通过Class获取Bean实例 * @param clazz * @return */ public static <T> T getBean(Class<T> clazz) { return bf.getBean(clazz);}}

BeanFactoryAware是一个函数式接口,实现了该接口,就可以通过BeanFactory 形参获取Spring上下文。

4、实现ApplicationListener接口

import org.springframework.context.ApplicationContext;import org.springframework.context.ApplicationListener;import org.springframework.context.event.ContextRefreshedEvent;import org.springframework.stereotype.Component;

@Componentpublic class ApplicationContextRegister3 implements ApplicationListener<ContextRefreshedEvent> {

static ApplicationContext act; @Override public void onApplicationEvent(ContextRefreshedEvent event) { act = event.getApplicationContext(); } /** * 获取bean * @param clzss * @return */ public <T> T getBean(Class<T> clzss){ return act.getBean(clzss); }}

ApplicationListener是一个函数式接口,实现了该接口,就可以通过泛型形参获取Spring上下文。

5、实现BeanFactoryPostProcessor接口

import org.springframework.beans.BeansException;import org.springframework.beans.factory.config.BeanFactoryPostProcessor;import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;import org.springframework.stereotype.Component;

@Componentpublic class ApplicationContextRegister4 implements BeanFactoryPostProcessor {

static ConfigurableListableBeanFactory bf; @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { bf = beanFactory; } /** * 通过Class获取Bean实例 * @param clazz * @return */ public <T> T getBean(Class<T> clazz) { return bf.getBean(clazz);}}

BeanFactoryPostProcessor接口是一个函数式接口,在这个接口里有一个ConfigurableListableBeanFactory接口的形参,它是BeanFactory的子类。

因此通过实现BeanFactoryPostProcessor接口可以获得Spring上下文,进而可以从Spring容器里获取bean的实例化对象。

通过以上5种工具类,我们就可以任意地从容器中获取任意一个bean的实例了,不仅适用于Filter,还可以用在动态获取bean实例的场景。

最后总结

以上列举了5种从 Spring 容器中获取 Bean 的方法,每种方式实现各有不同,当然也不仅限于这5种方法,还有其他更多方法,我们会用其中一种就可以了。

从原理上讲,这些接口有一个共性,它们都是函数式接口。不论是实现哪个接口,先是获取 BeanFactory 或 ApplicationContext 或其子类,最后再通过其获取Bean实例。

  • 发表于:
  • 原文链接https://page.om.qq.com/page/OqLc4Jj_kUekR_jDE1KkviKg0
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券