[关闭]
@liruiyi962464 2025-07-22T06:45:13.000000Z 字数 4806 阅读 68

通过 SpringContextProvider.getBean() 方法来获取 Spring 管理的 bean

代码


原因

FMSGCallBack_V31是一个回调接口的实现类 HCNetSDK.FMSGCallBack_V31 是海康威视SDK提供的回调接口,用于处理设备报警事件。这类回调接口通常由 SDK 内部实例化(而不是 Spring),因此 Spring无法自动注入依赖(如IYwAccessRecordService)。 避免NullPointerException如果直接在FMSGCallBack_V31 中使用 @Autowired,由于该类不是由Spring创建的,依赖注入会失败,导致ywAccessRecordServicenull。 通过 SpringContextProvider.getBean() 可以手动从 Spring 容器获取 Bean,确保依赖可用。 全局访问 Spring 上下文 SpringContextProvider 存储了ApplicationContext,使得 任何地方(包括非 Spring 管理的类) 都能获取 Bean。

处理方式

private static ApplicationContext context;Spring Framework 中的一个重要组件,用于存储 Spring容器的上下文。下面是对其的详细解释:

1. ApplicationContext 的定义

ApplicationContext是 Spring 框架的核心接口之一,代表 Spring 容器。它负责管理 bean 的生命周期、配置、依赖注入等。
它不仅提供了基本的功能(如获取 bean),还支持国际化、事件传播、和其他功能。

2.静态变量的意义

context声明为static的目的是在整个应用程序中共享同一个上下文实例。这意味着你可以在任何地方访问 Spring 管理的 bean,而不需要每次都获取上下文实例。
由于ApplicationContext是重用的,使用静态变量可以避免多次创建和查找上下文的开销。

2.如何使用

通过setApplicationContext方法,Spring 会在初始化时调用这个方法,将当前的ApplicationContext实例传递给SpringContextProvider
一旦上下文被设置,其他类可以通过SpringContextProvider.getBean()方法来获取 Spring 管理的 bean。

SpringContextProvider.java

  1. import org.springframework.beans.BeansException;
  2. import org.springframework.context.ApplicationContext;
  3. import org.springframework.context.ApplicationContextAware;
  4. import org.springframework.stereotype.Component;
  5. @Component
  6. public class SpringContextProvider implements ApplicationContextAware {
  7. private static ApplicationContext context;
  8. @Override
  9. public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
  10. context = applicationContext;
  11. }
  12. public static <T> T getBean(Class<T> clazz) {
  13. return context.getBean(clazz);
  14. }
  15. }

FMSGCallBack_V31.java

  1. import org.springframework.stereotype.Component;
  2. @Component
  3. public class FMSGCallBack_V31 implements HCNetSDK.FMSGCallBack_V31 {
  4. private IYwAccessRecordService ywAccessRecordService;
  5. public FMSGCallBack_V31() {
  6. this.ywAccessRecordService = SpringContextProvider.getBean(IYwAccessRecordService.class);
  7. }
  8. @Override
  9. public boolean invoke(int lCommand, NET_DVR_ALARMER pAlarmer, Pointer pAlarmInfo, int dwBufLen, Pointer pUser) {
  10. // 使用 ywAccessRecordService
  11. alarmDataHandle(lCommand, pAlarmer, pAlarmInfo, dwBufLen, pUser);
  12. return true;
  13. }
  14. }

代码使用了 SpringContextProvider 来获取 Spring 管理的 Bean(IYwAccessRecordService),而不是直接通过 @Autowired注入。这种写法通常是为了解决 在非 Spring 管理的类中获取 Bean 的问题。下面分析这种写法的原因、潜在问题以及改进建议:


1. 为什么这样写?

