如何在在非 Spring 管理的类中访问 Spring Bean

org.springframework.context.ApplicationContextAware 是 Spring 框架中的一个接口,它的主要作用是让一个 Bean 可以获得 Spring 容器(即 ApplicationContext)的引用,从而能以编程方式访问容器中的其他 Bean、资源等。


🌟 1. 接口定义与继承关系

  • 所在包org.springframework.context
  • 继承自org.springframework.beans.factory.Aware
  • 核心方法
    • setApplicationContext(ApplicationContext applicationContext)
      • 容器在创建实现了此接口的 Bean 后自动调用,传入当前的 ApplicationContext
public interface ApplicationContextAware {
    void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}
  • setApplicationContext 方法:当 Spring 容器创建这个 Bean 实例时,如果它实现了 ApplicationContextAware 接口,Spring 会自动调用这个方法,并将 ApplicationContext 作为参数传入。
  • ApplicationContext:是 Spring 的核心接口之一,代表了整个 IoC 容器,提供了大量方法来获取 Bean、资源文件、发布事件等。

🚗 2. 工作原理

  1. 识别阶段
    Spring 在启动并扫描 Bean 定义时,会检测哪些 Bean 实现了 ApplicationContextAware 接口。
  2. 实例化与依赖注入
    容器实例化 Bean 并完成一般的依赖注入后,调用 Aware 接口族的回调方法(含 setApplicationContext)。
  3. 回调执行
    实现类接收到 ApplicationContext,可将其保存为静态变量或实例变量,以便后续编程式调用。

🧠 3. 使用场景

  • 需要。
  • 编写工具类或者框架组件,需要访问容器中的其他 Bean。
  • 某些第三方集成(例如自定义注解处理器)中需要使用 Spring 容器功能。

📦 4. 示例代码

1️⃣ 自定义一个 Bean 实现 ApplicationContextAware

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;

import java.util.Map;

/**
 * Spring 应用上下文工具类
 * <p>实现 ApplicationContextAware 接口用于获取 Spring 应用上下文,
 * 提供静态方法方便在非 Spring 管理环境中获取 Bean 及环境信息等</p>
 */
public class SpringContextUtils implements ApplicationContextAware {

    // 静态保存 Spring 应用上下文
    private static ConfigurableApplicationContext applicationContext;

    /**
     * Spring 容器自动回调方法,注入应用上下文
     * @param ctx Spring 应用上下文对象
     * @throws BeansException 当上下文注入失败时抛出异常
     */
    @Override
    public void setApplicationContext(@NonNull ApplicationContext ctx) throws BeansException {
        SpringContextUtils.applicationContext = (ConfigurableApplicationContext) ctx;
    }

    /**
     * 获取当前 Spring 应用上下文
     * @return 返回当前应用上下文实例
     */
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    /**
     * 根据 Bean 名称获取 Spring 容器中的 Bean 实例
     * @param name Bean 在容器中注册的名称
     * @return 返回匹配的 Bean 实例
     * @throws BeansException 当找不到对应 Bean 时抛出异常
     */
    public static Object getBean(String name) throws BeansException {
        return applicationContext.getBean(name);
    }

    /**
     * 根据类型获取 Spring 容器中的 Bean 实例
     * @param requiredType Bean 的类类型
     * @param <T> 泛型类型参数
     * @return 返回匹配类型的 Bean 实例
     * @throws BeansException 当找不到唯一匹配的 Bean 时抛出异常
     */
    public static <T> T getBean(Class<T> requiredType) throws BeansException {
        return applicationContext.getBean(requiredType);
    }

    /**
     * 根据名称和类型获取 Bean,支持强制类型转换
     * @param name Bean 名称
     * @param requiredType Bean 类型
     * @param <T> 泛型类型参数
     * @return 转换后的 Bean 实例
     */
    public static <T> T getBean(String name, Class<T> requiredType) {
        return applicationContext.getBean(name, requiredType);
    }

    /**
     * 根据类型获取容器中所有匹配的 Bean 实例
     * @param type Bean 类型
     * @param <T> 泛型类型
     * @return 返回 Bean 名称到 Bean 实例的映射
     */
    public static <T> Map<String, T> getBeansOfType(Class<T> type) {
        return applicationContext.getBeansOfType(type);
    }

    /**
     * 判断容器中是否包含指定名称的 Bean
     * @param name Bean 名称
     * @return 存在则返回 true,否则 false
     */
    public static boolean containsBean(String name) {
        return applicationContext.containsBean(name);
    }

    /**
     * 判断指定名称的 Bean 是否为单例
     * @param name Bean 名称
     * @return 是单例返回 true,否则 false
     */
    public static boolean isSingleton(String name) {
        return applicationContext.isSingleton(name);
    }

    /**
     * 获取指定 Bean 的类型
     * @param name Bean 名称
     * @return Bean 的类型
     */
    public static Class<?> getType(String name) {
        return applicationContext.getType(name);
    }

    /**
     * 获取 Bean 的别名
     * @param name Bean 名称
     * @return 别名数组
     */
    public static String[] getAliases(String name) {
        return applicationContext.getAliases(name);
    }

    /**
     * 发布 Spring 事件
     * @param event 事件对象
     */
    public static void publishEvent(Object event) {
        applicationContext.publishEvent(event);
    }

    /**
     * 获取 Environment 对象,可获取配置属性等信息
     * @return Environment 实例
     */
    public static Environment getEnvironment() {
        return applicationContext.getEnvironment();
    }

    /**
     * 根据属性名获取配置值
     * @param key 属性名
     * @return 属性值,若不存在则返回 null
     */
    public static String getProperty(String key) {
        return applicationContext.getEnvironment().getProperty(key);
    }

    /**
     * 注册 JVM 关闭钩子,确保 Spring 容器正常关闭
     */
    public static void registerShutdownHook() {
        applicationContext.registerShutdownHook();
    }

    /**
     * 获取当前激活的 Spring Profile 列表
     * @return 激活的 Profile 数组
     */
    public static String[] getActiveProfiles() {
        return applicationContext.getEnvironment().getActiveProfiles();
    }
}

2️⃣ 使用它来获取 Spring 管理的 Bean:

AppUnitTypesService appUnitTypesService = SpringContextUtils.getBean(AppUnitTypesService.class);
List<AppUnitTypes> productTypes = appUnitTypesService.list();

🔍 5. 注意事项

注意点说明
不推荐滥用频繁通过 ApplicationContextAware 获取 Bean 会破坏 Spring 的依赖注入原则(DI),降低代码的可测试性和解耦性。
线程安全ApplicationContext 本身是线程安全的,但你获取到的 Bean 可能不是(取决于其作用域)。
替代方式更推荐使用构造器注入或字段注入来获取依赖。只有在无法通过 DI 注入的场景中再使用它。

✅ 5. 总结

特性描述
所属接口org.springframework.context.ApplicationContextAware
作用获取 Spring 容器对象(ApplicationContext
方法setApplicationContext(ApplicationContext applicationContext)
用途在 Bean 中动态访问 Spring 管理的对象
推荐使用工具类、集成框架、必须访问容器的特殊场景

如果你有具体的使用场景或代码示例,我可以帮你分析是否该使用它,或者有没有更优雅的替代方案。

人生不作安期生,醉入东海骑长鲸