应用上下文中一些bean的依赖在Spring Batch中形成了一个循环

Posted

技术标签:

【中文标题】应用上下文中一些bean的依赖在Spring Batch中形成了一个循环【英文标题】:The dependencies of some of the beans in the application context form a cycle in Spring Batch 【发布时间】:2022-01-09 06:12:40 【问题描述】:

我正在开发一个简单的 Spring Batch 应用程序,当我完成配置后,我发现了这个问题:

应用上下文表单中一些bean的依赖关系 一个循环:

jobRestController 在文件中定义 [/home/yassine/Downloads/demo/target/classes/com/example/demo/JobRestController.class] springBatchConfig 在文件中定义 [/home/yassine/Downloads/demo/target/classes/com/example/demo/SpringBatchConfig.class]

行动:

不鼓励依赖循环引用,它们是 默认禁止。更新您的应用程序以删除 bean之间的依赖循环。作为最后的手段,可能 通过设置自动中断循环 spring.main.allow-circular-references 为 true。

两个类的实现:

SpringBatchConfig:

import lombok.AllArgsConstructor;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.LineMapper;
import org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper;
import org.springframework.batch.item.file.mapping.DefaultLineMapper;
import org.springframework.batch.item.file.transform.DelimitedLineTokenizer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;

@Configuration
@EnableBatchProcessing
@AllArgsConstructor
public class SpringBatchConfig 

    private JobBuilderFactory jobBuilderFactory;
    private StepBuilderFactory stepBuilderFactory;
    private ItemReader<BankTransaction> bankTransactionItemReader;
    private ItemProcessor<BankTransaction, BankTransaction> bankTransactionItemProcessor;
    private ItemWriter<BankTransaction> bankTransactionItemWriter;

    @Bean
    public Job bankJob() 
        Step step1 = stepBuilderFactory.get("step-load-data")
                .<BankTransaction, BankTransaction>chunk(100)
                .reader(bankTransactionItemReader)
                .processor(bankTransactionItemProcessor)
                .writer(bankTransactionItemWriter)
                .build();
        return jobBuilderFactory.get("bank-data-loader-job")
                .start(step1)
                .build();
    

    @Bean
    public FlatFileItemReader<BankTransaction> flatFileItemReader(@Value("$inputFile") Resource inputFile) 
        FlatFileItemReader<BankTransaction> flatFileItemReader = new FlatFileItemReader<>();
        flatFileItemReader.setName("CSV-READER");
        flatFileItemReader.setLinesToSkip(1);
        flatFileItemReader.setResource(inputFile);
        flatFileItemReader.setLineMapper(lineMapper());
        return flatFileItemReader;
    

    @Bean
    public LineMapper<BankTransaction> lineMapper() 
        DefaultLineMapper<BankTransaction> lineMapper = new DefaultLineMapper<>();
        DelimitedLineTokenizer lineTokenizer = new DelimitedLineTokenizer();
        lineTokenizer.setDelimiter(",");
        lineTokenizer.setStrict(false);
        lineTokenizer.setNames("id", "accountID", "strTransactionDate", "transactionType", "amount");
        lineMapper.setLineTokenizer(lineTokenizer);
        BeanWrapperFieldSetMapper<BankTransaction> fieldSetMapper = new BeanWrapperFieldSetMapper<>();
        fieldSetMapper.setTargetType(BankTransaction.class);
        lineMapper.setFieldSetMapper(fieldSetMapper);
        return lineMapper;
    

JobRestController:

import lombok.AllArgsConstructor;
import org.springframework.batch.core.*;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

@RestController
@AllArgsConstructor
public class JobRestController 

    private JobLauncher jobLauncher;
    private Job job;

    @GetMapping("/startJob")
    public BatchStatus load() throws Exception 
        Map<String, JobParameter> parameters = new HashMap<>();
        parameters.put("time", new JobParameter(System.currentTimeMillis()));
        JobParameters jobParameters = new JobParameters(parameters);
        JobExecution jobExecution = jobLauncher.run(job, jobParameters);
        while (jobExecution.isRunning()) 
            System.out.println(".....");
        
        return jobExecution.getStatus();
    

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.1</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>demo</description>
    <properties>
        <java.version>11</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-batch</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.batch</groupId>
            <artifactId>spring-batch-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

我无法修复它,我在 *** 上也没有发现任何类似的问题。提前谢谢你

【问题讨论】:

您能分享您的pom.xmlbuild.gradle 内容吗? @chaitanyaguruprasad 我刚刚分享了,谢谢 另外请发布您的错误信息 @chaitanyaguruprasad 是的,我在第一部分也发布了,应用程序上下文中一些 bean 的依赖关系形成一个循环 【参考方案1】:

