SPRING-BATCH错误:没有可用于步骤范围的上下文持有者

huangapple go评论100阅读模式
英文:

SPRING-BATCH ERROR: No context holder available for step scope

问题

这是我的简单作业配置:

@Configuration
public class Testjob {
    
    @Autowired
    private StepBuilderFactory stepBuilderFactory;
    
    @Autowired
    private JobBuilderFactory jobBuilderFactory;
    
    @Bean
    @org.springframework.batch.core.configuration.annotation.StepScope
    public Step step1() {    
        return stepBuilderFactory.get("step1")
                            .tasklet((stepContribution, chunkContext) -> {
                                System.out.println("Hello World !");
                                return RepeatStatus.FINISHED;
                            }).build();
    }
    
    @Bean
    @DependsOn("step1")
    public Job job1() {
        return jobBuilderFactory.get("job1")
                            .start(step1())
                            .build();
    }
    
    @Bean
    public StepScope stepScope() {
        StepScope stepScope = new StepScope();
        stepScope.setAutoProxy(true);
        return stepScope;
    }

}

正如您所看到的,我已经按照这里的说明配置了stepScope bean,并且还在我的application.properties文件中添加了以下行:

spring.main.allow-bean-definition-overriding=true

这是我遇到的错误。我做错了什么?

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.step1': Scope 'step' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No context holder available for step scope
    ...

请注意,我的项目配置了两个数据源,还有Spring MVC Web:

数据源 1,用于 JPA/Hibernate:

@Configuration
@EnableJpaRepositories(entityManagerFactoryRef = "entityManagerFactory", 
        basePackages = {"com.foo.repositories"})
public class JpaConfig {

    @Autowired
    private Environment env;

    @Bean
    @Primary
    @ConfigurationProperties("spring.datasource")
    public DataSourceProperties dataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean
    @Primary
    @DependsOn({"dataSourceProperties"})
    public DataSource hikariDataSource(DataSourceProperties dataSourceProperties) {
        return dataSourceProperties.initializeDataSourceBuilder().build();
    }

    @Bean("entityManagerFactory")
    @DependsOn({"hikariDataSource"})
    public LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean(EntityManagerFactoryBuilder
            entityManagerFactoryBuilder, @Qualifier("hikariDataSource") DataSource dataSource) {

        Map<String, Object> properties = new HashMap<>();
        properties.put("hibernate.dialect", env.getProperty("spring.hib.properties.hibernate.dialect"));
        properties.put("hibernate.temp.use_jdbc_metadata_defaults", env.getProperty(
                "spring.hib.properties.hibernate.temp.use_jdbc_metadata_defaults"));

        return entityManagerFactoryBuilder.dataSource(dataSource)
                                .persistenceUnit("TestPersistenceUnit")
                                .properties(properties)
                                .packages("com.foo.entities")
                                .build();
    }
}

数据源 2,用于 Spring Batch JDBC Job Repository:

@Configuration
public class BatchFrameworkConfig {
    
    @Autowired
    private Environment env;
    
    @Bean("batch_datasource_properties")
    @ConfigurationProperties("spring.batch.datasource")
    public DataSourceProperties dataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean("batch_datasource")
    @DependsOn({"batch_datasource_properties"})
    public DataSource batchFrameworkDatasource(DataSourceProperties dataSourceProperties) {
        return dataSourceProperties.initializeDataSourceBuilder().build();
    }

    @Bean
    @DependsOn({"batch_datasource"})
    public BatchConfigurer defaultBatchConfigurer(@Qualifier("batch_datasource") DataSource dataSource) {
        return new DefaultBatchConfigurer(dataSource);
    }
}

我找不到这里提到的神秘XML。

出了什么问题?

谢谢。

英文:

This is my simple job configuration:

@Configuration
public class Testjob {
	
	@Autowired
	private StepBuilderFactory stepBuilderFactory;
	
	@Autowired
	private JobBuilderFactory jobBuilderFactory;
	
	@Bean
	@org.springframework.batch.core.configuration.annotation.StepScope
	public Step step1() {	
		return stepBuilderFactory.get(&quot;step1&quot;)
							.tasklet((stepContribution, chunkContext) -&gt; {
								System.out.println(&quot;Hello World !&quot;);
								return RepeatStatus.FINISHED;
							}).build();
	}
	
	@Bean
	@DependsOn(&quot;step1&quot;)
	public Job job1() {
		return jobBuilderFactory.get(&quot;job1&quot;)
							.start(step1())
							.build();
	}
	
	@Bean
		public StepScope stepScope() {
			StepScope stepScope = new StepScope();
			stepScope.setAutoProxy(true);
			return stepScope;
		}

}

As you can see I have configured the stepScope bean as instructed over here and and also added the following line to my application.properties:

spring.main.allow-bean-definition-overriding=true

This is the error I am getting. Where am I going wrong ?

