0

As far as I know spring provides some ways to inject beans into non-managed classes. It can be done explicitly with AutowireCapableBeanFactory. (How to inject dependencies into a self-instantiated object in Spring?)

But I've faced strange (IMHO) behavior, when spring performs such injection automatically.

Here is an example with spring batch,

Configuration:

@SpringBootConfiguration 
public class ProcessorJobConfig {

//.....

@Bean(name = "pullRestTemplate")
public RestTemplate createPullRestTemplate() {
    RestTemplate restTemplate = restTemplateBuilder.build();

    return restTemplate;
}

@Bean(name = "step")
public Step step(@Autowired ItemReader<Measurement> itemReader,
                 @Autowired ItemProcessor<Measurement, Event> itemProcessor,
                 @Autowired ItemWriter<Event> itemWriter) {
    return stepBuilderFactory.get("step")
            .<Measurement, Event>chunk(Integer.MAX_VALUE)
            .reader(itemReader)
            .processor(itemProcessor)
            .writer(itemWriter)
            .build();
}

@Bean(name = "restProcessorJob")
public Job job(@Qualifier("step") Step step) throws Exception {
    return jobBuilderFactory.get("restProcessorJob")
            .start(step)
            .build();
}

@Bean
public ItemReader<Measurement> itemReader() {
    RestMeasureReader restMeasureReader = new RestMeasureReader(); // Use new() explicitly
    return restMeasureReader;
}

//.....
}

Reader:

public class RestMeasureReader implements ItemReader<Measurement> {
private static final Logger LOGGER = LoggerFactory.getLogger(RestMeasureReader.class);

/**
 * NOTE: This field will be injected automatically by spring, even we are using new() to create instance of this class.
 */
@Autowired
@Qualifier("pullRestTemplate")
private RestTemplate restTemplate;

@Override
public Measurement read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException {
    // do some stuff
}
}

And application itself

@EnableBatchProcessing
@EnableTask
@SpringBootApplication
public class TestAutowiredTaskApplication {
    public static void main(String[] args) {
        SpringApplication.run(TestAutowiredTaskApplication.class, args);
}
}

Even I use explicit new() to instantiate RestMeasureReader, its RestTemplate field will be injected afterwards. Is it normal behavior? I do not expect spring to automatically inject fields when creating object with new().

Community
  • 1
  • 1
  • 1
    You are creating a new instance of a class inside a method annotated with the spring `@Bean` annotation... Which makes it a spring managed bean. It would be true if you remove the `@Bean` annotation as then it isn't a spring managed bean. I suggest a read of the reference guide especially the section on java based configuration. – M. Deinum Dec 29 '16 at 14:23

2 Answers2

3

If you are talking about using new inside of your @Configuration class, then yes it is normal behavior. This is you Spring java configs. So it's is Spring managed context. You are not going to call itemReader() in your code explicitly. So, when you are going to do this:

@Autowired
private ItemReader<Measurement> iterReader;

you will get instance of your RestMeasureReader from Spring's IoC.

But if you will try to do explicitly call new RestMesureReader() inside of your code, you will get a new instance of RestMesureReader not a Spring Proxy with injected @Autowired fields.

Try to remove @Bean from your itemReader() method declaration and won't event be able to autowire RestMesureReader.

So basically @Configuration classes are just a Spring configuration, not a real java code. Even though you call new Spring will still return you a proxy class.

For more information check this guide.

Sergii Bishyr
  • 8,331
  • 6
  • 40
  • 69
1

Spring processes beans returned by methods that are annotated with @Bean This allows you to use autowiring or livecycle callbacks when using Java configuration.

A more minimalistic example:

@Configuration
public class MyConfiguration {

  @Bean
  public A a() {
    return new A();
  }

  static class A {
    @Autowired
    private B b;

    @PostConstruct
    public void onPostConstruct() {
      System.out.println("postConstruct: " + b);
    }
  }

  @Component
  static class B {
  }
}

Here, even if the bean named a is created manually, Spring will inject dependencies (b) and call @PostConstruct callbacks.

micha
  • 47,774
  • 16
  • 73
  • 80