Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I qualify an autowired setter that I don't "own"

The gist is that the Spring Batch (v2) test framework has JobLauncherTestUtils.setJob with an @Autowired annotation. Our test suite has multiple Job class providers. Since this class is not something I can modify, I'm not sure how I can qualify which job it gets autowired with, which may be different per test.

 STDOUT [WARN ] [2015.04.15 11:14:42] support.GenericApplicationContext - Exception encountered during context initialization - cancelling refresh attempt
 org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jobLauncherTestUtilsForSnapshot': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void org.springframework.batch.test.JobLauncherTestUtils.setJob(org.springframework.batch.core.Job); nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [org.springframework.batch.core.Job] is defined: expected single matching bean but found 2: coverageRuleBatch,generateMetricsSnapshotJob

I've tried adding this JavaConfig which is recognized, but the error says it's still autocalling setJob

@Configuration
public class SpringTestConfiguration
{
@Bean
public JobLauncherTestUtils jobLauncherTestUtilsForSnapshot( final Job generateMetricsSnapshotJob )
{
    JobLauncherTestUtils jobLauncherTestUtils = new JobLauncherTestUtils();
    jobLauncherTestUtils.setJob( generateMetricsSnapshotJob );
    return jobLauncherTestUtils;
}
}

note: I don't require a JavaConfig solution, but it'd be nice. Also, I'd like, if possible, to still Autowire fields like JobRepository, as there is only one.

like image 499
xenoterracide Avatar asked Apr 15 '15 16:04

xenoterracide


People also ask

What is the difference between @autowired and @qualifier?

The difference are that @Autowired and @Qualifier are the spring annotation while @Resource is the standard java annotation (from JSR-250) . Besides , @Resource only supports for fields and setter injection while @Autowired supports fields , setter ,constructors and multi-argument methods injection.

Can we use qualifier with Autowired?

The @Qualifier annotation is used to resolve the autowiring conflict, when there are multiple beans of same type. The @Qualifier annotation can be used on any class annotated with @Component or on methods annotated with @Bean . This annotation can also be applied on constructor arguments or method parameters.

Why autowire is not working?

There are several reasons @Autowired might not work. When a new instance is created not by Spring but by for example manually calling a constructor, the instance of the class will not be registered in the Spring context and thus not available for dependency injection.

How to use Qualifier in Spring XML?

There may be a situation when you create more than one bean of the same type and want to wire only one of them with a property. In such cases, you can use the @Qualifier annotation along with @Autowired to remove the confusion by specifying which exact bean will be wired.


2 Answers

My solution, when I ran into the same problem, was to restrict the component-scan so that only a single Job bean is created in the test context.

@Configuration
@ComponentScan(basePackages={
    "com.example.batch.jobs.metrics",  //package where generateMetricsSnapshotJob is the only job
    "com.example.batch.common",
    "..."
})
public class SpringTestConfiguration
{
    @Bean
    public JobLauncherTestUtils jobLauncherTestUtils()
    {
        //generateMetricsSnapshotJob and other requirements will be autowired
        return new JobLauncherTestUtils();
    }
}

You might need to adjust your package structure for this to work.

like image 161
Alex Wittig Avatar answered Nov 15 '22 17:11

Alex Wittig


The solution I came up with

@Configuration
public class SpringBatchTestConfiguration
{
@Bean
public static JobLauncherTestUtils jobLauncherTestUtilsForSnapshot()
{
    return new SnapshotJobLauncherTestUtils();
}

public static class SnapshotJobLauncherTestUtils extends JobLauncherTestUtils
{
    @Override
    @Qualifier( "generateMetricsSnapshotJob" )
    public void setJob( final Job job )
    {
        super.setJob( job );
    }
}
}

and in the final test

@Autowired
@Qualifier( "jobLauncherTestUtilsForSnapshot" )
protected JobLauncherTestUtils jobLauncherTestUtils;

fairly confident I could just annotate my TestUtils with @Component and name it properly and do the same thing.

like image 38
xenoterracide Avatar answered Nov 15 '22 16:11

xenoterracide