是否可以访问在步骤范围之外定义的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;
}
我希望能够在不修改代码的情况下添加更多策略。
答案 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()
。