8

我正在尝试研究如何为我正在编写的 web 应用程序开发一个可合理扩展的批处理框架。

我正在为 webapp 使用 Spring MVC,并带有一个自定义 DAO 层(要访问数据库,需要从一个设置为 @Autowired 并由 Spring 在运行时注入的UnitOfWorkFactory构造一个UnitOfWork实例)。

我正在使用 Spring Scheduler 注释 (@Scheduled) 来安排任务,但是我希望这些任务在集群中的不同机器上运行。每个批处理作业都应由其中一台集群机器拾取,然后执行。

Hazelcast似乎很适合此,因为分布式执行设计为此目的看起来非常简单和优雅。

我遇到了文档似乎没有涵盖的问题。我已经阅读了有关Spring Integration的文档,但是这似乎集中在如何使用 Spring 配置 Hazelcast(我已经完成了)。

当调度程序指示任务要开始时,我想创建任务的一个新实例(一个Callable实例)并将其提交给DistributedExecutor。当集群机器接收到要运行的任务时,我需要集群机器上的 Spring 容器在任务尝试执行之前将UnitOfWorkFactory实例注入批处理任务。每个集群都从 Spring 开始,并且UnitOfWorkFactory已经使用正确的详细信息进行了实例化,问题在于将UnitOfWorkFactory实例注入到我的任务中。

有人知道我如何配置我的应用程序,以便 Hazelcast 可以在Callable启动时自动注入UnitOfWorkFactory吗?我尝试将Callable标记为 Serializable 和ApplicationContextAware,但在尝试运行任务时仍然会收到NullPointerException 。

我可以直接访问 ApplicationContext,但我不希望这样做,因为它会限制我的任务的可测试性,并为我的批处理作业引入对 Spring 的硬依赖。

4

1 回答 1

7

通过 2.1 版Hazelcast 可以将 Spring 上下文和/或 Spring bean 注入 Hazelcast 托管对象。

如果您使用 Hazelcast Spring 配置配置 Hazelcast 并使用 注释 bean @SpringAware,Hazelcast 将要求 Spring 注入该 bean 的依赖项。

<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:hz="http://www.hazelcast.com/schema/spring"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
            http://www.hazelcast.com/schema/spring
            http://www.hazelcast.com/schema/spring/hazelcast-spring-2.1.xsd">
   <hz:hazelcast id="instance">
      <hz:config>
         <hz:group name="dev" password="password"/>
         <hz:network port="5701" port-auto-increment="false">
            <hz:join>
                <hz:multicast enabled="false" />
                <hz:tcp-ip enabled="true">
                    <hz:members>10.10.1.2, 10.10.1.3</hz:members>
                </hz:tcp-ip>
            </hz:join>
         </hz:network>
         ...
      </hz:config>
   </hz:hazelcast>

   <bean id="someBean" class="com.hazelcast.examples.spring.SomeBean" 
          scope="singleton" />
   ...
</beans>

@SpringAware 
public class SomeTask implements Callable<Long>, ApplicationContextAware, Serializable {
   private transient ApplicationContext context;
   private transient SomeBean someBean;

   public Long call() throws Exception {
     return someBean.value;
   }

   public void setApplicationContext(final ApplicationContext applicationContext)
       throws BeansException {
      context = applicationContext;
   }

   @Autowired
   public void setSomeBean(final SomeBean someBean) {
      this.someBean = someBean;
   }
}

对于早于 2.1 的版本:

Hazelcast 2.1 之前的版本不支持 Spring,因此对于 2.1 之前的版本,无法将 Spring 上下文或任何 Spring bean 注入 Hazelcast 托管对象。

Hazelcast 组上有一篇帖子询问此功能。

可调用的 Hazelcast / 依赖注入

正如您可能已经知道并在 Hazelcast 组中建议的那样,您可以使用以下方法访问 Spring ApplicationContext;

public class ApplicationContextProvider implements ApplicationContextAware {
    private static ApplicationContext context = null;

    public synchronized void setApplicationContext(ApplicationContext applicationContext)
        throws BeansException {
        if(context == null) {
            context = applicationContext;
        }
    }

    public static <T> T getBean(String name) {
        return (T) context.getBean(name);
    }
}  

class MyCallable implements Callable {
    ....
    public Object call() throws Exception {
        SomeServiceBean bean = ApplicationContextProvider.getBean("serviceBean");
        ....
    }
}
于 2011-12-28T10:05:22.227 回答