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)
Related
Trying to build a task executor app in spring boot. The idea is to design a template to retrieve the default TaskConfig so that executor can just execute it.
#Component
public class TaskExecutor {
private final TaskTemplate taskTemplate;
#Autowired
public TaskExecutor(TaskTemplate taskTemplate) {
this.taskTemplate=taskTemplate;
}
public void runTask() {
final TaskConfiguration taskConfig = taskTemplate.getTaskConfig("taskName");
taskConfig.do();
}
}
#Component
public class TaskTemplate {
private final TaskParam1 taskParam1;
private final TaskParam2 taskParam2;
#Autowired
public TaskTemplate(TaskParam1 taskParam1, TaskParam2 taskParam2) {
this.taskParam1 = taskParam1;
this.taskParam2 = taskParam2;
}
public TaskConfiguration getTaskConfig() {
// Logic to build the task configuration from task template params
}
}
The problem I see is that the TaskTemplate is coupled with the TaskExecutor (Autowired), which I wish to remove.
I wanted to replace it with a static convenient method to return the singleton Template so that I could execute the getTaskConfig with it.
Looking for suggestion to improve upon this.
Thanks
You can inject ApplicationContext to another bean like below code. After spring initialized you can use BeanGetter.getTaskTemplate() to get TaskTemplate singleton bean.
...
final TaskConfiguration taskConfig = BeanGetter.getTaskTemplate().getTaskConfig("taskName");
...
#Service
public class BeanGetter {
private static ApplicationContext applicationContext;
#Autowired
public BeanGetter(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public static TaskTemplate getTaskTemplate(){
return applicationContext.getBean(TaskTemplate.class);
}
}
I have this situation:
public class Other {
public void test() {
new ClassA().process();
}
}
public class ClassA {
#Autowired
private ClassB classB;
public void process() {
classB.executeSomething(); //--> NUllPOinter because classA was not created by spring.
}
}
#Service
public class ClassB {
public void executeSomething() {
// execute something
}
}
I tried use ApplicationContext but the problem continued.
Someone, have a idea what i should do ?
Thanks.
This is always the right way to declare a class dependency (always for annotated ioc spring bean):
public class ClassA {
private final ClassB objectB;
public ClassA(final ClassB objectB) {
this.objectB = objectB;
}
public void process() {
objectB.executeSomething();
}
}
In Other class, we should retrieve the singleton ClassB instance. Then it must be a bean component.
#Component
public class Other {
private final ApplicationContext applicationContext;
#Autowired
public Other(final ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public void test() {
new ClassA(applicationContext.getBean(ClassB.class)).process();
}
}
If Other class can't be a spring component, you can't retrieve the application context and then you can't inject the ClassB instance.
You can clearly autowire directly the ClassB object instance into Other component, but this example underline that you can handle bean fetching with an ApplicationContext ref.
This question already has answers here:
Why is my Spring #Autowired field null?
(21 answers)
Closed 3 years ago.
I'm doing a Spring Boot 2.1.6 project.
I have a class ScheduledTasks when I have an autowired object db which gives me access to jdbcTemplate so I can perform queries. When I call the start from main which is another file the db object is null. If I place the start method directly in main class db is not null.
I'm not sure what the issue is. I place #Component annotation in ScheduledTasks so that Spring is aware of my autowired object. What am I missing?
This is my ScheduledTasks:
#Component
public class ScheduledTasks {
private Logger log = VastLogger.getLogger(TrackingEventController.class);
#Autowired
private DBHandler db;
public void start() {
if (db == null) {
log.info("db is null from parent");
}
}
}
this is my main class:
#SpringBootApplication
#EnableJms
public class ServerMain implements CommandLineRunner {
private static final Logger log = LogManager.getLogger(ServerMain.class);
#Autowired
private DBHandler db;
public static void main(String[] args) {
log.warn("from main");
ConfigurableApplicationContext context = SpringApplication.run(ServerMain.class, args);
}
#Override
public void run(String... strings) throws Exception {
log.info("starting run");
db.initDBTables();
ScheduledTasks tasks = new ScheduledTasks();
tasks.start();
}
You are creating ScheduledTasks using new. In that case, you are not using an object created by spring hence auto-wire will not work. You should also wire the ScheduledTasks object in the main class.
#SpringBootApplication
#EnableJms
public class ServerMain implements CommandLineRunner {
private static final Logger log = LogManager.getLogger(ServerMain.class);
#Autowired
private DBHandler db;
#Autowired
private ScheduledTasks tasks;
public static void main(String[] args) {
log.warn("from main");
ConfigurableApplicationContext context = SpringApplication.run(ServerMain.class, args);
}
#Override
public void run(String... strings) throws Exception {
log.info("starting run");
db.initDBTables();
tasks.start();
}
My service classes in my spring MVC program are still null upon running the application. Both classes are #Service and have #Autowired in the controller class but are still null. I've browsed around for a few days and all I've found is 2 causes, neither of which ( I believe) apply to my circumstance.
Spring boot app trying to create a discord bot, autowiring not working in controller or Junit test (NPE upon execution, and variable shows null while debugging).
Driver class:
package com.deth;
//imports
#SpringBootApplication
public class DethBotApplication {
private static Logger logger = Logger.getLogger(DethBotApplication.class);
#Autowired
private static BotCommandListener botListener;
public static void main(String[] args) {
SpringApplication.run(DethBotApplication.class, args);
try {
JDA jda = new JDABuilder(AccountType.BOT)
.setToken(TOKEN)
//.addEventListener(command controller)
.addEventListener(botListener)
.build(); //starts listening in discord server.
Relevant controller code:
package com.deth.controller;
//imports
#Component
public class BotCommandListener extends ListenerAdapter {
private static Logger logger = Logger.getLogger(BotCommandListener.class);
#Autowired
#Qualifier("raidAdminService")
private RaidAdminService raidAdminService;
#Autowired
private RaidRosterServiceImpl raidRosterService;
#Autowired
private RaidAttendanceService raidAttendanceService;
#Override
public void onMessageReceived(MessageReceivedEvent event) {
JDA jda = event.getJDA();
String msg = event.getMessage().getContentDisplay();
if(msg.startsWith("!")) {
String command = "";
if(!msg.contains(" ")) {
command = msg;
} else {
command = msg.subSequence(0, msg.indexOf(" ")).toString();
logger.trace("possible command: " + command);
}
try {
switch (command) {
//raid leader commands
case "!open":
raidAdminService.createRaid(event); //NPE here
logger.trace("!open detected");
break;
raidAdminService:
package com.deth.service;
//imports
#Service("raidAdminService")
public class RaidAdminServiceImpl extends CommandInfoService implements RaidAdminService {
String intRegex = "[0-9]+";
#Override
public void createRaid(MessageReceivedEvent event) {
// TODO Auto-generated method stub
package structure:
com
deth
DethBotApplication
Controller
DethBotCommandListner
Service
RaidAdminService (interface)
RaidAdminServiceImpl (class)
....
while program is up & running, send "!open" in discord server, correctly hitting the switch statement and trying to call createRaid method, but RaidAdminService wasn't autowired so its calling the method on null.
I think issue is in your DethBotApplication class. you can't autowire there. main class need to be executed first. after that app will look for #Componet, #Service, #Controller... annotations. below code might fix your issue.
package com.deth;
//imports
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.ConfigurableApplicationContext;
#SpringBootApplication
public class DethBotApplication extends SpringBootServletInitializer {
private static Logger logger = Logger.getLogger(DethBotApplication.class);
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(DethBotApplication.class, args);
BotCommandListener botListener = context.getBean(BotCommandListener.class);
try {
JDA jda = new JDABuilder(AccountType.BOT)
.setToken(TOKEN)
//.addEventListener(command controller)
.addEventListener(botListener)
.build(); //starts listening in discord server.
Look at that :
#Autowired
private static BotCommandListener botListener;
public static void main(String[] args) {
SpringApplication.run(DethBotApplication.class, args);
try {
JDA jda = new JDABuilder(AccountType.BOT)
.setToken(TOKEN)
.addEventListener(botListener)
//...
}
You annotate #Autowired a static field while Spring doesn't inject bean in static fields (but only in instance fields).
Your problem is indeed very common : you want to perform some tasks in the main() of the Spring Boot application class that depend on some beans.
The right approach is using the #PostConstruct annotation. A method annotated with that is (automatically) executed once the dependency injection was performed on the current bean : here the Spring Boot Application.
It would give :
#Autowired
private BotCommandListener botListener;
#PostConstruct
public void init(){
JDA jda = new JDABuilder(AccountType.BOT)
.setToken(TOKEN)
.addEventListener(botListener)
.build();
// ...
}
You instantiated the object manually, so Spring doesn't know about that bean. That's why your #Autowired field is null.
Spring boot has to load primary class first, then continue to the injected bean through these class.
Extend either an ApplicationRunner or CommandLineRunner class
package com.deth;
//imports
#SpringBootApplication
#Component
public class DethBotApplication implements CommandLineRunner {
private static Logger logger = Logger.getLogger(DethBotApplication.class);
#Autowired
private /*static*/ BotCommandListener botListener; // i remove "static" here
public static void main(String[] args) {
SpringApplication.run(DethBotApplication.class, args);
}
#Override
public void run(String args) {
try {
JDA jda = new JDABuilder(AccountType.BOT)
.setToken(TOKEN)
//.addEventListener(command controller)
.addEventListener(botListener)
.build(); //starts listening in discord server.
...
The key here is (as other answers point out) #Autowired only works if it is a #Component and not "static".
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.