在Spring Batch中访问步骤范围之外的Bean

时间:2014-05-09 09:16:28

标签: spring spring-batch

是否可以访问在步骤范围之外定义的bean?例如,如果我定义策略" strategyA"并在作业参数中传递它我希望@Value解析为strategyA bean。这可能吗?我目前正在通过从applicationContext手动获取bean来解决这个问题。

@Bean
@StepScope
public Tasklet myTasklet(
        @Value("#{jobParameters['strategy']}") MyCustomClass myCustomStrategy)

    MyTasklet myTasklet= new yTasklet();

    myTasklet.setStrategy(myCustomStrategy);

    return myTasklet;
}

我希望能够在不修改代码的情况下添加更多策略。

2 个答案:

答案 0 :(得分:4)

排序答案是肯定的。这是更普遍的弹簧/设计模式问题评估者然后是Spring Batch Spring Batch棘手的部分是bean创建的配置和理解范围 让我们假设您的所有策略都实现了如下所示的策略界面:

interface Strategy {
    int execute(int a, int b); 
};

每个策略都应该实现策略并使用@Component注释来允许自动发现新策略。确保所有新策略都放在正确的包装下,以便组件扫描找到它们 例如:

@Component
public class StrategyA implements Strategy {
    @Override
    public int execute(int a, int b) {
        return a+b;
    }
}

以上是单例,将在应用程序上下文初始化时创建 使用@Value("#{jobParameters [' strategy']}")这个阶段为时尚早,因为还没有创建JobParameter。

所以我建议一个定位器bean,以后在创建myTasklet时使用(步骤范围)。

StrategyLocator类:

public class StrategyLocator {
    private Map<String, ? extends Strategy> strategyMap;

    public Strategy lookup(String strategy) {
        return strategyMap.get(strategy);
    }

    public void setStrategyMap(Map<String, ? extends Strategy> strategyMap) {
        this.strategyMap = strategyMap;
    }

}

配置如下:

@Bean
@StepScope
public MyTaskelt myTasklet () {
      MyTaskelt myTasklet = new MyTaskelt();
      //set the strategyLocator
      myTasklet.setStrategyLocator(strategyLocator());
      return myTasklet;
}
@Bean 
protected StrategyLocator strategyLocator(){
    return  = new StrategyLocator();    
}    

要初始化StrategyLocator,我们需要确保已经创建了所有策略。因此,最好的方法是在ContextRefreshedEvent事件上使用ApplicationListener(此示例中的警告名称以小写字母开头,更改这很容易......)。

@Component
public class PlugableStrategyMapper implements ApplicationListener<ContextRefreshedEvent> {
    @Autowired
    private StrategyLocator strategyLocator;
    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        ApplicationContext applicationContext = contextRefreshedEvent.getApplicationContext();
        Map<String, Strategy> beansOfTypeStrategy = applicationContext.getBeansOfType(Strategy.class);
        strategyLocator.setStrategyMap(beansOfTypeStrategy);        
    }

}

tasklet将保存一个String类型的字段,该字段将使用@Value注入Strategy enum String,并使用&#34;在步骤&#34;之前使用定位器解析。监听器。

    public class MyTaskelt implements Tasklet,StepExecutionListener {
        @Value("#{jobParameters['strategy']}")
        private String strategyName;
        private Strategy strategy;
        private StrategyLocator strategyLocator;

        @BeforeStep
        public void beforeStep(StepExecution stepExecution) {
            strategy = strategyLocator.lookup(strategyName);        
        }
        @Override
        public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
            int executeStrategyResult = strategy.execute(1, 2); 
        }
           public void setStrategyLocator(StrategyLocator strategyLocator) {
               this.strategyLocator = strategyLocator;
           }
    }

要将侦听器附加到taskelt,您需要在步骤配置中设置它:

@Bean
protected Step myTaskletstep() throws MalformedURLException {
     return steps.get("myTaskletstep")
    .transactionManager(transactionManager())
    .tasklet(deleteFileTaskelt())
    .listener(deleteFileTaskelt())
    .build();
 }

答案 1 :(得分:0)

jobParameters只保存一个String对象而不是真实对象(我认为这不是一个很好的实践,将bean定义存储到参数中)。
我将以这种方式行动:

@Bean
@StepScope    
class MyStategyHolder {
  private MyCustomClass myStrategy;
  // Add get/set

  @BeforeJob
  void beforeJob(JobExecution jobExecution) {
    myStrategy = (Bind the right strategy using job parameter value);
  }
}

并注册MyStategyHolder作为听众 在您的tasklet中使用@Value("#{MyStategyHolder.myStrategy}")或访问MyStategyHolder实例并执行getMyStrategy()