diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/scope/StepScope.java b/spring-batch-core/src/main/java/org/springframework/batch/core/scope/StepScope.java
index f014bf10e..d7e687a98 100644
--- a/spring-batch-core/src/main/java/org/springframework/batch/core/scope/StepScope.java
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/scope/StepScope.java
@@ -1,176 +1,176 @@
-/*
- * Copyright 2006-2013 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.springframework.batch.core.scope;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.springframework.batch.core.scope.context.StepContext;
-import org.springframework.batch.core.scope.context.StepSynchronizationManager;
-import org.springframework.beans.BeanWrapper;
-import org.springframework.beans.BeanWrapperImpl;
-import org.springframework.beans.factory.ObjectFactory;
-import org.springframework.beans.factory.config.Scope;
-
-/**
- * Scope for step context. Objects in this scope use the Spring container as an
- * object factory, so there is only one instance of such a bean per executing
- * step. All objects in this scope are <aop:scoped-proxy/> (no need to
- * decorate the bean definitions).
- *
- *
- * In addition, support is provided for late binding of references accessible
- * from the {@link StepContext} using #{..} placeholders. Using this feature,
- * bean properties can be pulled from the step or job execution context and the
- * job parameters. E.g.
- *
- *
- * <bean id="..." class="..." scope="step">
- * <property name="parent" ref="#{stepExecutionContext[helper]}" />
- * </bean>
- *
- * <bean id="..." class="..." scope="step">
- * <property name="name" value="#{stepExecutionContext['input.name']}" />
- * </bean>
- *
- * <bean id="..." class="..." scope="step">
- * <property name="name" value="#{jobParameters[input]}" />
- * </bean>
- *
- * <bean id="..." class="..." scope="step">
- * <property name="name" value="#{jobExecutionContext['input.stem']}.txt" />
- * </bean>
- *
- *
- * The {@link StepContext} is referenced using standard bean property paths (as
- * per {@link BeanWrapper}). The examples above all show the use of the Map
- * accessors provided as a convenience for step and job attributes.
- *
- * @author Dave Syer
- * @author Michael Minella
- * @since 2.0
- */
-public class StepScope extends BatchScopeSupport {
-
- private static final String TARGET_NAME_PREFIX = "stepScopedTarget.";
-
- private Log logger = LogFactory.getLog(getClass());
-
- private final Object mutex = new Object();
-
- /**
- * Context key for clients to use for conversation identifier.
- */
- public static final String ID_KEY = "STEP_IDENTIFIER";
-
- public StepScope() {
- super();
- setName("step");
- }
-
- /**
- * This will be used to resolve expressions in step-scoped beans.
- */
- @Override
- public Object resolveContextualObject(String key) {
- StepContext context = getContext();
- // TODO: support for attributes as well maybe (setters not exposed yet
- // so not urgent).
- return new BeanWrapperImpl(context).getPropertyValue(key);
- }
-
- /**
- * @see Scope#get(String, ObjectFactory)
- */
- @Override
- public Object get(String name, ObjectFactory> objectFactory) {
- StepContext context = getContext();
- Object scopedObject = context.getAttribute(name);
-
- if (scopedObject == null) {
-
- synchronized (mutex) {
- scopedObject = context.getAttribute(name);
- if (scopedObject == null) {
-
- if (logger.isDebugEnabled()) {
- logger.debug(String.format("Creating object in scope=%s, name=%s", this.getName(), name));
- }
-
-
- scopedObject = objectFactory.getObject();
- context.setAttribute(name, scopedObject);
-
- }
-
- }
-
- }
- return scopedObject;
- }
-
- /**
- * @see Scope#getConversationId()
- */
- @Override
- public String getConversationId() {
- StepContext context = getContext();
- return context.getId();
- }
-
- /**
- * @see Scope#registerDestructionCallback(String, Runnable)
- */
- @Override
- public void registerDestructionCallback(String name, Runnable callback) {
- StepContext context = getContext();
- if (logger.isDebugEnabled()) {
- logger.debug(String.format("Registered destruction callback in scope=%s, name=%s", this.getName(), name));
- }
- context.registerDestructionCallback(name, callback);
- }
-
- /**
- * @see Scope#remove(String)
- */
- @Override
- public Object remove(String name) {
- StepContext context = getContext();
- if (logger.isDebugEnabled()) {
- logger.debug(String.format("Removing from scope=%s, name=%s", this.getName(), name));
- }
- return context.removeAttribute(name);
- }
-
- /**
- * Get an attribute accessor in the form of a {@link StepContext} that can
- * be used to store scoped bean instances.
- *
- * @return the current step context which we can use as a scope storage
- * medium
- */
- private StepContext getContext() {
- StepContext context = StepSynchronizationManager.getContext();
- if (context == null) {
- throw new IllegalStateException("No context holder available for step scope");
- }
- return context;
- }
-
- @Override
- public String getTargetNamePrefix() {
- return TARGET_NAME_PREFIX;
- }
-}
+/*
+ * Copyright 2006-2023 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.batch.core.scope;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.batch.core.scope.context.StepContext;
+import org.springframework.batch.core.scope.context.StepSynchronizationManager;
+import org.springframework.beans.BeanWrapper;
+import org.springframework.beans.BeanWrapperImpl;
+import org.springframework.beans.factory.ObjectFactory;
+import org.springframework.beans.factory.config.Scope;
+
+/**
+ * Scope for step context. Objects in this scope use the Spring container as an
+ * object factory, so there is only one instance of such a bean per executing
+ * step. All objects in this scope are <aop:scoped-proxy/> (no need to
+ * decorate the bean definitions).
+ * <bean id="..." class="..." scope="step">
+ * <property name="parent" ref="#{stepExecutionContext[helper]}" />
+ * </bean>
+ *
+ * <bean id="..." class="..." scope="step">
+ * <property name="name" value="#{stepExecutionContext['input.name']}" />
+ * </bean>
+ *
+ * <bean id="..." class="..." scope="step">
+ * <property name="name" value="#{jobParameters[input]}" />
+ * </bean>
+ *
+ * <bean id="..." class="..." scope="step">
+ * <property name="name" value="#{jobExecutionContext['input.stem']}.txt" />
+ * </bean>
+ *
+ *
+ * The {@link StepContext} is referenced using standard bean property paths (as
+ * per {@link BeanWrapper}). The examples above all show the use of the Map
+ * accessors provided as a convenience for step and job attributes.
+ *
+ * @author Dave Syer
+ * @author Michael Minella
+ * @since 2.0
+ */
+public class StepScope extends BatchScopeSupport {
+
+ private static final String TARGET_NAME_PREFIX = "scopedTarget.";
+
+ private Log logger = LogFactory.getLog(getClass());
+
+ private final Object mutex = new Object();
+
+ /**
+ * Context key for clients to use for conversation identifier.
+ */
+ public static final String ID_KEY = "STEP_IDENTIFIER";
+
+ public StepScope() {
+ super();
+ setName("step");
+ }
+
+ /**
+ * This will be used to resolve expressions in step-scoped beans.
+ */
+ @Override
+ public Object resolveContextualObject(String key) {
+ StepContext context = getContext();
+ // TODO: support for attributes as well maybe (setters not exposed yet
+ // so not urgent).
+ return new BeanWrapperImpl(context).getPropertyValue(key);
+ }
+
+ /**
+ * @see Scope#get(String, ObjectFactory)
+ */
+ @Override
+ public Object get(String name, ObjectFactory> objectFactory) {
+ StepContext context = getContext();
+ Object scopedObject = context.getAttribute(name);
+
+ if (scopedObject == null) {
+
+ synchronized (mutex) {
+ scopedObject = context.getAttribute(name);
+ if (scopedObject == null) {
+
+ if (logger.isDebugEnabled()) {
+ logger.debug(String.format("Creating object in scope=%s, name=%s", this.getName(), name));
+ }
+
+
+ scopedObject = objectFactory.getObject();
+ context.setAttribute(name, scopedObject);
+
+ }
+
+ }
+
+ }
+ return scopedObject;
+ }
+
+ /**
+ * @see Scope#getConversationId()
+ */
+ @Override
+ public String getConversationId() {
+ StepContext context = getContext();
+ return context.getId();
+ }
+
+ /**
+ * @see Scope#registerDestructionCallback(String, Runnable)
+ */
+ @Override
+ public void registerDestructionCallback(String name, Runnable callback) {
+ StepContext context = getContext();
+ if (logger.isDebugEnabled()) {
+ logger.debug(String.format("Registered destruction callback in scope=%s, name=%s", this.getName(), name));
+ }
+ context.registerDestructionCallback(name, callback);
+ }
+
+ /**
+ * @see Scope#remove(String)
+ */
+ @Override
+ public Object remove(String name) {
+ StepContext context = getContext();
+ if (logger.isDebugEnabled()) {
+ logger.debug(String.format("Removing from scope=%s, name=%s", this.getName(), name));
+ }
+ return context.removeAttribute(name);
+ }
+
+ /**
+ * Get an attribute accessor in the form of a {@link StepContext} that can
+ * be used to store scoped bean instances.
+ *
+ * @return the current step context which we can use as a scope storage
+ * medium
+ */
+ private StepContext getContext() {
+ StepContext context = StepSynchronizationManager.getContext();
+ if (context == null) {
+ throw new IllegalStateException("No context holder available for step scope");
+ }
+ return context;
+ }
+
+ @Override
+ public String getTargetNamePrefix() {
+ return TARGET_NAME_PREFIX;
+ }
+}
diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/annotation/StepScopeConfigurationTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/annotation/StepScopeConfigurationTests.java
index 2af2dc03b..82d081bcd 100644
--- a/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/annotation/StepScopeConfigurationTests.java
+++ b/spring-batch-core/src/test/java/org/springframework/batch/core/configuration/annotation/StepScopeConfigurationTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2006-2019 the original author or authors.
+ * Copyright 2006-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -103,6 +103,19 @@ public class StepScopeConfigurationTests {
assertEquals("STEP", value.call());
}
+ /**
+ * @see org.springframework.batch.core.configuration.xml.CoreNamespaceUtils#autoregisterBeansForNamespace
+ */
+ @Test
+ public void testStepScopeUsingNamespaceAutoregisterBeans() throws Exception {
+ init(StepScopeConfigurationTestsUsingNamespaceAutoregisterBeans.class);
+
+ ISimpleHolder value = (ISimpleHolder) context.getBean("xmlValue");
+ assertEquals("STEP", value.call());
+ value = (ISimpleHolder) context.getBean("javaValue");
+ assertEquals("STEP", value.call());
+ }
+
@Test
public void testStepScopeWithProxyTargetClassInjected() throws Exception {
init(StepScopeConfigurationInjectingProxy.class);
@@ -187,7 +200,14 @@ public class StepScopeConfigurationTests {
}
}
- public static class SimpleHolder {
+ public static interface ISimpleHolder {
+
+ String call() throws Exception;
+
+ }
+
+ public static class SimpleHolder implements ISimpleHolder {
+
private final String value;
protected SimpleHolder() {
@@ -222,6 +242,18 @@ public class StepScopeConfigurationTests {
@EnableBatchProcessing
public static class StepScopeConfigurationXmlImportUsingNamespace {
+ @Bean
+ @StepScope
+ protected SimpleHolder javaValue(@Value("#{stepExecution.stepName}") final String value) {
+ return new SimpleHolder(value);
+ }
+
+ }
+
+ @Configuration
+ @ImportResource("org/springframework/batch/core/configuration/annotation/StepScopeConfigurationTestsUsingNamespaceAutoregisterBeans-context.xml")
+ public static class StepScopeConfigurationTestsUsingNamespaceAutoregisterBeans {
+
@Bean
@StepScope
protected SimpleHolder javaValue(@Value("#{stepExecution.stepName}")
diff --git a/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/annotation/StepScopeConfigurationTestsUsingNamespaceAutoregisterBeans-context.xml b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/annotation/StepScopeConfigurationTestsUsingNamespaceAutoregisterBeans-context.xml
new file mode 100644
index 000000000..00b9a7e65
--- /dev/null
+++ b/spring-batch-core/src/test/resources/org/springframework/batch/core/configuration/annotation/StepScopeConfigurationTestsUsingNamespaceAutoregisterBeans-context.xml
@@ -0,0 +1,30 @@
+
+