持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第10天,点击查看活动详情。
SpringBatch从入门到精通-2-StepScope作用域和用法【掘金日新计划】
SpringBatch从入门到精通-3-并行处理【掘金日新计划】
SpringBatch从入门到精通-3.2-并行处理-远程分区【掘金日新计划】
SpringBatch从入门到精通-3.3-并行处理-远程分区(消息聚合)【掘金日新计划】
SpringBatch从入门到精通-4 监控和指标【掘金日新计划】
SpringBatch从入门到精通-4.2 监控和指标-原理【掘金日新计划】
SpringBatch从入门到精通-5 数据源配置相关【掘金日新计划】
SpringBatch从入门到精通-5.2 数据源配置相关-原理【掘金日新计划】
自动配置相关
SpringBoot自动配置里也对SpringBatch进行了支持。可以看下面的对
入口类BatchAutoConfiguration
//配置类
@Configuration
//配置类生效条件(含有JobLauncher.class/DataSource.class,JdbcOperations.class)
@ConditionalOnClass({ JobLauncher.class, DataSource.class, JdbcOperations.class })
//控制自动配置顺序(在HibernateJpaAutoConfiguration.class)之后
@AutoConfigureAfter(HibernateJpaAutoConfiguration.class)
//配置类生效条件(JobLauncher 已加载的Bean)
@ConditionalOnBean(JobLauncher.class)
//加载BatchProperties配置
@EnableConfigurationProperties(BatchProperties.class)
//导入BatchConfigurerConfiguration 使之生效
@Import(BatchConfigurerConfiguration.class)
public class BatchAutoConfiguration {
看 主要方法
@Bean
//bean的生效条件,在spring容器中未定义BatchDataSourceInitializer 时生效
@ConditionalOnMissingBean
// bean的生效条件,含有DataSource的时候生效
@ConditionalOnBean({DataSource.class})
public BatchDataSourceInitializer batchDataSourceInitializer(DataSource dataSource, ResourceLoader resourceLoader) {
return new BatchDataSourceInitializer(dataSource, resourceLoader, this.properties);
}
@Bean
//bean的生效条件,未含有jobLauncherCommandLineRunner 的时候生效
@ConditionalOnMissingBean
//在配置spring.batch.job.enabled=true的时候生效。默认是true
@ConditionalOnProperty(
prefix = "spring.batch.job",
name = {"enabled"},
havingValue = "true",
matchIfMissing = true
)
// 这个也就是系统启动之后自动去运行job的地方
public JobLauncherCommandLineRunner jobLauncherCommandLineRunner(JobLauncher jobLauncher, JobExplorer jobExplorer) {
JobLauncherCommandLineRunner runner = new JobLauncherCommandLineRunner(jobLauncher, jobExplorer);
String jobNames = this.properties.getJob().getNames();
//如果配置了jobName 那么会 自动运行对应的jobname
if (StringUtils.hasText(jobNames)) {
runner.setJobNames(jobNames);
}
return runner;
}
@Bean
// 这个是生成一个基于观察者模式的,
@ConditionalOnMissingBean({ExitCodeGenerator.class})
public JobExecutionExitCodeGenerator jobExecutionExitCodeGenerator() {
return new JobExecutionExitCodeGenerator();
}
@Bean
// 设置默认的一个jobOperator
@ConditionalOnMissingBean({JobOperator.class})
public SimpleJobOperator jobOperator(JobExplorer jobExplorer, JobLauncher jobLauncher, ListableJobLocator jobRegistry, JobRepository jobRepository) throws Exception {
SimpleJobOperator factory = new SimpleJobOperator();
factory.setJobExplorer(jobExplorer);
factory.setJobLauncher(jobLauncher);
factory.setJobRegistry(jobRegistry);
factory.setJobRepository(jobRepository);
if (this.jobParametersConverter != null) {
factory.setJobParametersConverter(this.jobParametersConverter);
}
return factory;
}
这里面有个 JobExecutionExitCodeGenerator他接收了一个JobExecutionEvent。
那什么时候发送的事件呢?在JobLauncherCommandLineRunner.java 里
protected void execute(Job job, JobParameters jobParameters) throws JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException, JobParametersInvalidException, JobParametersNotFoundException {
JobParameters nextParameters = (new JobParametersBuilder(jobParameters, this.jobExplorer)).getNextJobParameters(job).toJobParameters();
JobExecution execution = this.jobLauncher.run(job, nextParameters);
if (this.publisher != null) {
// 执行完之后。将jobExecution发出去
this.publisher.publishEvent(new JobExecutionEvent(execution));
}
}
那这个JobExecutionExitCodeGenerator接收了事件消息。又在什么时候使用呢?
ConfigurableApplicationContext ctx= SpringApplication.run(Demo03DataSourceApplication.class, args);
SpringApplication.exit(ctx);
看关键方法SpringApplicaiton.exit(ctx)
public static int exit(ApplicationContext context, ExitCodeGenerator... exitCodeGenerators) {
Assert.notNull(context, "Context must not be null");
int exitCode = 0;
try {
try {
ExitCodeGenerators generators = new ExitCodeGenerators();
Collection<ExitCodeGenerator> beans = context.getBeansOfType(ExitCodeGenerator.class).values();//获取定义好的bean内容
generators.addAll(exitCodeGenerators);
generators.addAll(beans);
exitCode = generators.getExitCode();
if (exitCode != 0) {
context.publishEvent(new ExitCodeEvent(context, exitCode));
}
}
finally {
close(context);
}
}
catch (Exception ex) {
ex.printStackTrace();
exitCode = (exitCode != 0) ? exitCode : 1;
}
return exitCode;
}
获取返回码并将返回码进行打印。
batchConfigurer配置类BatchConfigurerConfiguration
BatchConfigurerConfiguration是在BatchAutoConfiguration类中进行import进去的。
//bean生效条件,含有PlatformTransactionManager
@ConditionalOnClass(PlatformTransactionManager.class)
//bean生效条件,缺少配置BatchConfigurer的累
@ConditionalOnMissingBean(BatchConfigurer.class)
//配置类
@Configuration
class BatchConfigurerConfiguration {
可以看到这个类只有两个内容。定义jdbcBatchConfigure和jpaBatchConfigurer
@Configuration
//如果未发现entityManagerFactory 的bean那么 定义BasicBatchConfigurer
@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
// //如果未发现entityManagerFactory 的bean那么 定义JpaBatchConfigurer
@ConditionalOnClass(name = "javax.persistence.EntityManagerFactory")
@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);
}
}
可以看到该类主要判断的条件是classloader里是否含有EntityManagerFactory和entityManagerFactory的bean。
如果含有了那么走JpaBatchConfigurer如果未含有走BasicBatchConfigurer 也就是默认的。
启动执行类JobLauncherCommandLineRunner
这个类是在配置spring.batch.job.enabled=true 的时候才会走的类。
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "spring.batch.job", name = "enabled", havingValue = "true", matchIfMissing = true)
public JobLauncherCommandLineRunner jobLauncherCommandLineRunner(
JobLauncher jobLauncher, JobExplorer jobExplorer) {
// 这里使用的时候new了一个对象
JobLauncherCommandLineRunner runner = new JobLauncherCommandLineRunner(
jobLauncher, jobExplorer);
//加载配置中的job名称
String jobNames = this.properties.getJob().getNames();
if (StringUtils.hasText(jobNames)) {
runner.setJobNames(jobNames);
}
return runner;
}
看他因为喜欢了接口CommandLineRunner
所以主要看他的run方法
@Override
public void run(String... args) throws JobExecutionException {
logger.info("Running default command line with: " + Arrays.asList(args));
launchJobFromProperties(StringUtils.splitArrayElementsIntoProperties(args, "="));
}
protected void launchJobFromProperties(Properties properties)
throws JobExecutionException {
JobParameters jobParameters = this.converter.getJobParameters(properties);
//执行配置的job
executeLocalJobs(jobParameters);
// 执行jobRegistry 注册的job
executeRegisteredJobs(jobParameters);
}
//执行配置的job
private void executeLocalJobs(JobParameters jobParameters)
throws JobExecutionException {
for (Job job : this.jobs) {
if (StringUtils.hasText(this.jobNames)) {
String[] jobsToRun = this.jobNames.split(",");
if (!PatternMatchUtils.simpleMatch(jobsToRun, job.getName())) {
logger.debug("Skipped job: " + job.getName());
continue;
}
}
execute(job, jobParameters);
}
}
// 执行jobRegistry 注册的job
private void executeRegisteredJobs(JobParameters jobParameters)
throws JobExecutionException {
if (this.jobRegistry != null && StringUtils.hasText(this.jobNames)) {
String[] jobsToRun = this.jobNames.split(",");
for (String jobName : jobsToRun) {
try {
Job job = this.jobRegistry.getJob(jobName);
if (this.jobs.contains(job)) {
continue;
}
execute(job, jobParameters);
}
catch (NoSuchJobException ex) {
logger.debug("No job found in registry for job name: " + jobName);
}
}
}
}
后续可以在分析一下jobRegistry 。然后举个例子代码
代码位置: github.com/jackssybin/…