1. JobRepository
存储 Job 的仓库,可以配置成 内存存储
JobRepostitory 的接口定义如下:
JobRepositoryFactoryBean的接口签名如下,里面我们可以看出需要指定数据源等信息。
配置一个 JobRepository
我们知道,在@EnableBatchProcess 注解之后,Spring Boot 会帮我自动配置一套 Spring Batch 的配置, 我们来通过 Spring Boot 的源码来解析一下如何注入一个 JobRepository。
Spring Boot的所有自动配置都包含在spring.boot.autoconfigure.jar 这个 jar 中。根据 Spring 的加载规则,自动配置的类需要 spring.factories 中引入,所以我们打开这个文件,找到 batch 相关的配置
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration 但是我们在这个类中只看到 JobRepository 的引用,并没有他的 Bean注解,所以一定是在这个之前已经引入了这个类的配置:
@Configuration
@ConditionalOnClass({ JobLauncher.class, DataSource.class, JdbcOperations.class })
@AutoConfigureAfter(HibernateJpaAutoConfiguration.class)
@ConditionalOnBean(JobLauncher.class)
@EnableConfigurationProperties(BatchProperties.class)
@Import(BatchConfigurerConfiguration.class)
public class BatchAutoConfiguration {
如果我们的猜想没错的话,从上边我们不难看出JobRepository 的定义一定是在 BatchConfigurerConfiguration.class 中,我们打开这个类。
@ConditionalOnClass(PlatformTransactionManager.class)
@ConditionalOnMissingBean(BatchConfigurer.class)
@Configuration
class BatchConfigurerConfiguration {
@Configuration
@ConditionalOnMissingBean(name = "entityManagerFactory")
static class JdbcBatchConfiguration {
@Bean
public BasicBatchConfigurer batchConfigurer(BatchProperties properties, DataSource dataSource,
ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
return new BasicBatchConfigurer(properties, dataSource, transactionManagerCustomizers.getIfAvailable());
}
}
@Configuration
@ConditionalOnClass(EntityManagerFactory.class)
@ConditionalOnBean(name = "entityManagerFactory")
static class JpaBatchConfiguration {
@Bean
public JpaBatchConfigurer batchConfigurer(BatchProperties properties, DataSource dataSource,
ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers,
EntityManagerFactory entityManagerFactory) {
return new JpaBatchConfigurer(properties, dataSource, transactionManagerCustomizers.getIfAvailable(),
entityManagerFactory);
}
}
}
然并卵,我们还是没有找到对应的 JobRepository的注解,等等,BasicBatchConfigurer.java这个好像是猫腻,我们打开这个这个类,果真里面就有private JobRepository jobRepository;
我们开看一下他是怎么初始化的,首先里面有个@PostConstruct 注解的initialize()方法,
@PostConstruct
public void initialize() {
try {
this.transactionManager = buildTransactionManager();
this.jobRepository = createJobRepository();
this.jobLauncher = createJobLauncher();
this.jobExplorer = createJobExplorer();
}
catch (Exception ex) {
throw new IllegalStateException("Unable to initialize Spring Batch", ex);
}
}
在这个方法里面调用了createJobRepository(),下边就是默认创建JobRepository所需要传入的属性。如果我们自定义JobRepository,同样的我们也需要传入这些属性。
protected JobRepository createJobRepository() throws Exception {
JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
PropertyMapper map = PropertyMapper.get();
map.from(this.dataSource).to(factory::setDataSource);
map.from(this::determineIsolationLevel).whenNonNull().to(factory::setIsolationLevelForCreate);
map.from(this.properties::getTablePrefix).whenHasText().to(factory::setTablePrefix);
map.from(this::getTransactionManager).to(factory::setTransactionManager);
factory.afterPropertiesSet();
return factory.getObject();
}
你也许会有疑问,BatchConfigurerConfiguration.java 中有这个注解 @ConditionalOnMissingBean(BatchConfigurer.class),而我们打开BatchConfigurer.java 这个接口的实现类,发现有个DefaultBatchConfigurer.java 被注解了进来,那为什么BatchConfigurerConfiguration.java里面的另外两个注解还是生效了呢?这就是SpringBoot 设计的精妙之处了。
@Component
public class DefaultBatchConfigurer implements BatchConfigurer {
当我们程序中注入了 Database 相关的参数,spring boot 会默认初始化一个entityManagerFactory 而一旦有两个这个
被 static 修饰的JdbcBatchConfiguration 和 JpaBatchConfiguration就会生效,这个时候就会注入基于数据库存储的 JopRepository。
@ConditionalOnClass(EntityManagerFactory.class)
@ConditionalOnBean(name = "entityManagerFactory")
那如果不配置 Datasource 呢?如果我们程序中不注入一个 Datasource ,程序就会报错。也就是说 Spring Boot 默认是必须使用数据库版本的。如果想不使用,只能自己实现一个 BatchConfig.java 接口了。
2. JobLauncher
一个根据 Job 名称和 JobParameters 触发 batch 的接口,不管触发成功与否都会返回一个 JobExecution 对象。如果当前的 JobName 和 JobParameters 已经对应一个 JobExecution ,则返回这个,如果么有则新建一个。
定义如下:
public interface JobLauncher {
public JobExecution run(Job job, JobParameters jobParameters) throws JobExecutionAlreadyRunningException,
JobRestartException, JobInstanceAlreadyCompleteException, JobParametersInvalidException;
}
配置一个 JobLauncher
正常情况下Spring boot 会帮我们创建一个默认的 JobLauncher,如果想客户化这个配置,可以参考 JobRepository.
protected JobLauncher createJobLauncher() throws Exception {
SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
jobLauncher.setJobRepository(getJobRepository());
jobLauncher.afterPropertiesSet();
return jobLauncher;
}
3. JobExplorer
提供了在运行期Mete-Data 数据的只读操作
接口定义如下:
参照之前的方法,下边我们在 Spring Boot 找到的默认实现的方法
protected JobExplorer createJobExplorer() throws Exception {
PropertyMapper map = PropertyMapper.get();
JobExplorerFactoryBean factory = new JobExplorerFactoryBean();
factory.setDataSource(this.dataSource);
map.from(this.properties::getTablePrefix).whenHasText().to(factory::setTablePrefix);
factory.afterPropertiesSet();
return factory.getObject();
}
4 JobOperator
之前介绍JobRepository 提供了对 meta-data 的增删改查。JobExplorer则提供了在运行期对这些数据的只读操作。但是一些常见的监视操作如,停止,重启,汇总等对于业务操作人员是非常有用的,JobOperator 则提供了这些操作。
JobOperator 接口的定义:
配置一个简单的例子:
@Bean
public SimpleJobOperator jobOperator(JobExplorer jobExplorer,
JobRepository jobRepository,
JobRegistry jobRegistry) {
SimpleJobOperator jobOperator = new SimpleJobOperator();
jobOperator.setJobExplorer(jobExplorer);
jobOperator.setJobRepository(jobRepository);
jobOperator.setJobRegistry(jobRegistry);
jobOperator.setJobLauncher(jobLauncher);
return jobOperator;
}
5. JobRegistry
JobRegistry (父接口为 JobLocator )并非强制使用,它能够协助用户在上下文中追踪job是否可用,也能够在应用上下文收集在其他地方(子上下文)创建的job信息。自定义的JobRegistry实现常被用于操作job的名称或是其他属性。框架提供了一个基于map的默认实现,能够从job的名称映射到job的实例
6. Batch Namespace
Spring Boot 之后大家已经习惯了基于 JavaConfig 的配置,但是 Batch 依然支持基于 XML 的配置,在官方文档中,基本上两种方式的配置都会提供。上边提到的领域模型概念在基于 XML 的配置中,虽然使用标准的 Beans 空间也可以配置,但是 Batch 还是提供了自己的命名空间,让配置变得更方便,下边就是一个配置的例子:
<beans:beans xmlns="http://www.springframework.org/schema/batch"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/batch
https://www.springframework.org/schema/batch/spring-batch.xsd">
<job id="ioSampleJob">
<step id="step1">
<tasklet>
<chunk reader="itemReader" writer="itemWriter" commit-interval="2"/>
</tasklet>
</step>
</job>
</beans:beans>
上边提到的 schema 在 spring-batch-core中有指定,我们看到现在默认的是使用 spring-batch-3.0.xsd
3.0的 XSD 的具体结构和约束可以在下边路径中找到。
至此 Spring Batch 的核心概念就介绍完了,下一篇我们将介绍具体的实例概念如 Job,Step 等。