org.springframework.beans.factory.BeanCreationException: Error creating bean with name &#39;scopedTarget.step1&#39;: Scope &#39;step&#39; is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No context holder available for step scope
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:368) ~[spring-beans-5.2.5.RELEASE.jar:5.2.5.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) ~[spring-beans-5.2.5.RELEASE.jar:5.2.5.RELEASE]
	at org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTargetSource.java:35) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:192) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
	at com.sun.proxy.$Proxy359.getName(Unknown Source) ~[na:na]
	at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:115) ~[spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
	at org.springframework.batch.core.job.AbstractJob.handleStep(AbstractJob.java:410) ~[spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
	at org.springframework.batch.core.job.SimpleJob.doExecute(SimpleJob.java:136) ~[spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
	at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:319) ~[spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
	at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:147) ~[spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
	at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50) ~[spring-core-5.2.5.RELEASE.jar:5.2.5.RELEASE]
	at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:140) ~[spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
	at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:127) ~[spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
	at com.sun.proxy.$Proxy360.run(Unknown Source) ~[na:na]
	at org.springframework.boot.autoconfigure.batch.JobLauncherCommandLineRunner.execute(JobLauncherCommandLineRunner.java:192) ~[spring-boot-autoconfigure-2.2.6.RELEASE.jar:2.2.6.RELEASE]
	at org.springframework.boot.autoconfigure.batch.JobLauncherCommandLineRunner.executeLocalJobs(JobLauncherCommandLineRunner.java:166) ~[spring-boot-autoconfigure-2.2.6.RELEASE.jar:2.2.6.RELEASE]
	at org.springframework.boot.autoconfigure.batch.JobLauncherCommandLineRunner.launchJobFromProperties(JobLauncherCommandLineRunner.java:153) ~[spring-boot-autoconfigure-2.2.6.RELEASE.jar:2.2.6.RELEASE]
	at org.springframework.boot.autoconfigure.batch.JobLauncherCommandLineRunner.run(JobLauncherCommandLineRunner.java:148) ~[spring-boot-autoconfigure-2.2.6.RELEASE.jar:2.2.6.RELEASE]
	at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:784) ~[spring-boot-2.2.6.RELEASE.jar:2.2.6.RELEASE]
	at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:768) ~[spring-boot-2.2.6.RELEASE.jar:2.2.6.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:322) ~[spring-boot-2.2.6.RELEASE.jar:2.2.6.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226) ~[spring-boot-2.2.6.RELEASE.jar:2.2.6.RELEASE]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215) ~[spring-boot-2.2.6.RELEASE.jar:2.2.6.RELEASE]
	at com.foo.BatchApplication.main(BatchApplication.java:17) ~[classes/:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
	at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) ~[spring-boot-devtools-2.2.6.RELEASE.jar:2.2.6.RELEASE]
