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".
Related
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();
}
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'm building a lambda based on this code
The uppercaseService is "injected" like this:
#Component("uppercaseFunction")
public class UppercaseFunction implements Function<UppercaseRequest, UppercaseResponse> {
private final UppercaseService uppercaseService;
public UppercaseFunction(final UppercaseService uppercaseService) {
this.uppercaseService = uppercaseService;
}
This works fine until I try to inject another service inside UppercaseService.
#Service
public class UppercaseService {
#Autowired
MyService myService;
public String uppercase(final String input) {
myService.doSomething();
return input.toUpperCase(Locale.ENGLISH);
}
}
AWS console returns:
"errorMessage": "Error creating bean with name 'uppercaseService':
Unsatisfied dependency expressed through field 'myService'
This service works in a non lambda context. The class is present in the .jar built with maven package.
I tried the solution # https://www.profit4cloud.nl/blog/just-spring-enabled-aws-lambdas without success.
You have to initialize your MyService bean first. Since your MyService come from external service which very likely to have different package than your own package
Either directly:
#SpringBootApplication
public class UpperFunctionApplication {
#Bean
public MyService myService() {
return new MyService(); // You must provide code to construct new MyService bean
}
public static void main(String[] args) throws Exception {
SpringApplication.run(UpperFunctionApplication.class, args);
}
}
or via componentscan:
#SpringBootApplication(scanBasePackageClasses = {UpperFunctionApplication.class, MyService.class})
public class UpperFunctionApplication {
#Bean
public MyService myService() {
return new MyService(); // You must provide code to construct new MyService bean
}
public static void main(String[] args) throws Exception {
SpringApplication.run(UpperFunctionApplication.class, args);
}
}
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)
Why when running spring tests with #ContextConfiguration(...) #Autowired works automatically and when running Java application I get NullPointerException?
With following example I get NullPointerException:
public class FinalTest {
#Autowired
private App app;
public FinalTest() {
}
public App getApp() {
return app;
}
public void setApp(App app) {
this.app = app;
}
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
FinalTest finalTest = new FinalTest();
finalTest.getApp().getCar().print();
finalTest.getApp().getCar().getWheel().print();
}
}
With following example it works:
public class FinalTest {
private App app;
public FinalTest() {
}
public App getApp() {
return app;
}
public void setApp(App app) {
this.app = app;
}
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
FinalTest finalTest = new FinalTest();
finalTest.setApp((App)context.getBean("app"));
finalTest.getApp().getCar().print();
finalTest.getApp().getCar().getWheel().print();
}
}
In tests no need of doing context.getBean(), it just works with #Autowired:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"/applicationContext-test.xml"})
public class AppTest{
#Autowired
private App app;
#Test
public void test(){
assertEquals("This is a SEAT_test car.", this.app.getCar().toString());
assertEquals("This is a 10_test wheel.", this.app.getCar().getWheel().toString());
}
}
Thanks.
Anytime you use #Autowired, the class into which the dependency is going to be injected needs to be managed by Spring.
A test with:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"/applicationContext-test.xml"})
is managed by Spring. When the annotations do not exist, the class is not managed by Spring and therefor no
dependency injection is performed
You're expecting Spring to be able to inject beans into an instance it doesn't manage.
You're creating your object manually
FinalTest finalTest = new FinalTest();
Spring can only inject beans into objects it manages. Here, Spring has nothing to do with the object created above.
Declare a FinalTest bean in your context and retrieve it. It will have been autowired if your configuration is correct.