0

Until today I was 100 percent sure I don't have to create new instance of class when defining it as a bean. Today I got a bit confused.

I will try to explain part of it in words as I think uploading all the code will make it hard to understand.

I created new REST project using intellij and Spring. I created new class mapped it and added @RestController to it. In this class I added property of another class that I created myself and added @Autowired to it. I never created new instance of this class BUT I did add a bean configuration. until now all worked fine.

I wanted to add ThreadPoolTaskScheduler logic so I opened new class, added new property ThreadPoolTaskScheduler and marked it with @Autowired.

I added a Bean for it:

 @Bean
public ThreadPoolTaskScheduler threadPoolTaskScheduler(){
    ThreadPoolTaskScheduler threadPoolTaskScheduler
            = new ThreadPoolTaskScheduler();
    threadPoolTaskScheduler.setPoolSize(5);
    threadPoolTaskScheduler.setThreadNamePrefix(
            "ThreadPoolTaskScheduler");
    return threadPoolTaskScheduler;
}

Now in the main class if I don't send new instance of the class if will throw me null exception.

so this code is working:

public static void main(String[] args) {
    SpringApplication.run(RestApiApplication.class, args);
    TaskScheduler taskScheduler = new TaskScheduler(new ThreadPoolTaskScheduler());
    taskScheduler.setTaskScheduler();
}

and this code is not:

public static void main(String[] args) {
    SpringApplication.run(RestApiApplication.class, args);
    TaskScheduler taskScheduler = new TaskScheduler();
    taskScheduler.setTaskScheduler();
}

this is the TaskScheduler class:

    @Controller
public class TaskScheduler {
    @Autowired
    ThreadPoolTaskScheduler threadPoolTaskScheduler;
    TaskScheduler(){}
    TaskScheduler(ThreadPoolTaskScheduler threadPoolTaskScheduler){
        this.threadPoolTaskScheduler = threadPoolTaskScheduler;
    }

    public void setTaskScheduler(){
        threadPoolTaskScheduler.schedule(
                new ScheduledTask(),
                new Date());
    }
}
  1. I can't figure out the reason get NULL for threadPoolTaskScheduler at setTaskScheduler, any idea?
  2. If I definde TaskScheduler also as a bean it works ok, why do I have to? spring can handle everthing or nothing?

If you want me to add more code just tell me.

M. Deinum
  • 115,695
  • 22
  • 220
  • 224
assaf.gov
  • 517
  • 6
  • 19
  • 4
    When you use `new` on an object, you are excluding it from Spring control. `TaskScheduler taskScheduler = new TaskScheduler();` - this object is not controlled by spring, it is instantiated by you, so Spring won't resolve any dependency in this case. – BackSlash Dec 13 '18 at 13:55
  • But I don't want to use spring for TaskScheduler I want to use it for ThreadPoolTaskScheduler – assaf.gov Dec 13 '18 at 13:56

2 Answers2

1

When you instantiate a bean itself, it does not make it managed anymore and thus all of the @Autowired dependencies will not be processed. In order to make sure that dependencies are injected properly, you will need to instantiate the bean through the Spring context. Spring needs to instantiate the whole beans trail in order to perform the dependency injection properly. Please check the IoC details in this link: https://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/beans.html#beans-factory-class-ctor

Looking at your code, there are two ways to do this

Using Spring Boot

The class that will have the main method will look like this:

@SpringBootApplication
public class RestApiApplication implements CommandLineRunner {

    @Autowired
    private TaskScheduler ts;

    public static void main(String[] args) {
        SpringApplication.run(RestApiApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        ts.<your-code>
    }

}

The other way is to use the ApplicationContext to get the bean instance directly which works well if you need to perform some integration/unit testing.

Eslam Nawara
  • 645
  • 3
  • 8
0

You can get your bean object like:

public class RestApiApplication{

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(RestApiApplication.class, args);
        TaskScheduler ts = context.getBean(TaskScheduler.class);
        ts.setTaskScheduler()
    }
}

But it's bad idea in real project use ApplicationContext like this example!

If you need REST you should use @RestController annotation. Check this guide

  • Calling it from the main method was just for testing, the question is why do I need to pass new instance while it is defined as a bean – assaf.gov Dec 13 '18 at 14:14
  • @user1913615 You don't need to pass new instance, you just need to `@Autowired YourBean someBean` that bean into your controller. Controller bean would be created by spring too. All beans are stored in Spring IoC container. – Andrii Vdovychenko Dec 13 '18 at 14:23
  • But the @Autowired for ThreadPoolTaskScheduler is on the TaskScheduler class – assaf.gov Dec 13 '18 at 14:34
  • But you create your TaskScheduler using `new` `TaskScheduler taskScheduler = new TaskScheduler();` it's incorrect... – Andrii Vdovychenko Dec 13 '18 at 14:40
  • This is my question, why it is incorrect, can't part of it be autowired and part not? – assaf.gov Dec 13 '18 at 14:58