今天我遇到了同样的问题,我通过以下步骤解决了它:

    你应该创建 ItemWriter,ItemProcessor Beans 在另一个配置类中声明 Job bean

春季批处理配置

package com.example.demo.config;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.List;


import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.LineMapper;
import org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper;
import org.springframework.batch.item.file.mapping.DefaultLineMapper;
import org.springframework.batch.item.file.transform.DelimitedLineTokenizer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;

import com.example.demo.dao.Person;
import com.example.demo.dao.PersonRepository;

import lombok.RequiredArgsConstructor;

@Configuration
@EnableBatchProcessing
@RequiredArgsConstructor
public class SpringBatchConfig 
    
    @Bean
    public FlatFileItemReader<Person> getItemReader(@Value("$filePath") Resource resource)
        FlatFileItemReader<Person> itemReader = new FlatFileItemReader<Person>();
        itemReader.setName("CSV-READER");
        itemReader.setLinesToSkip(1);
        itemReader.setResource(resource);
        itemReader.setLineMapper(PersonLineMapper());
        return itemReader;
    
    
    @Bean
    public LineMapper<Person> PersonLineMapper()
        DefaultLineMapper<Person> lineMapper = new DefaultLineMapper<Person>();
        DelimitedLineTokenizer lineTokenizer = new DelimitedLineTokenizer();
        lineTokenizer.setStrict(false);
        lineTokenizer.setDelimiter(";");
        lineTokenizer.setNames("id","fName","lName","strBirthDate","gender","dispo");
        lineMapper.setLineTokenizer(lineTokenizer);
        BeanWrapperFieldSetMapper<Person> fieldSetMapper = new BeanWrapperFieldSetMapper<Person>();
        fieldSetMapper.setTargetType(Person.class);
        lineMapper.setFieldSetMapper(fieldSetMapper);
        return lineMapper;
    
    
    @Bean
    public ItemProcessor<Person, Person> getItemProcessor()
        return new ItemProcessor<Person, Person>() 
            private DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
            @Override
            public Person process(Person item) throws Exception 
                item.setBirthDate(dateFormat.parse(item.getStrBirthDate()));
                return item;
            
        ;
    
    
    @Bean
    public ItemWriter<Person> getItemWriter() 
        return new ItemWriter<Person>() 
            @Autowired  
            private PersonRepository personRepository;
            @Override
            public void write(List<? extends Person> items) throws Exception 
                personRepository.saveAll(items);
                
            
        ;
    


工作配置

package com.example.demo.config;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.example.demo.dao.Person;

import lombok.RequiredArgsConstructor;

@Configuration
@RequiredArgsConstructor
public class JobConfig 
    private final JobBuilderFactory jobBuilderFactory;
    private final StepBuilderFactory stepBuilderFactory;
    private final ItemReader<Person> personItemReader;
    private final ItemWriter<Person> peronItemWriter;
    private final ItemProcessor<Person, Person> personItemProcessor;
    
    @Bean
    public Job personJob() 
        Step step1 = this.stepBuilderFactory.get("step-load-data")
                .<Person,Person>chunk(100)
                .writer(peronItemWriter)
                .reader(personItemReader)
                .processor(personItemProcessor)
                .build();
        
        return this.jobBuilderFactory.get("person-data-loader-job")
                    .start(step1)
                    .build();
    


用于启动作业的休息控制器

package com.example.demo.web;

import java.util.HashMap;
import java.util.Map;

import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameter;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import lombok.RequiredArgsConstructor;

@RestController
@RequiredArgsConstructor
public class PersonRestController 
    
    private final JobLauncher jobLauncher;
    private final Job job;
    
    @GetMapping("/startBatch")
    public BatchStatus load() throws Exception
        Map<String, JobParameter> parameters = new HashMap<>();
        parameters.put("time", new JobParameter(System.currentTimeMillis()));
        JobParameters jobParameters = new JobParameters(parameters);
        JobExecution jobExecution = jobLauncher.run(job, jobParameters);
        while(jobExecution.isRunning()) 
            System.out.println("....");
        
        return jobExecution.getStatus();
    
    
    
    

【讨论】:

以上是关于应用上下文中一些bean的依赖在Spring Batch中形成了一个循环的主要内容,如果未能解决你的问题,请参考以下文章

应用上下文中一些bean的依赖形成了一个循环

Spring 5:以函数式方式注册 Bean

spring03

3.Spring自动装配

java怎么配置spring的bean

Bean的自动装配