@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(例如:调用一些方法
我有一个 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(例如:调用一些方法