实现 ApplicationContextAware 接口, ApplicationContext 为 null 问题

2022-06-15 18:08:59 +08:00
 zhongpingjing

主要封装一些获取 bean 的方法,代码如下:

@Component
public class SpringUtils implements ApplicationContextAware {

    /**
     * 上下文对象实例
     */
    private static ApplicationContext context = null;

    @Override
    @Autowired
    public void setApplicationContext(@NotNull ApplicationContext applicationContext) throws BeansException {
        context = applicationContext;
        log.info("Inject applicationContext succeeded");
    }
    
    public static ApplicationContext getApplicationContext() {
        if (context == null) {
            throw new IllegalArgumentException("ApplicationContext wasn't injected");
        }
        return context;
    }
}

在其他 bean 的构造方法里面调用SpringUtils.getApplicationContext。 奇怪的是,在 idea 启动正常,打包部署的时候就会抛错IllegalArgumentException

网上一推解法都试过了,都没用。 请求大佬支援。

2063 次点击
所在节点    Java
17 条回复
agzou
2022-06-15 18:19:57 +08:00
目测,@Autowired 问题,有试过把 @Autworied 去掉吗,实现了 ApplicationContextAware 接口,不需要再用 @Autowired
fzdwx
2022-06-15 18:20:58 +08:00
没问题
justRua
2022-06-15 19:53:28 +08:00
本地 debug 可以但是打包后不行,可能是打的包有问题,可以远程 debug 看看启动时会不会进 setApplicationContext
Red998
2022-06-15 20:04:56 +08:00
SpringUtils. context= applicationContext; 需要指向
不需要 @Autowired 这个 spring 生命周期会回调接口
fmumu
2022-06-15 20:15:50 +08:00
1 是 ApplicationContextAware 的的调用和 bean 的初始化先后问题
2 是在其他 bean 的初始化为什么要用这个 utils,依赖其他 bean 的话用构造器注入不就好了
BBCCBB
2022-06-15 20:24:00 +08:00
你是不是在这个 setApplicationContext 方法完成之前就调用了 getApplicationContext? 还有就是实现接口的方法上不需要加 @Autowired
zhongpingjing
2022-06-16 09:27:57 +08:00
@agzou
@redorblacck886
@fmumu
@BBCCBB 没加 @Autowired 也不行啊
zhongpingjing
2022-06-16 09:28:54 +08:00
@BBCCBB 我在其他 bean 的构造方法调了,可能这个 bean 比 SpringUtils 先加载?
agzou
2022-06-16 09:34:59 +08:00
@zhongpingjing #8 调用的 bean 应该在初始完之后调用,实现 InitializingBean 接口 afterPropertiesSet 方法,或者使用 @PostConstruct ,在构造方法里面不能保证其他 bean 给初始化了
zhongpingjing
2022-06-16 09:49:52 +08:00
@fmumu
1.我也感觉是顺序问题,但是我用了 @Order ,指定了高优先级也一样的错误,想知道怎么能让 utils 最先加载
2.构造器注入不准确,因为被注入的 bean ,有两个泛型<T ,确定类型>,会注入不需要的 bean ,所以要使用了 getBeanProvider 来手动注入
zhongpingjing
2022-06-16 09:53:28 +08:00
@agzou 目前是打算按你这样说改造了,如果这个问题找不到原因的话
zhongpingjing
2022-06-16 09:56:16 +08:00
@justRua 会进的,不在其他 bean 里面调用 getApplicationContext ,最后能打印 Inject applicationContext succeeded 。感觉是调用顺序问题,就是 idea 启动 utils 能在其他 bean 先加载,打包后就反了
BBCCBB
2022-06-16 09:58:38 +08:00
> 我在其他 bean 的构造方法调了

你在构造方法里调用这个? bean 加载的顺序不固定. 你可以试试 @DependsOn

或者在其他 bean 里监听 @ApplicationReadyEvent 来做初始化.

@Order 对 bean 的加载没用, 只对 interceptor 这些 aop 有用.
cppc
2022-06-16 10:30:30 +08:00
@zhongpingjing order 不是这样用的哦。你要想"要求" SpringUtils 在初始化(构造方法)时必须可用,最简单的方法就是在构造方法中添加一个 SpringUtils 类型参数,这样 Spring 就知道依赖关系了。

```java

@Service
public class MyService {

// 如果要确保在 bean 初始化期间用到另一个 bean
public MyService (SpringUtils utils){
assert utils != null;
}

// 你自己能确认 SpringUtils 已经初始化的情况下,才能直接使用,比如应用已经启动了
@EventListener
public void doIt(ApplicationStartedEvent evt){
assert SpringUtils.getApplicationContext() != null;
}

}

```

总之,要么你自己确保依赖关系正确,要么给 Spring 足够的提示,让他帮你管理。
zhongpingjing
2022-06-16 10:32:50 +08:00
@BBCCBB 看来确实是顺序问题,加上 @DependsOn 就好了
zhongpingjing
2022-06-16 10:34:54 +08:00
@cppc 嗯嗯,确实是顺序问题,把依赖顺序明确了后就行了,神奇的地方就是 idea 启动正常,打包启动就不正常
BBCCBB
2022-06-16 10:48:05 +08:00
不建议这样用哈, 启动阶段依赖 applicationContext 的地方, 采用注入 ApplicationContext 字段, 在 afterPropertiesSet 里执行初始化, 启动完成后可以用你这个工具类.

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/859836

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX