java - 尝试编写 Spring Batch 单元测试时出现 NoSuchBeanDefinitionException

标签 java spring unit-testing dependency-injection spring-batch

我正在尝试为我的 Spring Batch 作业组件编写单元测试,并向我的处理器声明。根据我在网上找到的所有信息,我做对了。但是当我尝试将它 Autowiring 到我的测试类中时,Spring 找不到我的处理器 bean。

我的处理器类的名称是 BatchFileRecordProcessor。我的测试类是 BatchFileRecordProcessorTest。这是后者的代码:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:/launch-context.xml"})
@TestExecutionListeners({DependencyInjectionTestExecutionListener.class, StepScopeTestExecutionListener.class})
public class BatchFileRecordProcessorTest
{
    @Autowired
    private BatchFileRecordProcessor processor = new BatchFileRecordProcessor();

    @Inject
    private BatchFileRecord batchFileRecord;

    public StepExecution getStepExecution()
    {
        StepExecution stepExecution = MetaDataInstanceFactory.createStepExecution();
        stepExecution.getExecutionContext().putString("fileName", "part-r-00000");

        return stepExecution;
    }

    @Test
    public void process() throws Exception
    {
        batchFileRecord.setSomeField("someValue");
        batchFileRecord.setSomeOtherField("someOtherValue");

        List<SetIdentityLinkInput> inputs = processor.process(batchFileRecord);
        assertEquals(1, inputs.size());
    }
}

这是我的 launch-context.xml 文件,我所有的 Spring 配置都位于此:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:batch="http://www.springframework.org/schema/batch"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-2.1.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <context:component-scan base-package="com.mobile.identity.batch" />

    <batch:job id="IdentitySojournerLogsBatch">
        <batch:validator ref="jobParametersValidator" />
        <batch:step id="batchJob.master">
            <batch:partition step="batchJob" partitioner="partitioner">
                <batch:handler grid-size="20" task-executor="partitionTaskExecutor" />
            </batch:partition>
        </batch:step>
        <batch:listeners>
            <batch:listener ref="jobListener"/>
        </batch:listeners>
    </batch:job>

    <bean id="partitioner" class="org.springframework.batch.core.partition.support.MultiResourcePartitioner" scope="step">
        <property name="resources" value="#{jobParameters['input.file.dir']}" />
    </bean>

    <bean id="partitionTaskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
        <property name="maxPoolSize" value="20" />
        <property name="corePoolSize" value="10" />
        <property name="queueCapacity" value="80" />
        <property name="WaitForTasksToCompleteOnShutdown" value="true" />
    </bean>

    <batch:step id="batchJob">
        <batch:tasklet>
            <batch:chunk reader="logFileReader" processor="batchFileRecordProcessor" writer="setIdentityLinkWriter" commit-interval="10" />
            <batch:listeners>
                <batch:listener ref="fileNameListener" />
                <batch:listener ref="logFileReaderListener" />
            </batch:listeners>
        </batch:tasklet>
    </batch:step>

    <bean id="logFileReaderParent" class="org.springframework.batch.item.file.FlatFileItemReader" abstract="true">
        <property name="lineMapper">
            <bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
                <property name="lineTokenizer">
                    <bean class="com.mobile.identity.batch.components.AmpersandDelimitedNameValuePairTokenizer">
                        <property name="names" value="isErrorRecord,deviceId,provider,deviceType,operatingSystem,operatingSystemVersion,idfaTag,timestamp,identifierCount,identifierString" />
                    </bean>
                </property>
                <property name="fieldSetMapper">
                    <bean class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
                        <property name="prototypeBeanName" value="batchFileRecord" />
                    </bean>
                </property>
            </bean>
        </property>
    </bean>

    <bean id="logFileReader" scope="step" autowire-candidate="false" parent="logFileReaderParent">
        <property name="resource" value="#{stepExecutionContext[fileName]}" />
    </bean>

    <bean id="batchFileRecordProcessor" class="com.mobile.identity.batch.components.BatchFileRecordProcessor" autowire-candidate="true" scope="step" />

    <bean id="batchFileRecord" class="com.mobile.identity.batch.model.BatchFileRecord" scope="prototype" />
