【问题标题】:@Inject and @PostConstruct not working in singleton pattern@Inject 和 @PostConstruct 不能以单例模式工作
【发布时间】:2015-06-28 09:25:00
【问题描述】:

我有一门课如下:

public class UserAuthenticator {

    private static UserAuthenticator authenticator = 

    @Inject
    private UserRepository userRepository;

    @PostConstruct
    public void init() {
        List<User> allUsers = userRepository.findAll();
        for (User user : allUsers) {
            users.put(user.getEmail(), user.getPassword());
            serviceKeys.put(user.getServiceKey(), user.getEmail());
        }
    }

    public static UserAuthenticator getInstance() {
        if (authenticator == null) {
            authenticator = new UserAuthenticator();
        }
        return authenticator;
    }
}

当我打电话时

UserAuthenticator authenticator = UserAuthenticator.getInstance();

init() 方法未被调用且 userRepository 为 null

我的 Web 应用程序在 JBOSS EAP 6.3 中运行。

这是怎么引起的,我该如何解决?

【问题讨论】:

    标签: java jakarta-ee dependency-injection singleton postconstruct


    【解决方案1】:

    在 Java EE 应用程序中,不要单例思考。这只是麻烦和混乱的秘诀。相反,请考虑“just create one”。告诉 Java EE 容器仅在应用程序范围内创建指定类的一个实例,并通过 Java EE 容器提供的工具获取该实例。您的具体问题是由于您手动使用new 运算符创建类的实例而没有手动执行注入和构建后调用,如下面的技术正确但概念上错误的示例:

    authenticator = new UserAuthenticator();
    authenticator.userRepository = new UserRepository();
    authenticator.init();
    

    换句话说,您错误地认为new 运算符会神奇地识别 bean 管理和依赖注入相关的注解。

    正确的方法取决于您要指出的负责创建和管理指定类的实例的人。如果是 CDI,那么只需告诉它使用 @Named @ApplicationScoped 在应用程序范围内只创建一个支持 bean 类的托管 bean 实例。

    import javax.inject.Named;
    import javax.enterprise.context.ApplicationScoped;
    
    @Named
    @ApplicationScoped
    public class UserAuthenticator {}
    

    它只会被创建一次,并且可以通过@Inject 在任何其他Java EE 托管工件中使用(阅读:在任何其他用@Named@Stateless@ManagedBean@WebServlet 注释的类中, @WebListener@WebFilter@Path 等):

    @Inject
    private UserAuthenticator userAuthenticator;
    

    如果您绝对肯定需要一个静态方法来获取给定支持类的当前 CDI 托管 bean 实例,那么您应该通过BeanManager 获取它,而不是手动构造实例 (@987654322 @):

    @SuppressWarnings("unchecked")
    public static <T> T getCurrentInstance(Class<T> beanClass) {
        BeanManager beanManager = CDI.current().getBeanManager();
        Bean<T> bean = (Bean<T>) beanManager.resolve(beanManager.getBeans(beanClass));
        return (T) beanManager.getReference(bean, bean.getBeanClass(), beanManager.createCreationalContext(bean));
    }
    

    用法:

    UserAuthenticator userAuthenticator = YourCDIUtil.getCurrentInstance(UserAuthenticator.class);
    // ...
    

    另见:

    【讨论】:

    • 谢谢你的解释:)
    【解决方案2】:

    我认为你不应该明确调用UserAuthenticator.getInstance(),而是将UserAuthenticator 定义为@ApplicationScoped,并通过应用服务器(@Inject) 提供的DI 获取实例。 用户验证器should be then initialized properly.

    【讨论】:

    • 谢谢,但 userRepository 仍然为空
    【解决方案3】:

    在您对该类执行某些操作之前,不会调用 @PostConstruct 方法(例如:调用某些方法

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-01-06
      • 2014-11-26
      • 2011-03-10
      • 2021-05-18
      • 2021-10-10
      • 2015-04-08
      • 1970-01-01
      相关资源
      最近更新 更多