如何使用 main(String[] args) 方法中的自动装配(@Autowired)引用?

sam*_*ers 6 spring dependency-injection inversion-of-control autowired spring-boot

我正在尝试使用来自主类的自动装配引用并且面临:

无法对非静态字段 zipCodeLookupService 进行静态引用。

这是显而易见的。但我想知道如何处理这种情况。涉及主类时自动装配的正确方法是什么。我的代码如下 -

接口类

package com.example.services;
public interface IZipCodeLookup {
    String retriveCityForZip(String zipCode);
}
Run Code Online (Sandbox Code Playgroud)

服务等级

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";
    }
}
Run Code Online (Sandbox Code Playgroud)

这是需要服务类的主类

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);       
    }
}
Run Code Online (Sandbox Code Playgroud)

有人可以建议 - 在涉及主类时如何或使用自动装配的正确方法是什么。

(因为将 Autowired 引用设为静态无论如何都不起作用)
AutowireWithMainClassApplication课堂上,更改为 -

@Autowired
static IZipCodeLookup zipCodeLookupService;
Run Code Online (Sandbox Code Playgroud)

投掷

线程“main”中的异常 java.lang.NullPointerException

dav*_*xxx 7

@SpringBootApplication注解注解的类不是经典 bean。
它从静态方法创建 Spring 上下文。
但是自动装配的依赖项不能是static

这就是为什么这个声明:

city=zipCodeLookupService.retriveCityForZip(args[0]);
Run Code Online (Sandbox Code Playgroud)

不会抛出 Spring 异常,而是在NullPointerException您声明zipCodeLookupServicestatic字段时抛出的经典异常。


在您的情况下,作为解决方法,您可以在主类中使用方法注释的实例方法中移动使用 Spring bean 的处理,javax.annotation.PostConstruct并将传递给该main()方法的参数存储在一个字段中,以便以后能够使用它:

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); 
}
Run Code Online (Sandbox Code Playgroud)

要回答您的评论,您应该注意以下几点 @PostConstruct

1) 它不是特定于 Spring 的注释。所以,官方文档可能会讨论比 Spring 更一般的东西,或者特定但不同的东西,比如 EJB(最初是为他们引入的)。

2) javadoc 的第一句总结了一般的预期行为。

PostConstruct 注解用于需要在依赖注入完成后执行任何初始化的方法。

但这句话

“依赖注入完成后执行”

确实意味着:

“在所有依赖注入完成后执行”

我们一般谈论依赖注入,而不是每个依赖注入。
所以,是的,坚持下去。

将其应用于您的案例应该会使事情变得更加清晰。
AutowireWithMainClassApplication作为类被认为是一个Spring bean@SpringBootApplication标注有@Configuration其本身标注了@Component
和任何 Spring bean 一样,它可以声明依赖注入。
这是一个依赖注入:

@Autowired
IZipCodeLookup zipCodeLookupService;
Run Code Online (Sandbox Code Playgroud)

但是您当然可以声明任意数量的依赖注入:

@Autowired
IZipCodeLookup zipCodeLookupService;

@Autowired
OtherClass otherClass;

...
Run Code Online (Sandbox Code Playgroud)

因此,只有当所有依赖项都被有效注入时,PostConstruct才会被调用一次。

  • 感谢您耐心解决这个问题。Gr8。你无与伦比的;-)。 (2认同)

Nao*_*Bar 6

您可以执行以下操作之一:

  1. @PostConstruct方法中使用@Autowired对象,该方法在依赖注入完成后执行,如上面davidxxx 所解释的

  2. 在 main() 中使用 Spring 的getBean()明确要求 Spring 框架在注入完成后返回对象:

    public static void main(String[] args) {
        ...
        ConfigurableApplicationContext appContext = SpringApplication.run(StartApplication.class, args);
        IZipCodeLookup service = appContext.getBean(IZipCodeLookup.class);
        ...
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. 使用 Spring 的CommandLineRunner组件(在 main 之后运行),它将负责自动装配您的对象:

    @Component
    public class MyRunner implements CommandLineRunner {
    
        @Autowired
        private IZipCodeLookup service;
    
        @Override
        public void run(String... args) throws Exception {
            ...
            service.doSomething();
            ... 
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  4. 在 main 中实现 Spring 的ApplicationRunner 的run 方法:

    @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();
            ... 
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)