@liruiyi962464
2025-07-22T06:45:13.000000Z
字数 4806
阅读 109
代码
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;@Componentpublic class SpringContextProvider implements ApplicationContextAware {private static ApplicationContext context;@Overridepublic 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;@Componentpublic class FMSGCallBack_V31 implements HCNetSDK.FMSGCallBack_V31 {private IYwAccessRecordService ywAccessRecordService;public FMSGCallBack_V31() {this.ywAccessRecordService = SpringContextProvider.getBean(IYwAccessRecordService.class);}@Overridepublic boolean invoke(int lCommand, NET_DVR_ALARMER pAlarmer, Pointer pAlarmInfo, int dwBufLen, Pointer pUser) {// 使用 ywAccessRecordServicealarmDataHandle(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 管理的代理对象
示例:
@Componentpublic class FMSGCallBackProxy implements HCNetSDK.FMSGCallBack_V31 {private final IYwAccessRecordService ywAccessRecordService;// Spring 注入@Autowiredpublic FMSGCallBackProxy(IYwAccessRecordService ywAccessRecordService) {this.ywAccessRecordService = ywAccessRecordService;}@Overridepublic boolean invoke(int lCommand, NET_DVR_ALARMER pAlarmer, Pointer pAlarmInfo, int dwBufLen, Pointer pUser) {// 实际业务逻辑alarmDataHandle(lCommand, pAlarmer, pAlarmInfo, dwBufLen, pUser);return true;}}
注册回调时:
// 在某个 Spring 管理的类中(如 @Service)@Servicepublic class AlarmService {@Autowiredprivate FMSGCallBackProxy callBackProxy;public void registerCallback() {HCNetSDK.INSTANCE.NET_DVR_SetDVRMessageCallBack_V31(callBackProxy, null);}}
@Configurable + AspectJ(高级)如果回调对象必须由 SDK 创建,但你又想用 @Autowired,可以:
1. 启用 @Configurable(需 AspectJ 编译时织入)。
2. 让 Spring 在 运行时 注入依赖到非 Spring 管理的对象。
示例:
@Configurablepublic class FMSGCallBack_V31 implements HCNetSDK.FMSGCallBack_V31 {@Autowiredprivate IYwAccessRecordService ywAccessRecordService;@Overridepublic 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:
@Componentpublic 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);}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) {context = applicationContext;}}