spring batch动态示例及原理

文章目录

JobController

java 复制代码
@RestController
public class JobController {

    @Autowired(required = false)
    private JobLauncher msgJobLauncher4;

    @Autowired(required = false)
    private Job msgJob4;

    @RequestMapping("msgJob4")
    public String msgJob4() {
        try {
            JobExecution jobExecution = msgJobLauncher4.run(msgJob4, new JobParametersBuilder()
                    .addString("time", System.currentTimeMillis() + "")
                    .toJobParameters());
            return "ok";
        } catch (JobExecutionAlreadyRunningException | JobRestartException | JobInstanceAlreadyCompleteException |
                 JobParametersInvalidException e) {
            throw new RuntimeException(e);
        }
    }

    @Autowired(required = false)
    private JobLauncher msgJobLauncher5;

    @Autowired(required = false)
    private Job msgJob5;

    @RequestMapping("msgJob5")
    public String msgJob5(@RequestParam(name = "key", defaultValue = "dft") String key) {
        try {
            JobExecution jobExecution = msgJobLauncher5.run(msgJob5, new JobParametersBuilder()
                    .addString("time", System.currentTimeMillis() + "")
                    .addString("key", key)
                    .toJobParameters());
            return "ok";
        } catch (JobExecutionAlreadyRunningException | JobRestartException | JobInstanceAlreadyCompleteException |
                 JobParametersInvalidException e) {
            throw new RuntimeException(e);
        }
    }

}

WriteFlatFileMsgJobConfig4

先写个100条数据 ,待会去读

java 复制代码
@Slf4j
@Configuration
public class WriteFlatFileMsgJobConfig4 {

    @Autowired
    private JobRepository jobRepository;

    @Autowired
    private PlatformTransactionManager txm;

    @Autowired
    private MsgMapper msgMapper;

    @Bean
    public JobLauncher msgJobLauncher4() {
        TaskExecutorJobLauncher jobLauncher = new TaskExecutorJobLauncher();
        jobLauncher.setJobRepository(jobRepository);
        return jobLauncher;
    }

    @Bean
    public Job msgJob4() {
        return new JobBuilder("msgJob4", jobRepository)
                .start(msgStep4())
                .build();
    }

    private Step msgStep4() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(1);
        executor.setMaxPoolSize(1);
        executor.afterPropertiesSet();

        return new StepBuilder("msgStep4", jobRepository)
                .<Person, Person>chunk(1, txm)
                .reader(msgReader4())
                .processor(msgProcessor4())
                .writer(msgWriter4())
                .taskExecutor(executor)
                .faultTolerant()
                .skip(Exception.class)
                .skipLimit(3)
                .build();
    }

    @Bean
    @StepScope
    public ItemReader<Person> msgReader4() {

        ItemReader<Person> itemReader = new ItemReader<>() {
            private int num = 1;
            private static final int DEFAULT_MAX_NUM = 100;

            @Override
            public Person read() throws Exception {
                synchronized (this) {
                    if (num <= DEFAULT_MAX_NUM) {
                        Person person = new Person();
                        person.setId(num);
                        person.setName("zzhua" + num);
                        person.setAge(new Random().nextInt(30));
                        System.out.println(person);
                        num++;
                        return person;
                    }
                    return null;
                }
            }
        };

        System.out.println("create msgReader4...: " + itemReader);

        return itemReader;
    }

    private ItemProcessor<Person, Person> msgProcessor4() {
        return new ItemProcessor<Person, Person>() {
            @Override
            public Person process(Person item) throws Exception {
                return item;
            }
        };
    }

    private ItemWriter<Person> msgWriter4() {
        return new FlatFileItemWriterBuilder<Person>()
                .name("msgWriter4")
                .delimited()
                .names("id", "name", "age")
                .resource(new FileSystemResource(new File(System.getProperty("user.dir") + "\\file\\person.txt")))
                .build();
    }

}

ReadFlatFileMsgJobConfig5

java 复制代码
@Configuration
public class ReadFlatFileMsgJobConfig5 {

    @Autowired
    private JobRepository jobRepository;

    @Autowired
    private PlatformTransactionManager txm;

    @Autowired
    private MsgMapper msgMapper;

    @Bean
    public JobLauncher msgJobLauncher5() {
        TaskExecutorJobLauncher jobLauncher = new TaskExecutorJobLauncher();
        jobLauncher.setJobRepository(jobRepository);
        return jobLauncher;
    }

    @Bean
    public Job msgJob5() {
        return new JobBuilder("msgJob5", jobRepository)
                .start(msgStep5())
                .build();
    }

    private Step msgStep5() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();

