I am trying to use an autowired reference from main class and am facing :
Cannot make a static reference to the non-static field zipCodeLookupService.
This is obvious. But I want to know how to handle this situation. What is the correct way of autowiring when main class is involved. My code will be as below -
Interface class
package com.example.services;
public interface IZipCodeLookup {
String retriveCityForZip(String zipCode);
}
Service Class
package com.example.services;
import org.springframework.stereotype.Service;
@Service
public class ZipCodeLookupService implements IZipCodeLookup {
@Override
public String retriveCityForZip(String zipCode) {
//below is mock code. actual code does a db lookup using a DAO.
if(zipCode=="94123") return "San Francisco";
return "not found in DB";
}
}
here is the main class that requires the service class
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import com.example.services.IZipCodeLookup;
@SpringBootApplication
public class AutowireWithMainClassApplication {
@Autowired
IZipCodeLookup zipCodeLookupService;
public static void main(String[] args) {
SpringApplication.run(AutowireWithMainClassApplication.class, args);
String city;
//this will not work, compilation error
//Cannot make a static reference to the non-static field zipCodeLookupService
city=zipCodeLookupService.retriveCityForZip(args[0]);
System.out.println("city for zipcode " + args[0] + " is " +city);
}
}
Could someone suggest - how or what is the correct way of using autowiring when main class is involved.
(As making the Autowired reference as static does not work anyway)
in AutowireWithMainClassApplication
class, changing to -
@Autowired
static IZipCodeLookup zipCodeLookupService;
throws
Exception in thread "main" java.lang.NullPointerException
A class annotated with a @SpringBootApplication
annotation is not a classic bean.
It creates the Spring context from a static method.
But autowired dependencies cannot be static.
That's why this statement :
city=zipCodeLookupService.retriveCityForZip(args[0]);
doesn't throw a Spring exception but a classic NullPointerException
as you declare zipCodeLookupService
as a static
field.
In your case, as workaround, you could move the processing that uses the Spring bean in a instance method annotated with javax.annotation.PostConstruct
method inside your main class and store the arguments passed to the main()
method in a field in order to be able to use it later :
private static String[] args;
@Autowired
IZipCodeLookup zipCodeLookupService;
public static void main(String[] args) {
AutowireWithMainClassApplication.args = args;
SpringApplication.run(AutowireWithMainClassApplication.class, args);
}
@PostConstruct
public void init() {
String city=zipCodeLookupService.retriveCityForZip(args[0]);
System.out.println("city for zipcode " + args[0] + " is " +city);
}
To answer to your comment, you should note several things about @PostConstruct
1) It is not an annotation specific to Spring. So, the official documentation may discuss about things more general than Spring or specific but different things such as EJB (it was originally introduced for them).
2) The first sentence of the javadoc summarizes the general expected behavior.
The PostConstruct annotation is used on a method that needs to be executed after dependency injection is done to perform any initialization.
But this sentence
"executed after dependency injection is done"
means indeed :
"executed after all dependency injections are done"
We talk about dependency injection in general, not about each dependency injection.
So, yes stick you to that.
Applying it to your case should make things clearer.
The AutowireWithMainClassApplication
class is considered as a Spring bean as @SpringBootApplication
is annotated with @Configuration
that is itself annotated with @Component
.
And as any Spring bean, it may declare dependency injection.
That is a dependency injection :
@Autowired
IZipCodeLookup zipCodeLookupService;
But you could of course declare as many dependency injections that you want to :
@Autowired
IZipCodeLookup zipCodeLookupService;
@Autowired
OtherClass otherClass;
...
So only as all dependencies are effectively injected, the PostConstruct
will be invoked one and once.