</beans>

这是我尝试运行测试类时遇到的异常:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.mobile.identity.batch.components.BatchFileRecordProcessorTest': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.mobile.identity.batch.components.BatchFileRecordProcessor com.mobile.identity.batch.components.BatchFileRecordProcessorTest.processor; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.mobile.identity.batch.components.BatchFileRecordProcessor] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:287)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1106)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireBeanProperties(AbstractAutowireCapableBeanFactory.java:374)
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:110)
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:75)
    at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:321)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:211)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:288)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:290)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:157)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:74)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:202)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:65)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.mobile.identity.batch.components.BatchFileRecordProcessor com.mobile.identity.batch.components.BatchFileRecordProcessorTest.processor; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.mobile.identity.batch.components.BatchFileRecordProcessor] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:506)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:284)
    ... 29 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.mobile.identity.batch.components.BatchFileRecordProcessor] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:924)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:793)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:707)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:478)
    ... 31 more

2014/04/08 22-13-59,938:ERR:ERROR[Caught exception while allowing TestExecutionListener [org.springframework.test.context.support.DependencyInjectionTestExecutionListener@569cc9e9] to prepare test instance [com.mobile.identity.batch.components.BatchFileRecordProcessorTest@4f8c0c6b]]

[org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues threw org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.mobile.identity.batch.components.BatchFileRecordProcessorTest': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.mobile.identity.batch.components.BatchFileRecordProcessor com.mobile.identity.batch.components.BatchFileRecordProcessorTest.processor; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.mobile.identity.batch.components.BatchFileRecordProcessor] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}]

尝试运行单元测试时,我尝试在 IntelliJ 的调试器中逐步执行 Spring 代码; Spring 似乎在 launch-context.xml 中找到我的 batchFileRecordProcessor bean 定义,但出于某种原因,它并没有确定它是我测试类中依赖项的合适 Autowiring 候选者。我尝试将 'autowire-candidate="true"' 属性添加到 launch-context.xml 中的 batchFileRecordProcessor bean 定义,但这并没有改变输出。在这一点上有点难过。

编辑:感谢您的回复。对 BatchFileRecordProcessor 构造函数的调用是一个错误,是我在尝试不涉及依赖项注入(inject)的替代方法时遗留下来的。

这是 BatchFileRecordProcessor 类定义:我删除了所有(我假设)与此问题无关的业务逻辑方法:

/**
 *
 */
@Component("batchFileRecordProcessor")
@Scope("step")
public class BatchFileRecordProcessor implements ItemProcessor<BatchFileRecord, List<SetIdentityLinkInput>>
{
    /**
     */
    @Override
    public List<SetIdentityLinkInput> process(BatchFileRecord record) throws Exception
    {
        ...Bunch of stuff...
    }

    /**
     *
     * @param fileName
     */
    @Value("#{stepExecutionContext[fileName]}")
    public void setFileName(String fileName)
    {
        this.fileName = jobUtils.getFileNameFromFullPath(fileName);
    }
}

请注意,我确实有一种方法可以注入(inject)步骤执行参数;不确定这是否相关。

我尝试按照建议添加嵌套的 aop:scoped-proxy 标记,但现在出现了不同的异常:

java.lang.IllegalStateException: Failed to load ApplicationContext
    at org.springframework.test.context.TestContext.getApplicationContext(TestContext.java:157)
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:109)
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:75)
    at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:321)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:211)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:288)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:290)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:157)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:74)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:202)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:65)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'batchFileRecordProcessor' defined in BeanDefinition defined in class path resource [launch-context.xml]: Initialization of bean failed; nested exception is org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class [class com.sun.proxy.$Proxy22]: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: Cannot subclass final class class com.sun.proxy.$Proxy22
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:527)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:294)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:225)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:291)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:567)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:913)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:464)
    at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:103)
    at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:1)
    at org.springframework.test.context.support.DelegatingSmartContextLoader.loadContext(DelegatingSmartContextLoader.java:228)
    at org.springframework.test.context.TestContext.loadApplicationContext(TestContext.java:124)
    at org.springframework.test.context.TestContext.getApplicationContext(TestContext.java:148)
    ... 27 more