		// 注意,可以使用多线程去读哦(支持reader使用@StepScope)
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(10);
        executor.afterPropertiesSet();

        return new StepBuilder("msgStep5", jobRepository)
                .<Person, Person>chunk(1, txm)
                .reader(msgReader5(null))
                .processor(msgProcessor5())
                .writer(msgWriter5())
                .taskExecutor(executor)
                .faultTolerant()
                .skip(Exception.class)
                .skipLimit(3)
                .build();
    }

    @Bean
    @StepScope // 必须声明为ItemStreamReader,否则会报错
    public ItemStreamReader<Person> msgReader5(
    	// 动态获取job的执行参数
    	@Value("#{jobParameters['key']}") String key
   	) {

        Properties props = new Properties();

        String readFileName = "person.txt";
        try {
        	// 动态读取文件中配置的属性名
            props.load(new FileReader(System.getProperty("user.dir") + "\\file\\file.properties"));
            readFileName = props.getProperty("readFileName");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        FlatFileItemReader<Person> itemReader = new FlatFileItemReaderBuilder<Person>()
            .name("msgReader5")
            .resource(new FileSystemResource(new File(System.getProperty("user.dir") + "\\file\\" + readFileName)))
            .delimited()
            .names("id", "name", "age")
            .targetType(Person.class)
            .build();

        System.out.println("create msgReader5..." + itemReader + "key: " + key);

        return itemReader;
    }

    private ItemProcessor<Person, Person> msgProcessor5() {
        return new ItemProcessor<Person, Person>() {
            @Override
            public Person process(Person item) throws Exception {
                System.out.println("processor->" + item);
                return item;
            }
        };
    }

    private ItemWriter<Person> msgWriter5() {
        return new ItemWriter<Person>() {
            @Override
            public void write(Chunk<? extends Person> chunk) throws Exception {
                System.out.println(chunk);
            }
        };
    }
    
}

file.properties

properties 复制代码
readFileName=person.txt

@StepScope的前因后果

1、执行step之前,维护着全局唯一的stepExecution
2、多线程任务通过TaskExecutorRepeatTemplate开启,每个线程执行自己的任务,执行任务之前,先将全局唯一的stepExecution注册进去

关键就在这里,只要stepExecution是唯一的,那么获取的stepContext就是同一个,那么如果一个step开了多线程去跑,那么肯定能保证stepExecution是同一个,那么stepContext就是所有线程所共享,那么执行step步骤的不同线程中获取的就是同一个stepContext。

3、再看StepScope获取bean的逻辑

再看获取stepContext的逻辑

如下继续调用

针对多个线程而言,每个线程在执行任务时,都是将全局唯一共享的stepExecution绑定到当前自己的线程,那么每个线程在此处肯定拿到的时同一个stepContext,于是每次代理从stepScope中获取的肯定是第一次创建的对象了

为什么要声明为ItemStreamReader

至于为什么要声明为ItemStream呢?因为@StepScope标记当前bean返回的是仅实现了ItemReader代理对象,判断时并不会认为它实现了ItemStream接口,因此默认就不会添加到streams中,所以必须显式声明为ItemStreamReader,如果需要动态写,同理可声明为IteamStreamWriter

1、添加的过程步骤

最终在构建step对象时,添加到了streams中

2、调用过程






相关推荐
大傻^2 小时前
Spring AI Alibaba 企业级实战:从0到1构建智能客服系统
java·人工智能·后端·spring·springaialibaba
短剑重铸之日2 小时前
《ShardingSphere解读》11 解析引擎:SQL 解析流程应该包括哪些核心阶段?(上)
java·后端·spring·shardingsphere·分库分表
cyforkk3 小时前
Spring AOP 进阶:揭秘 @annotation 参数绑定的底层逻辑
java·数据库·spring
大傻^3 小时前
Spring AI Alibaba 多模态开发:集成视觉理解与视频分析能力
人工智能·spring·音视频·springai·springaialibaba·混合检索
大傻^4 小时前
Spring AI Alibaba 文档智能处理:PDF、Markdown知识入库全链路
java·人工智能·spring·pdf·知识图谱·springai·springaialibaba
大傻^4 小时前
Spring AI Alibaba Deep Research:自动化深度调研与报告生成
人工智能·spring·自动化
恼书:-(空寄4 小时前
拦截器获取不到 POST 请求 JSON 结构体参数(完整解决方案)
java·spring boot·spring·servlet
希望永不加班4 小时前
如何在 SpringBoot 里自定义 Spring MVC 配置
java·spring boot·后端·spring·mvc
大傻^4 小时前
Spring AI Alibaba MCP协议实战:模型上下文协议集成与工具调用
java·人工智能·后端·spring·elasticsearch·springaialibaba