Spring Boot集成Hazelcast Jet时Job无法访问Spring上下文Bean问题
Spring Boot集成Hazelcast Jet作业无法访问Spring Bean问题修复
核心问题定位
当前配置存在4个直接导致Bean访问失败的问题:
- 依赖版本不兼容:
hazelcast-all、hazelcast-spring为4.2版本,hazelcast-jet为4.5版本,版本差会导致序列化、上下文注入逻辑类冲突 - ManagedContext配置缺失:仅给Hazelcast IMDG核心配置绑定了Spring上下文,未给Jet作业执行层配置上下文,作业反序列化时不会触发Spring注入
- 初始化顺序错误:在
DataFactory类加载阶段就提前获取springManagedContext引用,此时Spring容器可能未完成初始化,拿到的引用无效 - 静态变量依赖错误:Jet作业执行时使用独立类加载器,
AppBeans的静态CONTEXT变量不会随作业序列化传递,执行时必然为null;且作业类未加@SpringAware注解,Jet不会对其做Spring注入处理
分步修复方案
第一步:统一所有Hazelcast依赖版本
将三个核心依赖版本全部对齐为4.5,彻底解决类冲突问题:
<dependency> <groupId>com.hazelcast</groupId> <artifactId>hazelcast-all</artifactId> <version>4.5</version> </dependency> <dependency> <groupId>com.hazelcast.jet</groupId> <artifactId>hazelcast-jet</artifactId> <version>4.5</version> </dependency> <dependency> <groupId>com.hazelcast</groupId> <artifactId>hazelcast-spring</artifactId> <version>4.5</version> </dependency>
同版本下
hazelcast-all和hazelcast-spring无核心类冲突,可保证Spring上下文注入逻辑和Jet执行逻辑完全匹配。
第二步:修正Jet实例配置,正确绑定Spring上下文
将DataFactory交给Spring容器管理,通过依赖注入获取SpringManagedContext,同时给JetConfig本身也绑定上下文,不要仅配置底层HazelcastConfig:
@Component public class DataFactory implements Serializable { // 直接注入,禁止类初始化阶段通过静态方法提前获取 @Autowired private SpringManagedContext springManagedContext; public JetInstance buildJetInstance() { if (!EtlObjects.jetStart) { EtlObjects.jetStart = true; JetConfig jetConfig = new JetConfig(); jetConfig.getHazelcastConfig().setProperty( "hazelcast.logging.type", "log4j" ); jetConfig.getInstanceConfig().setCooperativeThreadCount(5); // 给Jet作业层配置上下文,之前遗漏该步骤 jetConfig.setManagedContext(springManagedContext); jetConfig.configureHazelcast(c -> { c.getNetworkConfig().setReuseAddress(true); c.setClusterName(UUID.randomUUID().toString()); c.setManagedContext(springManagedContext); c.getNetworkConfig().setPort(9493); c.getNetworkConfig().setPublicAddress("localhost"); c.getNetworkConfig().setPortAutoIncrement(true); c.getNetworkConfig().getJoin().getAutoDetectionConfig().setEnabled(false); c.getNetworkConfig().getJoin().getMulticastConfig().setEnabled(false); c.getNetworkConfig().getJoin().getTcpIpConfig().setEnabled(true) .setMembers(Arrays.asList("localhost")); }); EtlObjects.jetInstance = Jet.newJetInstance(jetConfig); } return EtlObjects.jetInstance; } }
第三步:改造作业类,通过Spring注入依赖而非静态类获取
所有在Jet作业中执行的类必须加@SpringAware注解,依赖的Bean通过@Autowired注入,禁止在执行逻辑中调用静态AppBeans方法:
@Component @SpringAware // 必须添加,Jet识别后会在反序列化时自动注入Spring依赖 public class JDBCDataSource implements ISourceBatch, Serializable { // 直接注入上下文,注入字段声明为transient,不需要随作业序列化 @Autowired private transient ApplicationContext applicationContext; // 若数据源固定,可直接注入DataSource,无需每次从上下文获取 // @Autowired // private transient DataSource dataSource; // 注意thissource字段如果是外部传入,也要做好序列化传递或声明为transient private Map<String, Object> thissource; @Override public BatchStage<Object> readSource(Pipeline pipeline) { // 将实例变量引用传递给lambda,不要引用外部静态变量 ApplicationContext ctx = this.applicationContext; Map<String, Object> sourceConfig = this.thissource; BatchSource<Object> jdbcSource = Sources.jdbc( () -> { String dsName = sourceConfig.get("datasourceName").toString(); DataSource ds = (DataSource) ctx.getBean(dsName); return ds.getConnection(); }, (con, parallelism, index) -> { // 原有业务逻辑保持不变 } ); return pipeline.readFrom(jdbcSource); } }
第四步:优化AppBeans工具类(可选,供非Jet场景使用)
修正工具类的非空校验,避免容器未初始化时调用抛出空指针,注意该类的静态方法禁止在Jet作业逻辑中调用:
@Component public class AppBeans implements ApplicationContextAware, ServletContextListener, BeanDefinitionRegistryPostProcessor, Serializable { private static ApplicationContext CONTEXT; private static ServletContext SERVLETCONTEXT; private static BeanDefinitionRegistry REGISTRY; private transient AutowireCapableBeanFactory beanFactory; @Override public void setApplicationContext(ApplicationContext context) throws BeansException { CONTEXT = context; beanFactory = context.getAutowireCapableBeanFactory(); } public static Object getBean(String beanName) { if (CONTEXT == null) { throw new IllegalStateException("Spring上下文未初始化完成,暂不支持获取Bean"); } return CONTEXT.getBean(beanName); } // 其余接口实现方法按原有逻辑补全即可 }
启动类中
SpringManagedContext的Bean定义无需修改,原有配置生效。
关键注意事项
- 所有在Jet作业中被序列化执行的类(Source、Sink、自定义Processor、lambda引用的自定义类),如果需要访问Spring Bean,必须满足两个条件:类上添加
@SpringAware注解;依赖字段用@Autowired注入且声明为transient,不需要随作业序列化。 - 禁止在Jet作业执行逻辑中引用外部类的静态变量获取Spring上下文,静态变量属于类级别属性,不会随作业序列化传递,在Jet的worker独立类加载器中默认值为null。
- 所有Hazelcast生态依赖必须保持版本完全一致,版本差会导致上下文注入失败、类转换异常等无明确报错的疑难问题。
内容的提问来源于stack exchange,提问作者user3458271
相关产品推荐
相关产品推荐