Caused by: org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class [class com.sun.proxy.$Proxy22]: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: Cannot subclass final class class com.sun.proxy.$Proxy22
    at org.springframework.aop.framework.Cglib2AopProxy.getProxy(Cglib2AopProxy.java:213)
    at org.springframework.aop.framework.ProxyFactory.getProxy(ProxyFactory.java:112)
    at org.springframework.aop.scope.ScopedProxyFactoryBean.setBeanFactory(ScopedProxyFactoryBean.java:109)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeAwareMethods(AbstractAutowireCapableBeanFactory.java:1475)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1443)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:519)
    ... 40 more
Caused by: java.lang.IllegalArgumentException: Cannot subclass final class class com.sun.proxy.$Proxy22
    at net.sf.cglib.proxy.Enhancer.generateClass(Enhancer.java:446)
    at net.sf.cglib.transform.TransformingClassGenerator.generateClass(TransformingClassGenerator.java:33)
    at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
    at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:216)
    at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:377)
    at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:285)
    at org.springframework.aop.framework.Cglib2AopProxy.getProxy(Cglib2AopProxy.java:201)
    ... 45 more

2014/04/09 09-46-30,053:ERR:ERROR[Caught exception while allowing TestExecutionListener [org.springframework.test.context.support.DependencyInjectionTestExecutionListener@aa0399b] to prepare test instance [com.mobile.identity.batch.components.BatchFileRecordProcessorTest@1d25f490]]

[org.springframework.test.context.TestContext.getApplicationContext threw java.lang.IllegalStateException: Failed to load ApplicationContext]

最佳答案

这有点晚了,但不是 @Autowired,试试这个注解:

@Resource(name="batchFileRecordProcessor") 

这将明确指示 Spring 将该 bean 连接到您的类。从那里,您应该从 Spring 收到一条关于为什么它不喜欢预期的 bean 的错误消息。

在我的例子中,我遇到了这样的错误:

Caused by: org.springframework.beans.factory.BeanNotOfRequiredTypeException: 
Bean named 'applicationEnrichmentProcessor' must be of type [com.xxxx.YyyyProcessor], 
but was actually of type [com.sun.proxy.$Proxy24]

看完Fixing BeanNotOfRequiredTypeException on Spring proxy cast on a non-singleton bean?this Spring forum post ,我发现通过添加此注释解决了问题:

@EnableAspectJAutoProxy(proxyTargetClass=true) 

这会导致 Spring 的代理机制代理您的类,而不是类的接口(interface),如 Spring documentation 中所述。 .

由于您的 BatchFileRecordProcessor 实现了一个 ItemProcessor 接口(interface),我怀疑这也是您的问题。

关于java - 尝试编写 Spring Batch 单元测试时出现 NoSuchBeanDefinitionException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22953776/

相关文章:

java - SQLite驱动程序: Getting issue with getBinaryStream when Blob is null

java - 如何在 jOOQ 中编写计数查询

java - 如何将 Spring Controller 中的对象列表绑定(bind)到 thymeleaf

java.lang.NoSuchMethodError : org. springframework.web.context.request.ServletRequestAttributes.<初始化>

c# - 如何对 ArgumentNullException 的重载构造函数进行单元测试,其中每个构造函数只有一个参数

java - 局部变量的初始化

java - JaxB 在大文档上的性能

mysql - H2 时间戳 - 始终插入(或获取)当前日期

python - 如何使用模拟作为函数参数在 Python 中修补常量

java - 为什么 FetchType.EAGER 在单元测试中不起作用?