(1) 原因分析

  1. FMSGCallBack_V31 是一个回调接口的实现类

    • HCNetSDK.FMSGCallBack_V31 可能是 海康威视 SDK 提供的回调接口,用于处理设备报警事件。
    • 这类回调接口通常由 SDK 内部实例化(而不是 Spring),因此 Spring 无法自动注入依赖(如 IYwAccessRecordService)。
  2. 避免 NullPointerException

    • 如果直接在 FMSGCallBack_V31 中使用 @Autowired,由于该类不是由 Spring 创建的,依赖注入会失败,导致 ywAccessRecordServicenull
    • 通过 SpringContextProvider.getBean() 可以 手动从 Spring 容器获取 Bean,确保依赖可用。
  3. 全局访问 Spring 上下文

    • SpringContextProvider 存储了 ApplicationContext,使得 任何地方(包括非 Spring 管理的类) 都能获取 Bean。

2. 这种写法的潜在问题

(1) 违反 Spring 依赖注入原则

(2) 线程安全问题

(3) 代码可读性降低


3. 改进建议

(1) 方案 1:改用 @Autowired + 代理模式(推荐)

如果 FMSGCallBack_V31 必须由 SDK 创建,但你又想用 Spring 依赖注入,可以:
1. 让 Spring 管理 FMSGCallBack_V31
2. 在 SDK 注册时传入 Spring 管理的代理对象

示例

  1. @Component
  2. public class FMSGCallBackProxy implements HCNetSDK.FMSGCallBack_V31 {
  3. private final IYwAccessRecordService ywAccessRecordService;
  4. // Spring 注入
  5. @Autowired
  6. public FMSGCallBackProxy(IYwAccessRecordService ywAccessRecordService) {
  7. this.ywAccessRecordService = ywAccessRecordService;
  8. }
  9. @Override
  10. public boolean invoke(int lCommand, NET_DVR_ALARMER pAlarmer, Pointer pAlarmInfo, int dwBufLen, Pointer pUser) {
  11. // 实际业务逻辑
  12. alarmDataHandle(lCommand, pAlarmer, pAlarmInfo, dwBufLen, pUser);
  13. return true;
  14. }
  15. }

注册回调时

  1. // 在某个 Spring 管理的类中(如 @Service)
  2. @Service
  3. public class AlarmService {
  4. @Autowired
  5. private FMSGCallBackProxy callBackProxy;
  6. public void registerCallback() {
  7. HCNetSDK.INSTANCE.NET_DVR_SetDVRMessageCallBack_V31(callBackProxy, null);
  8. }
  9. }

(2) 方案 2:改用 @Configurable + AspectJ(高级)

如果回调对象必须由 SDK 创建,但你又想用 @Autowired,可以:
1. 启用 @Configurable(需 AspectJ 编译时织入)。
2. 让 Spring 在 运行时 注入依赖到非 Spring 管理的对象。

示例

  1. @Configurable
  2. public class FMSGCallBack_V31 implements HCNetSDK.FMSGCallBack_V31 {
  3. @Autowired
  4. private IYwAccessRecordService ywAccessRecordService;
  5. @Override
  6. public boolean invoke(...) {
  7. // 可以直接使用 ywAccessRecordService
  8. }
  9. }

配置
- 在 pom.xml 中添加 AspectJ 插件:
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

- 启用 @Configurable
java
@SpringBootApplication
@EnableLoadTimeWeaving(aspectjWeaving = AspectJWeaving.ENABLED)
public class MyApp { ... }

(3) 方案 3:保持当前写法,但优化 SpringContextProvider

如果必须用 getBean(),可以优化 SpringContextProvider

  1. @Component
  2. public class SpringContextProvider implements ApplicationContextAware {
  3. private static ApplicationContext context;
  4. public static <T> T getBean(Class<T> clazz) {
  5. if (context == null) {
  6. throw new IllegalStateException("ApplicationContext not initialized");
  7. }
  8. return context.getBean(clazz);
  9. }
  10. @Override
  11. public void setApplicationContext(ApplicationContext applicationContext) {
  12. context = applicationContext;
  13. }
  14. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注