https://docs.spring.io/spring-batch/4.2.x/reference/html/job.html#configuringJobRepository
在上述链接中的小节Configuring a JobRepository , 有一段话
When using @EnableBatchProcessing, a JobRepository is provided out of the box for you. This section addresses configuring your own As described in earlier, the JobRepository is used for basic CRUD operations of the various persisted domain objects within Spring Batch, such as JobExecution and StepExecution. It is required by many of the major framework features, such as the JobLauncher, Job, and Step. When using java configuration, a JobRepository is provided for you. A JDBC based one is provided out of the box if a DataSource is provided, the Map based one if not. However you can customize the configuration of the JobRepository via an implementation of the BatchConfigurer interface.. (使用@EnableBatchProcessing注解,spring batch为你提供了开箱即用的JobRepository 。如前所述,JobRepository用于Spring Batch中各种持久化域对象的基本CRUD操作,例如JobExecution和StepExecution。 许多主要框架功能(例如JobLauncher,Job和Step)都需要它。 使用Java配置时,会为您提供JobRepository。 如果提供了数据源,则开箱即用地提供基于JDBC的数据库,否则不提供基于Map的数据库。 但是,您可以通过BatchConfigurer接口的实现来自定义JobRepository的配置。)
大致的意思,使用了@EnableBatchProcessing注解,无需实现BatchConfigurer接口来自定义JobRepository的相关配置,就能使用JobRepository进行开发。
一、开始之前,先介绍spring的一个知识点。
1、@Import注解,和xml配置的 <import />标签作用一样,允许通过它引入 @Configuration 注解的类 (java config),
2、@Import 的实现很多时候需要借助 ImportSelector 接口, 需要通过这个接口的实现类去决定要引入哪些 @Configuration。
3、Springboot 对@Import注解的处理过程,发生在
AbstractApplicationContext.refresh()
-> AbstractApplicationContext.invokeBeanFactoryPostProcessors(beanFactory)
->PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors)
->ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
-> ConfigurationClassPostProcessor.processConfigBeanDefinitions(BeanDefinitionRegistry registry)
1、 springboot初始化的普通context(非web) 是AnnotationConfigApplicationContext(spring注解容器) 在初始化的时候会初始化两个工具类, AnnotatedBeanDefinitionReader 和 ClassPathBeanDefinitionScanner 分别用来从 annotation driven 的配置和xml的配置中读取beanDefinition并向context注册, 那么在初始化 AnnotatedBeanDefinitionReader 的时候, 会向BeanFactory注册一个ConfigurationClassPostProcessor 用来处理所有的基于annotation的bean, 这个ConfigurationClassPostProcessor 是 BeanFactoryPostProcessor 的一个实现,springboot会保证在 invokeBeanFactoryPostProcessors(beanFactory) 方法中调用注册到它上边的所有的BeanFactoryPostProcessor 因此,在spring容器启动时,会调用ConfigurationClassPostProcessor .postProcessBeanDefinitionRegistry()方法。
4、ConfigurationClassParser, 在ConfigurationClassPostProcessor .postProcessBeanDefinitionRegistry()方法中实例化ConfigurationClassParser调用
// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
那么在 ConfigurationClassParser -> processConfigurationClass() -> doProcessConfigurationClass() 方法中我们找到了 (这里边的流程还是很清楚的, 分别按次序处理了@PropertySource, @ComponentScan, @Import, @ImportResource, 在处理这些注解的时候是通过递归处理来保证所有的都被处理了)
取重点代码段
// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), true);
processImports流程如下: •首先,判断如果被import的是 ImportSelector.class 接口的实现, 那么初始化这个被Import的类, 然后调用它的selectImports方法去获得所需要的引入的configuration, 然后递归处理 •其次,判断如果被import的是 ImportBeanDefinitionRegistrar 接口的实现, 那么初始化后将对当前对象的处理委托给这个ImportBeanDefinitionRegistrar (不是特别明白, 只是我的猜测) •最后, 将import引入的类作为一个正常的类来处理 ( 调用最外层的doProcessConfigurationClass())
综上, 如果引入的是一个正常的component, 那么会作为@Component或者@Configuration来处理, 这样在BeanFactory里边可以通过getBean拿到, 但如果你是 ImportSelector 或者 ImportBeanDefinitionRegistrar 接口的实现, 那么spring并不会将他们注册到beanFactory中,而只是调用他们的方法。
二、正题,根据@EnableBatchProcessing注解类的注释,找到BatchConfigurer接口的实现类
进入注解@EnableBatchProcessing, 注解类的注解有@Import(BatchConfigurationSelector.class),可知,导入了BatchConfigurationSelector类。 接下来进入BatchConfigurationSelector类分析
代码略
BatchConfigurationSelector类实现了ImportSelector接口,
Base Configuration class providing common structure for enabling and using Spring Batch. Customization is available by implementing the BatchConfigurer interface (基本配置类提供了用于启用和使用Spring Batch的通用结构。 通过实现BatchConfigurer接口可以进行自定义)
从注释可以了解到,通过@Import(BatchConfigurationSelector.class),可将BatchConfigurer接口的实现类(被@Configuration注解),查找后注入到spring IOC容器中。
BatchConfigurationSelector实现了接口ImportSelector的方法selectImports(),从该方法可知,会返回ModularBatchConfiguration、SimpleBatchConfiguration。因EnableBatchProcessing注解类中明确说了Modular=false,因此,默认返回SimpleBatchConfiguration类的全限定名。默认情境,这个类就是@EnableBatchProcessing注解要引入的功能,
进入SimpleBatchConfiguration类,被@Configuration注解。查看该类的继承关系,只继承了AbstractBatchConfiguration,并没有实现@EnableBatchProcessing注释中说的BatchConfigurer接口,难道没有进行JobRepository的相关配置。再找找,,,在AbstractBatchConfiguration抽象类中,看到了BatchConfigurer的使用。并不是通过接口方式实现BatchConfigurer来进行JobRepository的配置。而是运用组合关系,在AbstractBatchConfiguration中,实例化DefaultBatchConfigurer,该类实现了接口BatchConfigurer。通过DefaultBatchConfigurer进行JobRepository的相关配置。
三、何时调用ImportBatchConfigurationSelector的实现接口方法selectImports(),从而找到SimpleBatchConfiguration
从之前补充的知识点可知,触发位置在
ConfigurationClassParser -> processConfigurationClass() -> doProcessConfigurationClass()
doProcessConfigurationClass()内的重点代码 processImports(configClass, sourceClass, getImports(sourceClass), true);
processImports方法内,统一处理所有@Import引入的类,在该方法内,会进行 *.selectImports()调用。将返回值存入List<sourceClass> annotatedClass , 后续spring的处理,会扫描该list,将配置类注入到spring IOC容器中。
在processImports方法,也可以印证之前的观点,@Import引入的类,如果被@Component、@Configuration注解,且未实现ImportSelector接口,在spring容器中(BeanFactory),可以通过getBean拿到,但实现了 ImportSelector 或者 ImportBeanDefinitionRegistrar 接口,spring并不会将它们注入到容器中,而是只调用它们的方法selectImports()。