Caused by: java.lang.IllegalStateException: No context holder available for step scope
	at org.springframework.batch.core.scope.StepScope.getContext(StepScope.java:167) ~[spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
	at org.springframework.batch.core.scope.StepScope.get(StepScope.java:99) ~[spring-batch-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:356) ~[spring-beans-5.2.5.RELEASE.jar:5.2.5.RELEASE]
	... 37 common frames omitted

Please note my project has 2 datasources configured, along with Spring MVC Web:

Datasource 1, for JPA/Hiberate:

@Configuration
@EnableJpaRepositories(entityManagerFactoryRef = &quot;entityManagerFactory&quot;, 
		basePackages = {&quot;com.foo.repositories&quot;})
public class JpaConfig {

	@Autowired
	private Environment env;
	
	@Bean
	@Primary
	@ConfigurationProperties(&quot;spring.datasource&quot;)
	public DataSourceProperties dataSourceProperties() {
		return new DataSourceProperties();
	}
	
	@Bean
	@Primary
	@DependsOn({&quot;dataSourceProperties&quot;})
	public DataSource hikariDataSource(DataSourceProperties dataSourceProperties) {
		return dataSourceProperties.initializeDataSourceBuilder().build();
	}
	
	@Bean(&quot;entityManagerFactory&quot;)
	@DependsOn({&quot;hikariDataSource&quot;})
	public LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean(EntityManagerFactoryBuilder
			entityManagerFactoryBuilder, @Qualifier(&quot;hikariDataSource&quot;) DataSource dataSource) {
		
		Map&lt;String, Object&gt; properties = new HashMap&lt;&gt;();
		properties.put(&quot;hibernate.dialect&quot;, env.getProperty(&quot;spring.hib.properties.hibernate.dialect&quot;));
		properties.put(&quot;hibernate.temp.use_jdbc_metadata_defaults&quot;, env.getProperty(
				&quot;spring.hib.properties.hibernate.temp.use_jdbc_metadata_defaults&quot;));
		
		return entityManagerFactoryBuilder.dataSource(dataSource)
								.persistenceUnit(&quot;TestPersistenceUnit&quot;)
								.properties(properties)
								.packages(&quot;com.foo.entities&quot;)
								.build();
	}

DataSource 2, for Spring Batch JDBC Job Repository:

@Configuration
public class BatchFrameworkConfig {
	
	@Autowired
	private Environment env;
	
	@Bean(&quot;batch_datasource_properties&quot;)
	@ConfigurationProperties(&quot;spring.batch.datasource&quot;)
	public DataSourceProperties dataSourceProperties() {
		return new DataSourceProperties();
	}

	@Bean(&quot;batch_datasource&quot;)
	@DependsOn({&quot;batch_datasource_properties&quot;})
	public DataSource batchFrameworkDatasource(DataSourceProperties dataSourceProperties) {
		return dataSourceProperties.initializeDataSourceBuilder().build();
	}
	
	@Bean
	@DependsOn({&quot;batch_datasource&quot;})
	public BatchConfigurer defaultBatchConfigurer(@Qualifier(&quot;batch_datasource&quot;) DataSource dataSource) {
		return new DefaultBatchConfigurer(dataSource);
	}
	
}

I am unable to find the mysterious XML mentioned over here.

What is going wrong ?

Thanks.

答案1

得分: 3

我的错误。

@StepScope 应该像以下方式声明:

@Configuration
@EnableBatchProcessing
public class Testjob {

    @Autowired
    private StepBuilderFactory stepBuilderFactory;

    @Autowired
    private JobBuilderFactory jobBuilderFactory;

    @Bean
    @StepScope
    public Tasklet helloWorldTasklet() {
        return (stepContribution, chunkContext) -> {
            System.out.println("Hello World !");
            return RepeatStatus.FINISHED;
        };
    }

    @Bean
    public Step step1() {
        return stepBuilderFactory.get("step1")
                                .tasklet(helloWorldTasklet())
                                .build();
    }

    @Bean
    public Job job1() {
        return jobBuilderFactory.get("job1")
                                .start(step1())
                                .build();
    }
}

注意:helloWorldTasklet bean 被声明为 @StepScope,而不是 @Step

英文:

My mistake.

@StepScope should be declared like the following:

@Configuration
@EnableBatchProcessing
public class Testjob {
	
	@Autowired
	private StepBuilderFactory stepBuilderFactory;
	
	@Autowired
	private JobBuilderFactory jobBuilderFactory;
	
	@Bean
	@StepScope
	public Tasklet helloWorldTasklet() {
		return (stepContribution, chunkContext) -&gt; {
			System.out.println(&quot;Hello World !&quot;);
			return RepeatStatus.FINISHED;
		};
	}
	
	@Bean	
	public Step step1() {	
		return stepBuilderFactory.get(&quot;step1&quot;)
							.tasklet(helloWorldTasklet())
							.build();
	}
	
	@Bean
	public Job job1() {
		return jobBuilderFactory.get(&quot;job1&quot;)
							.start(step1())
							.build();
	}
	
	
}

Notice that the helloWorldTasklet bean is declared as @StepScope and not my @Step.

答案2

得分: 0

使用Java配置时,您不需要自行注册StepScope(这仅适用于XML配置),只需在您的配置类上添加@EnableBatchProcessing注解。在您的情况下,不需要添加@StepScope和@DependsOn注解。

@Configuration
@EnableBatchProcessing
public class Testjob {

    @Autowired
    private StepBuilderFactory stepBuilderFactory;

    @Autowired
    private JobBuilderFactory jobBuilderFactory;

    @Bean
    public Step step1() {
        return stepBuilderFactory.get("step1")
            .tasklet((stepContribution, chunkContext) -> {
                System.out.println("Hello World !");
                return RepeatStatus.FINISHED;
            }).build();
    }

    @Bean
    public Job job1() {
        return jobBuilderFactory.get("job1")
            .start(step1())
            .build();
    }
}

当您需要每个作业或步骤的新实例时,请在在步骤或作业中使用的bean上添加@JobScope和@StepScope注解。

英文:

Using Java configuration, you don't need to register yourself the StepScope (this is for XML), add the annotation @EnableBatchProcessing on your configuration. In your case, you don't need to add @StepScope and @DependsOn.

@Configuration
@EnableBatchProcessing
public class Testjob {

    @Autowired
    private StepBuilderFactory stepBuilderFactory;

    @Autowired
    private JobBuilderFactory jobBuilderFactory;

    @Bean
    public Step step1() {   
        return stepBuilderFactory.get(&quot;step1&quot;)
                            .tasklet((stepContribution, chunkContext) -&gt; {
                                System.out.println(&quot;Hello World !&quot;);
                                return RepeatStatus.FINISHED;
                            }).build();
    }

    @Bean
    public Job job1() {
        return jobBuilderFactory.get(&quot;job1&quot;)
                        .start(step1())
                        .build();
    }    

}

Use @JobScope and @StepScope on bean used in your step or job when you need a new instance for each job or step.

huangapple
  • 本文由 发表于 2020年8月10日 03:00:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/63330202.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定