I'm using Spring Boot and I'm not able to inject a task executor in a service bean.
Here's some code:
#Service
public class ClassA {
#Autowired
private ThreadPoolTaskExecutor taskExecutor;
public void doSht(){
for(int i = 0; i<this.taskExecutor.getMaxPoolSize(); i++){
this.taskExecutor.execute(new ClassB());
}
}
}
Class B:
public class ClassB implements Runnable {
#Override
public void run() {
System.out.println("Class B running");
}
}
Controller:
#Controller
public class IndexController {
#Autowired
ClassA ca;
#RequestMapping("/")
public String index(){
return "index";
}
#RequestMapping("test")
public String test(ClassA ca){
ca.doSht();
return "test";
}
}
And here's the task executor configuration:
#SpringBootApplication
public class App{
#Bean
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(5);
taskExecutor.setMaxPoolSize(10);
taskExecutor.setQueueCapacity(30);
return taskExecutor;
}
public static void main(String[] args) throws Exception{
SpringApplication app = new SpringApplication(App.class);
app.run(args);
}
}
I want the ClassB instances being executed when a request comes to /test, but I get a NullPointerException because the task executor being not autowired into ClassA bean.
What am I doing wrong?
To fix the error please see the following instructions:
Go to IndexController class
Go to public String test(ClassA ca) method
Remove Class ca input parameter form test method
test method should be like this
test method changed:
#RequestMapping("test")
public String test(){
ca.doSht();
return "test";
}
The null pointer exception is caused because test method is using ca method argument instead of ca object that comes from #Autowired annotation
As your using #SpringBootApplication in App class , it is registering your custom bean definition to IOC and also it is autowiring the bean correctly. Otherwise you'll get error on autowiring class.
So its not the issue with autowiring. You can set a debug point to check that. Now in your controller you messed up your instance variable with method argument by using same name. Moreover #RequestMapping not supplying your custom class objects. So it is coming as null and you're get the exception.
Hope you understand the issue.
Related
I have read many questions and answers about using #Scheduled with #Async in Spring but no one resolves my problem and my asynchronous method still runs single-threaded. So here is my Configuration class:
#EnableScheduling
#EnableAsync
#Configuration
#RequiredArgsConstructor
public class SchedulerConfiguration {
private final ThreadPoolProperties threadPoolProperties;
#Bean
public TaskExecutor commonTaskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(threadPoolProperties.getCorePoolSize()); // 10
taskExecutor.setMaxPoolSize(threadPoolProperties.getMaxPoolSize()); // 20
taskExecutor.setQueueCapacity(threadPoolProperties.getQueueCapacity()); // 5
taskExecutor.setThreadNamePrefix("TEST");
taskExecutor.initialize();
return taskExecutor;
}
}
Then we have a bean with the #Scheduled method:
#Component
#RequiredArgsConstructor
public class ScheduledTask {
private final ConfirmReservationTask confirmReservationTask;
#Scheduled(cron = "${booking.scheduler.confirmReservationsCron}")
public void process() {
confirmReservationTask.confirmReservations();
}
}
And finally, another bean (to avoid self-injection and proxy problems with asynchronous processing) with #Async method:
#Log4j2
#Component
#RequiredArgsConstructor
public class ConfirmReservationTask {
private final ReservationService reservationService;
#Async("commonTaskExecutor")
public void confirmReservations() {
...
}
}
unfortunately, this solution works in only one thread, however, the method uses the correct ThreadExecutor. How to solve it?
How to run code from class with #SpringBootApplication annotation. I want to run my code without calling to controller and get info from terminal not web browser. I tried to call weatherService in #SpringBootApplication but I've got a application failed start with description
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
| weatherClientApplication
↑ ↓
| weatherService defined in file [C:\Users\xxx\IdeaProjects\weatherclient\target\classes\com\xxx\restapiclient\service\WeatherService.class]
└─────┘
#SpringBootApplication
public class WeatherClientApplication {
private WeatherService weatherService;
public WeatherClientApplication(WeatherService weatherService) {
this.weatherService = weatherService;
}
private static final Logger log = LoggerFactory.getLogger(WeatherClientApplication.class);
public static void main(String[] args) {
SpringApplication.run(WeatherClientApplication.class, args);
}
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder){
return builder.build();
}
#Bean
public CommandLineRunner run(RestTemplate restTemplate) throws Exception {
return args -> {
log.info(weatherService.getTemperatureByCityName("Krakow"));
};
}
}
#Service
public class WeatherService {
private RestTemplate restTemplate;
public WeatherService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
public String getTemperatureByCityName(String cityName) {
String url = "http://api.openweathermap.org/data/2.5/weather?q=" + cityName + "&APPID=" + API_KEY + "&units=metric";
Quote quote = restTemplate.getForObject(url, Quote.class);
return String.valueOf(quote.getMain().getTemp());
}
}
You can do this by using main method and by using ApplicationContext, In this approach you don't need any CommandLineRunner
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(WeatherClientApplication.class, args);
WeatherService service = (WeatherService)context.getBean("weatherService");
service. getTemperatureByCityName("cityname");
}
1) What you want is implementing CommandLineRunner and define the entry point of your application in the public void run(String... args) method defined in this interface.
2) As said by Spring you have a cycle : break it with a injection outside the constructor.
Such as :
#SpringBootApplication
public class WeatherClientApplication implements CommandLineRunner{
#Autowired
private WeatherService weatherService;
//...
#Override
public void run(String... args) {
log.info(weatherService.getTemperatureByCityName("Krakow"));
}
//...
}
Generally constructor injection should be favored over field or setter injection but in your case, that is acceptable.
You are creating a cycle as you are injecting a service in the #SpringBootApplication itself. Constructor injection means that nothing can really happen until the class is built but that service is going to be created later on.
Don't use field injection on your #SpringBootApplication as it represents the root context. Your CommandLineRunner injects a RestTemplate but you are not using it. If you replace that by the WeatherService and remove the constructor injection, things should work just fine.
I am glad you find the weather application useful by the way :)
I have my main Application annotated with #SpringBootApplication. Here is the code for the following:
#SpringBootApplication
public class Application {
private Logger logger = LoggerFactory.getLogger(Application.class);
#Autowired
public ExternalConfiguration configuration;
#Autowired
WorkerThread workerThread;
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(new Object[] { Application.class });
springApplication.run(args);
}
}
And here is my WorkerThread.java
#Component
#Scope("prototype")
public class WorkerThread implements Runnable {
#Autowired
private ApplicationContext applicationContext;
#Autowired
ExternalConfiguration externalConfiguration;
#Autowired
WorkerConfig workerConfig;
WorkerQueueDispatcher dispatcher;
public WorkerThread() {
dispatcher = applicationContext.getBean(WorkerQueueDispatcher.class, externalConfiguration.getEventQ(),
workerConfig.getWorkers());
}
#Override
public void run() {
logger.info("Worker thread started. Thread ID :" + Thread.currentThread().getId());
dispatcher.run();
}
}
I tried debugging and found out that my ApplicationContext was not getting Autowired and is null.
I haven't used new for instantiating the WorkerThread.
Please help me.
Your problem is that you are using autowired fields in constructor here:
public WorkerThread() {
dispatcher = applicationContext.getBean(WorkerQueueDispatcher.class, externalConfiguration.getEventQ(),
workerConfig.getWorkers());
}
Constructor is called by spring before it is able to inject these dependencies. Therefore they are all null.
You have two options:
Do your initialization in #PostContruct instead of constructor.
Use constructor injection (it is good practice anyways)
Im fairly new to Java Spring IoC and here's my problem
I have a FactoryConfig class with all beans and annotation #Configuration and #ComponentScan written as below.
import org.springframwork.*
#Configuration
#ComponentScan(basePackages="package.name")
public class FactoryConfig {
public FactoryConfig() {
}
#Bean
public Test test(){
return new Test();
}
//And few more #Bean's
}
My Test class has a simple Print method
public class Test {
public void Print() {
System.out.println("Hello Test");
}
}
Now in my Main Class Ive created an ApplicationContentext of FactoryConfig. (I'm expecting all of my #Beans in Factory config will be initialised. However, it returns null when I access the Test class using #Autowired
My Main Class
public class Main {
#Autowired
protected static Test _autoTest;
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
ApplicationContext context =
new AnnotationConfigApplicationContext(FactoryConfig.class);
FactoryConfig config = context.getBean(FactoryConfig.class);
config.test().Print();
// _autoTest.Print(); <--- Im getting NULL Pointer Ex here
}
}
What is the correct way to #Autowire and use objects/beans? any clearer explanation would be much appreciated.
Only beans managed by Spring can have #Autowire annotations. Your main class is not managed by Spring: it's created by you and not declared in a Spring context: Spring doesn't known anything about your class, and doesn't inject this property.
You can just access in your main method the Test bean with :
context.getBean(Test.class).Print();
Usually, you get a "bootstrap" from the context, and call this bootstrap to start your application.
Moreover:
On Java, a method shouldn't start with an uppercase. Your Test class should have a print method, not Print.
If you start with Spring, you should maybe try Spring Boot
Spring does not manage your Main class, that's why you are getting Nullpointer Exception.
Using ApplicationContext to load beans, you can get your beans and access Methods as you are already doing -
ApplicationContext context =
new AnnotationConfigApplicationContext(FactoryConfig.class);
FactoryConfig config = context.getBean(FactoryConfig.class);
config.test().Print();
remove the static argument
protected Test _autoTest;
Your class
public class Test {
public void Print() {
System.out.println("Hello Test");
}
}
is not visible to Spring. Try adding an appropriate annotation to it, like #Component.
The reason is that your Main is not managed by Spring. Add it as bean in your configuration:
import org.springframwork.*
#Configuration
#ComponentScan(basePackages="package.name")
public class FactoryConfig {
public FactoryConfig() {
}
#Bean
public Test test(){
return new Test();
}
#Bean
public Main main(){
return new Main();
}
//And few more #Bean's
}
And then you can edit your main() as follows:
public class Main {
#Autowired
protected Test _autoTest;
public static void main(String[] args) throws InterruptedException {
ApplicationContext context =
new AnnotationConfigApplicationContext(FactoryConfig.class);
Test test = context.getBean(Test.class);
Main main = context.getBean(Main.class);
test.Print();
main._autoTest.Print();
}
}
This is very similar to the other question here: Spring Boot #Async method in controller is executing synchronously. However my #Service method annotated with #Async is still executing synchronously. I've tried all methods from different forums to no use. Hopefully someone could help me figure out why. A simple spring boot project as below doesn't work.
AsyncConfiguration.java
#Configuration
#EnableAsync
public class AsyncConfiguration(){}
SomeService.java
#Service
public class SomeService() {
#Async
public void doSomething() {
try {
Thread.sleep(5000L);
} catch (Exception ignore){}
}
}
SomeController.java
#Controller
public class SomeController() {
#Inject SomeService someService;
#RequestMapping(value="/", method=RequestMethod.POST)
public String doStuff() {
someService.doSomething();
return "mytemplate";
}
}
Here is a simple example with #Async. Follow these steps to get #Async to work in your Spring Boot application:
Step 1: Add #EnableAsync annotation and Add TaskExecutor Bean to Application Class.
Example:
#SpringBootApplication
#EnableAsync
public class AsynchronousSpringBootApplication {
private static final Logger logger = LoggerFactory.getLogger(AsynchronousSpringBootApplication.class);
#Bean(name="processExecutor")
public TaskExecutor workExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setThreadNamePrefix("Async-");
threadPoolTaskExecutor.setCorePoolSize(3);
threadPoolTaskExecutor.setMaxPoolSize(3);
threadPoolTaskExecutor.setQueueCapacity(600);
threadPoolTaskExecutor.afterPropertiesSet();
logger.info("ThreadPoolTaskExecutor set");
return threadPoolTaskExecutor;
}
public static void main(String[] args) throws Exception {
SpringApplication.run(AsynchronousSpringBootApplication.class,args);
}
}
Step 2: Add Method which executes an Asynchronous Process
#Service
public class ProcessServiceImpl implements ProcessService {
private static final Logger logger = LoggerFactory.getLogger(ProcessServiceImpl.class);
#Async("processExecutor")
#Override
public void process() {
logger.info("Received request to process in ProcessServiceImpl.process()");
try {
Thread.sleep(15 * 1000);
logger.info("Processing complete");
}
catch (InterruptedException ie) {
logger.error("Error in ProcessServiceImpl.process(): {}", ie.getMessage());
}
}
}
Step 3: Add an API in the Controller to execute the asynchronous processing
#Autowired
private ProcessService processService;
#RequestMapping(value = "ping/async", method = RequestMethod.GET)
public ResponseEntity<Map<String, String>> async() {
processService.process();
Map<String, String> response = new HashMap<>();
response.put("message", "Request is under process");
return new ResponseEntity<>(response, HttpStatus.OK);
}
I have also written a blog and a working application on GitHub with these steps. Please check:
http://softwaredevelopercentral.blogspot.com/2017/07/asynchronous-processing-async-in-spring.html
Sorry for my English.
The same problem happened to me.
The solution was to add
#Autowired
private SomeService someService;
In the Controller class, in this way it allows the class to contain the Beam Configurations associated with "SomeService", thus being able to execute the asynchronous method perfectly.
Here is a project with a functional asynchronous method:
https://github.com/JColmenares/async-method-api-rest.git