@Inject 和@PostConstruct 不能在单例模式下工作

@Inject and @PostConstruct not working in singleton pattern

我有一个 class 如下:

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 中。

这是怎么造成的,我该如何解决?

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

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

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

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

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

import javax.inject.Named;
import javax.enterprise.context.ApplicationScoped;

@Named
@ApplicationScoped
public class UserAuthenticator {}

它将仅创建一次,并可通过 @Inject 在任何其他 Java EE 管理的工件中使用,如下所示(阅读:在任何其他 class 中注释为 @Named, @Stateless, @ManagedBean, @WebServlet, @WebListener, @WebFilter, @Path, 等等..):

@Inject
private UserAuthenticator userAuthenticator;

如果您绝对肯定需要一个静态方法来获取给定支持 class 的当前 CDI 托管 bean 实例,那么您应该通过 BeanManager 获取它,如下所示手动构建实例 (assuming Java EE 7 / CDI 1.1 available):

@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);
// ...

另请参阅:

  • Java singleton class vs JSF application scoped managed bean - differences?
  • Java EE 6 and Singletons

@PostConstruct 方法将不会被调用,直到您对该方法执行某些操作class(例如:调用一些方法