How to use autowired (@Autowired) references from main(String[] args) method?

狂风中的少年 提交于 2019-12-11 01:44:20

问题


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


回答1:


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 PostConstructwill be invoked one and once.




回答2:


You can do one of the following:

  1. Use the @Autowired object in a @PostConstruct method, which is executed after dependency injection is done, as davidxxx explained above

  2. Use Spring's getBean() in your main() to explicitly ask Spring framework to return the object after the injection completes:

    public static void main(String[] args) {
        ...
        ConfigurableApplicationContext appContext = SpringApplication.run(StartApplication.class, args);
        IZipCodeLookup service = appContext.getBean(IZipCodeLookup.class);
        ...
    }
    
  3. Use Spring's CommandLineRunner component (runs right after main), which will be responsible on autowiring your object:

    @Component
    public class MyRunner implements CommandLineRunner {
    
        @Autowired
        private IZipCodeLookup service;
    
        @Override
        public void run(String... args) throws Exception {
            ...
            service.doSomething();
            ... 
        }
    }
    
  4. Implement Spring's ApplicationRunner's run method in your main:

    @SpringBootApplication
    public class StartApplication implements ApplicationRunner {
    
        @Autowired
        private IZipCodeLookup service;
    
        public static void main(String[] args) {
            ConfigurableApplicationContext appContext = SpringApplication.run(StartApplication.class, args);
        }
    
        @Override
        public void run(ApplicationArguments args) throws Exception {
            ...
            service.doSomething();
            ... 
        }
    }
    


来源:https://stackoverflow.com/questions/46617044/how-to-use-autowired-autowired-references-from-mainstring-args-method

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!