【问题标题】:CDI - @Injected field nullCDI - @Injected 字段为空
【发布时间】:2019-08-04 06:53:02
【问题描述】:

我想在我的应用程序启动时初始化一个集合并用数据填充它。然后我想在我的应用程序的任何地方访问它。例如,我希望我的 REST API 可以访问已填充数据的共享集合。

我首先尝试使用带有@Startup 和@Singleton 注释的启动类来实现。当我在那里注入我的 userService 时,我遇到了一些问题,并且由于这篇文章中的建议:@Inject and @PostConstruct not working in singleton pattern 我尝试使用 @ApplicationScoped 注释来做到这一点:

@Named
@ApplicationScoped
public class KwetterApp {

    @Inject
    private UserService service;

    @PostConstruct
    public void init() {
        try {
            User harry = new User("Harry", "harry@outlook.com", "New York", "http://harry.com", "Hi, I'm Harry!", UserType.REGULAR);
            User nick = new User("Nick", "nick@outlook.com", "California", "http://nick.com", "Hi, I'm Nick!", UserType.REGULAR);
            User jane = new User("Jane", "jane@outlook.com", "Texas", "http://jane.com", "Hi, I'm Jane!", UserType.REGULAR);

            Tweet tweet = new Tweet("eating...", harry);
            Tweet tweet1 = new Tweet("swimming...", harry);
            Tweet tweet2 = new Tweet("jogging...", jane);

            harry.addTweet(tweet);
            harry.addTweet(tweet1);
            jane.addTweet(tweet2);
            service.create(harry);
            service.create(nick);
            service.create(jane);
        }
        catch (Exception e){
            e.printStackTrace();
        }
    }

    public UserService getService() {
        return service;
    }
}

我在我的休息服务中注入这个类:

@RequestScoped
@Path("/user")
public class UserRest {

//    @Inject
//    private UserService userService;

@Inject
private KwetterApp kwetterApp;

//    private KwetterApp kwetterApp = KwetterApp.getInstance();
private UserService userService = kwetterApp.getService();

@GET
@Produces({"application/json"})
public List<User> get() throws UserException {

    return userService.getAll();
    }
}

注入此 KwetterApp 时会导致以下异常:

StandardWrapperValve[rest.RestApplication]: Servlet.service() for servlet rest.RestApplication threw exception
java.lang.NullPointerException
    at rest.UserRest.<init>(UserRest.java:27)
    at rest.UserRest$Proxy$_$$_WeldClientProxy.<init>(Unknown Source)

我有一个空 beans.xml 文件,其中 bean-discovery-mode 设置为“all”。 CDI 框架应该能够识别我的 KwetterApp 类进行注入,对吗?为什么是空的?

提前致谢,

迈克

【问题讨论】:

    标签: java cdi


    【解决方案1】:

    这里

    @Inject
    private KwetterApp kwetterApp;
    private UserService userService = kwetterApp.getService();
    

    我认为kwetterApp 字段不会在userService 之前设置。
    CDI 将在对象构建后设置该字段。

    无论如何都应该使用的替代方法是构造函数注入

    @RequestScoped
    @Path("/user")
    public class UserRest {
      private KwetterApp kwetterApp;
      private UserService userService;
    
      protected UserRest() {}
    
      @Inject
      public UserRest(final KwetterApp kwetterApp) {
        this.kwetterApp = kwetterApp;
        this.userService = kwetterApp.getService();
      }
    
      @GET
      @Produces({"application/json"})
      @Override
      public List<User> get() throws UserException {
        return userService.getAll();
      }
    }
    

    需要protected 构造函数,因为@RequestScoped 是一个普通范围的bean,并且它必须是可代理的,如规范中所述。

    唯一不需要空构造函数的注解是@Singleton(来自javax.inject)。

    【讨论】:

    • 感谢您的反应。当我这样做时,我得到一个“正常范围的 bean 类 rest.UserRest 不可代理,因为它没有无参数构造函数”错误。然而,创建一个空的无参数构造函数会导致 stackoverflow 错误。
    • @Maikkeyy 见stackoverflow.com/questions/46543657/… 我会选择接口路由。这是因为代理是如何创建的。 (在 Spring 中,由于 CGLIB 的版本,不需要无参数构造函数)
    • 如果您需要示例,请告诉我。
    • 一个例子永远不会伤害:)。但我不明白为什么它可以使用接口而不是实际的类本身?
    • @Maikkeyy 我认为这是因为 CDI 实现实现接口而不是扩展类。 扩展一个类意味着必须调用超级构造函数(在这种情况下,使用参数,所以 CDI 不知道该做什么)
    【解决方案2】:

    如果您想使用注入的 bean 来初始化对象,那么您必须使用 @PostConstruct 注释方法,因为注入的 CDI bean 仅在 CDI 中的 @PostContruct 注释方法中可用,并且在之后而不是在字段初始化期间或构造函数调用。

    因此,UserService 已经是一个 CDI bean,您可以将其注入到您的 rest 服务 bean 中,因为它将与当前活动范围内使用的 bean 相同。 KwetterApp 在当前活动范围内可用,因此 UserService 也将可用。只有 @Dependend 作用域 bean 的行为不同,每个 bean 都提供自己的实例。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-01-05
      • 2019-04-13
      • 2020-03-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-12-23
      相关资源
      最近更新 更多