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

dau*_*ruy 5 java singleton dependency-injection java-ee postconstruct

我有一个课程如下:

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

我打电话的时候

UserAuthenticator authenticator = UserAuthenticator.getInstance();
Run Code Online (Sandbox Code Playgroud)

init() 方法未被调用,而userRepository是 null

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

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

Bal*_*usC 6

在Java EE应用程序中,不要单身思考.这只是麻烦和困惑的秘诀.相反,想想" 只创造一个 ".告诉Java EE容器只创建指定类的一个实例,应用程序范围,并通过Java EE容器提供的工具获取实例.您的具体问题是由于您使用运算符手动创建类的实例new而没有手动执行注入和post构造调用,如下面的技术正确但概念上错误的示例:

authenticator = new UserAuthenticator();
authenticator.userRepository = new UserRepository();
authenticator.init();
Run Code Online (Sandbox Code Playgroud)

换句话说,您错误地预期new操作员会神奇地识别bean管理和依赖注入相关的注释.

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

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

@Named
@ApplicationScoped
public class UserAuthenticator {}
Run Code Online (Sandbox Code Playgroud)

将创建了一次,并通过提供@Inject任何其他Java EE管理神器如下(阅读:在注释的任何其他类@Named,@Stateless,@ManagedBean,@WebServlet,@WebListener,@WebFilter,@Path,等):

@Inject
private UserAuthenticator userAuthenticator;
Run Code Online (Sandbox Code Playgroud)

如果您非常肯定需要一个静态方法来获取给定支持类的当前CDI托管bean实例,那么您应该通过BeanManager以下方式获取它,而不是手动构造实例(假设Java EE 7/CDI 1.1可用)):

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

用法:

UserAuthenticator userAuthenticator = YourCDIUtil.getCurrentInstance(UserAuthenticator.class);
// ...
Run Code Online (Sandbox Code Playgroud)

也可以看看: