@liruiyi962464
2025-07-22T06:45:13.000000Z
字数 4806
阅读 68
代码
FMSGCallBack_V31
是一个回调接口的实现类 HCNetSDK.FMSGCallBack_V31
是海康威视SDK提供的回调接口,用于处理设备报警事件。这类回调接口通常由 SDK 内部实例化(而不是 Spring),因此 Spring无法自动注入依赖(如IYwAccessRecordService)
。 避免NullPointerException
如果直接在FMSGCallBack_V31 中使用 @Autowired
,由于该类不是由Spring
创建的,依赖注入会失败,导致ywAccessRecordService
为null
。 通过 SpringContextProvider.getBean()
可以手动从 Spring 容器获取 Bean,确保依赖可用。 全局访问 Spring 上下文 SpringContextProvider 存储了ApplicationContext,使得 任何地方(包括非 Spring 管理的类) 都能获取 Bean。
private static ApplicationContext context;
是 Spring Framework
中的一个重要组件,用于存储 Spring容器的上下文。下面是对其的详细解释:
ApplicationContext
是 Spring 框架的核心接口之一,代表 Spring 容器。它负责管理 bean 的生命周期、配置、依赖注入等。
它不仅提供了基本的功能(如获取 bean),还支持国际化、事件传播、和其他功能。
将context
声明为static
的目的是在整个应用程序中共享同一个上下文实例。这意味着你可以在任何地方访问 Spring 管理的 bean,而不需要每次都获取上下文实例。
由于ApplicationContext
是重用的,使用静态变量可以避免多次创建和查找上下文的开销。
通过setApplicationContext
方法,Spring 会在初始化时调用这个方法,将当前的ApplicationContext
实例传递给SpringContextProvider
。
一旦上下文被设置,其他类可以通过SpringContextProvider.getBean()
方法来获取 Spring 管理的 bean。
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class SpringContextProvider implements ApplicationContextAware {
private static ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static <T> T getBean(Class<T> clazz) {
return context.getBean(clazz);
}
}
import org.springframework.stereotype.Component;
@Component
public class FMSGCallBack_V31 implements HCNetSDK.FMSGCallBack_V31 {
private IYwAccessRecordService ywAccessRecordService;
public FMSGCallBack_V31() {
this.ywAccessRecordService = SpringContextProvider.getBean(IYwAccessRecordService.class);
}
@Override
public boolean invoke(int lCommand, NET_DVR_ALARMER pAlarmer, Pointer pAlarmInfo, int dwBufLen, Pointer pUser) {
// 使用 ywAccessRecordService
alarmDataHandle(lCommand, pAlarmer, pAlarmInfo, dwBufLen, pUser);
return true;
}
}
代码使用了 SpringContextProvider
来获取 Spring 管理的 Bean(IYwAccessRecordService
),而不是直接通过 @Autowired
注入。这种写法通常是为了解决 在非 Spring 管理的类中获取 Bean 的问题。下面分析这种写法的原因、潜在问题以及改进建议:
FMSGCallBack_V31
是一个回调接口的实现类
HCNetSDK.FMSGCallBack_V31
可能是 海康威视 SDK 提供的回调接口,用于处理设备报警事件。 IYwAccessRecordService
)。 避免 NullPointerException
FMSGCallBack_V31
中使用 @Autowired
,由于该类不是由 Spring 创建的,依赖注入会失败,导致 ywAccessRecordService
为 null
。 SpringContextProvider.getBean()
可以 手动从 Spring 容器获取 Bean,确保依赖可用。全局访问 Spring 上下文
SpringContextProvider
存储了 ApplicationContext
,使得 任何地方(包括非 Spring 管理的类) 都能获取 Bean。@Autowired
,而不是手动调用 getBean()
。 getBean()
会使代码 紧耦合 到 Spring,不利于单元测试(难以 Mock)。SpringContextProvider
使用 static ApplicationContext
,如果 ApplicationContext
未初始化或被覆盖,可能导致 NullPointerException
。 context
被意外修改(虽然极罕见),可能引发问题。@Autowired
,而要手动 getBean()
。@Autowired
+ 代理模式(推荐)如果 FMSGCallBack_V31
必须由 SDK 创建,但你又想用 Spring 依赖注入,可以:
1. 让 Spring 管理 FMSGCallBack_V31
2. 在 SDK 注册时传入 Spring 管理的代理对象
示例:
@Component
public class FMSGCallBackProxy implements HCNetSDK.FMSGCallBack_V31 {
private final IYwAccessRecordService ywAccessRecordService;
// Spring 注入
@Autowired
public FMSGCallBackProxy(IYwAccessRecordService ywAccessRecordService) {
this.ywAccessRecordService = ywAccessRecordService;
}
@Override
public boolean invoke(int lCommand, NET_DVR_ALARMER pAlarmer, Pointer pAlarmInfo, int dwBufLen, Pointer pUser) {
// 实际业务逻辑
alarmDataHandle(lCommand, pAlarmer, pAlarmInfo, dwBufLen, pUser);
return true;
}
}
注册回调时:
// 在某个 Spring 管理的类中(如 @Service)
@Service
public class AlarmService {
@Autowired
private FMSGCallBackProxy callBackProxy;
public void registerCallback() {
HCNetSDK.INSTANCE.NET_DVR_SetDVRMessageCallBack_V31(callBackProxy, null);
}
}
@Configurable
+ AspectJ(高级)如果回调对象必须由 SDK 创建,但你又想用 @Autowired
,可以:
1. 启用 @Configurable
(需 AspectJ 编译时织入)。
2. 让 Spring 在 运行时 注入依赖到非 Spring 管理的对象。
示例:
@Configurable
public class FMSGCallBack_V31 implements HCNetSDK.FMSGCallBack_V31 {
@Autowired
private IYwAccessRecordService ywAccessRecordService;
@Override
public boolean invoke(...) {
// 可以直接使用 ywAccessRecordService
}
}
配置:
- 在 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 { ... }
SpringContextProvider
如果必须用 getBean()
,可以优化 SpringContextProvider
:
@Component
public class SpringContextProvider implements ApplicationContextAware {
private static ApplicationContext context;
public static <T> T getBean(Class<T> clazz) {
if (context == null) {
throw new IllegalStateException("ApplicationContext not initialized");
}
return context.getBean(clazz);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
context = applicationContext;
}
}