如何在 ApplicationListener 中初始化 autowire 对象?

How to make autowire object initialized in ApplicationListener?

我想读取ApplicationListener中的数据,但是我的对象没有初始化。下面是我的代码:

AppContextListener.java

@Component
public class AppContextListener implements ApplicationListener<ContextRefreshedEvent> {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {       
        AppContext.getInstance();
    }
}

AppContext.java

public class AppContext {    
    private static AppContext instance;

    @Autowired
    MyElasticsearchRepository repository;

    public AppContext(){
        InitData();
    }

    public static synchronized AppContext getInstance() {
        if (instance == null) {
            instance = new AppContext();
        }
        return instance;
    }   

    private void InitData(){       
        List<MyEntity> dataList = repository.findAllEntities();//repository is null here
        //.......
    }   
}

MyElasticsearchRepository.java

 public interface MyElasticsearchRepository extends ElasticsearchRepository<MyEntity,String>
 { }

问题

As you can see in my code, at InitData(), repository is null. I don't know why @Autowired MyElasticsearchRepository repository; does not worked here.

请告诉我如何解决这个问题。非常感谢。

@Autowired 仅在 bean 标记有 Stereotype 注释(What's the difference between @Component, @Repository & @Service annotations in Spring?)或您在 spring 配置中明确定义它时才有效。

AppContextListener.java

@Component // AFAIR not needed. Spring will create this bean when it will see that class implements `ApplicationListener` interface.
public class AppContextListener implements ApplicationListener<ContextRefreshedEvent> {
    @Autowired
    private AppContext appContext;

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {       
        appContext.initData();
    }
}

AppContext.java

@Component
public class AppContext {    

    @Autowired
    MyElasticsearchRepository repository;

    public void initData(){       
        List<MyEntity> dataList = repository.findAllEntities();//repository is null here
        //.......
    }   
}

@Autowired 只有在构建 AppContext 对象后才会起作用。由于您尝试在构造函数中访问 @Autowired 元素,因此它不存在。

您的代码有几处错误。

首先,您使用的是单例模式,我认为这是一种反模式,尤其是与自动布线结合使用时。

其次,在您的 getInstance() 方法中,您自己创建了 AppContext 的新实例。这个实例不是由 Spring 管理的,所以 @Autowired 在这里几乎没用,Spring 只能将依赖项注入它知道的 bean。

而是让您的 AppContext 成为一个组件(或任何您喜欢的服务)。删除 getInstance 方法并改为使用构造函数注入。

@Component
public class AppContext {    

    private final MyElasticsearchRepository repository;

    @Autowired
    public AppContext(MyElasticsearchRepository repository){
        this.repository=repository;
    }
    ...
}

第三,您正在尝试使用构造函数中的 @Autowired 实例(您正在执行方法调用,希望它在那里),但是自动连接只能在 bean 实例上完成。所以在那一刻自动接线还没有发生,你的变量将永远是 null。不要从构造函数调用方法,而是使用构造函数注入或使用 @PostConstruct.

注释 InitData 方法
@PostConstruct
private void InitData(){       
    List<MyEntity> dataList = repository.findAllEntities();
    ...        
}

既然你的 AppContext 是一个组件,它将被 spring 检测到,你可以简单地将它注入你的 ApplicationListener.

@Component
public class AppContextListener implements ApplicationListener<ContextRefreshedEvent> {

    private final AppContext appContext;

    @Autowired
    public AppContextListener(AppContext appContext) {
        this.appContext=appContext;
    }
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {       
        // Do your thing with appContext
    }
}

注意:我更喜欢对必填字段进行构造函数注入,对可选字段进行setter注入。您应该避免字段注入(即实例字段上的 @Autowired),因为这被认为是一种不好的做法。请参阅 here 为什么字段注入是邪恶的并且应该避免。

你就不能这样做吗?

    @Component
    public class AppContextListener implements ApplicationListener<ContextRefreshedEvent> {
        @Override
        public void onApplicationEvent(ContextRefreshedEvent event) {       
            ApplicantContext context = event.getApplicationContext();
            MyElasticsearchRepository repository = context.getBean(MyElasticSearchRepository.class);
            //do stuff
        }
    }

http://docs.spring.io/autorepo/docs/spring/4.1.4.RELEASE/javadoc-api/org/springframework/context/event/ContextRefreshedEvent.html