SpringBatch从入门到精通-5.3 数据源配置相关-原理-自动配置【掘金日新计划】

245 阅读4分钟

持续创作,加速成长,6月更文活动来啦!| 掘金·日新计划

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第10天,点击查看活动详情

SpringBatch从入门到精通-1【掘金日新计划】

SpringBatch从入门到精通-2-StepScope作用域和用法【掘金日新计划】

SpringBatch从入门到精通-3-并行处理【掘金日新计划】

SpringBatch从入门到精通-3.2-并行处理-远程分区【掘金日新计划】

SpringBatch从入门到精通-3.3-并行处理-远程分区(消息聚合)【掘金日新计划】

SpringBatch从入门到精通-4 监控和指标【掘金日新计划】

SpringBatch从入门到精通-4.2 监控和指标-原理【掘金日新计划】

SpringBatch从入门到精通-5 数据源配置相关【掘金日新计划】

SpringBatch从入门到精通-5.2 数据源配置相关-原理【掘金日新计划】

自动配置相关

SpringBoot自动配置里也对SpringBatch进行了支持。可以看下面的对

image-20220612005740849

入口类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 {

image-20220612011034111

看 主要方法

@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 {

image-20220612014914232

可以看到这个类只有两个内容。定义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;
    }

image-20220612015805103

看他因为喜欢了接口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/…