NullPointerException when inject enterprise bean i

2019-08-28 14:59发布

问题:

I am trying to inject and EJB 3.1 in one of my RESTful services. I've followed the post: Inject an EJB into JAX-RS (RESTful service) and tried all options except building an injection provider. The current solution that I am trying uses a combination of @RequestScoped and @Inject, but my injected bean variable is still null. I have a beans.xml in the /WEB-INF folder.

How can I inject an EJB into my REST service class?

UserService

@Local   
@Path("user/v1")
@Produces(MediaType.APPLICATION_JSON)
public class UserServiceV1 implements SystemLogger {

    @Inject
    private ApplicationBean appBean;

    @GET
    @Path("pingbean")
    @Produces(MediaType.APPLICATION_JSON)
    public Response pingAppBean() {
        if(appBean == null) {
            return Response.status(Response.Status.UNAUTHORIZED).entity("{\"faild\": \"App bean is null\"}").build();
        }
        String message = appBean.getHello();
        return Response.status(Response.Status.OK)
                .entity(message)
                .build();
    }
}

ApplicationBean The SystemHandler resides in jar module and is a standard class with business logic.

@Stateless
@Local
public class ApplicationBean implements ApplicationBeanLocal {

    @Override
    public String getHello() {
        return "Hello from ApplicationBean";
    };
}

JAX-RS configuration

@ApplicationPath("service")
public class ApplicationService extends Application {

    @Override
    public Set<Class<?>> getClasses() {
        Set<Class<?>> resources = new HashSet<>();
        resources.add(UserServiceV1.class);
        resources.add(ApplicationBean.class);
        resources.add(CorsFilterProvider.class);
        return resources;
    }
}

Exception

14:07:01,230 ERROR [io.undertow.request] UT005023: Exception handling request to /MyApp/service/user/v1/login: org.jboss.resteasy.spi.UnhandledException: java.lang.NullPointerException
        at org.jboss.resteasy.core.ExceptionHandler.handleApplicationException(ExceptionHandler.java:76) [resteasy-jaxrs-3.0.14.Final.jar:3.0.14.Final]
        at org.jboss.resteasy.core.ExceptionHandler.handleException(ExceptionHandler.java:212) [resteasy-jaxrs-3.0.14.Final.jar:3.0.14.Final]

Resteasy /JAX-RS I've added a CDI implementation for resteasy according to the documentation

<dependency>
    <groupId>org.jboss.resteasy</groupId>
    <artifactId>resteasy-jaxrs</artifactId>
    <version>3.0.14.Final</version>
    <scope>provided</scope>
</dependency>
<dependency>
        <groupId>org.jboss.resteasy</groupId>
        <artifactId>resteasy-cdi</artifactId>
        <version>3.0.14.Final</version>
        <scope>provided</scope>
 </dependency>

EDIT: changed code details in the question and title

回答1:

EJB 3.1 implies a Java EE 6 container implementation. Java EE 6 implies JAX-RS 1.1.

JAX-RS 1.1 is only required to support @EJB injection of enterprise java beans.

JAX-RS 2.0 as provided in a Java EE 7 implementation supports @Inject for EJBs.



回答2:

As Steve C answered, the proper way to inject an EJB in JAX-RS 1.1 is with the javax.ejb.EJB annotation. The REST service must also be an EJB for this to work. As such, you have to use the javax.ejb.Stateless annotation instead of javax.enterprise.context.RequestScoped.

The end result is as follows:

@Stateless
@Path("user/v1")
public class UserServiceV1 implements SystemLogger {
    @EJB
    private ApplicationBean appBean;

    //etc.
}

EDIT

Your updated code doesn't deploy. Either have ApplicationBean implement ApplicationBeanLocal and inject the interface, or don't implement it and inject the class directly. With that corrected, I managed to run your example just fine.

Also, in ApplicationService, you don't need to add ApplicationBean.class. You only register there REST root resources and feature providers. @Local is also unnecessary in the UserServiceV1 class, it's not an EJB.

Furthermore, it's beans.xml, not bean.xml (but this file is not necessary anymore from CDI 1.1 on).

See my testcode below:

pom.xml dependencies for the jar module:

<dependencies>
    <dependency>
        <groupId>javax.ejb</groupId>
        <artifactId>javax.ejb-api</artifactId>
        <version>3.2</version>
    </dependency>
</dependencies>

ApplicationBeanLocal.java:

public interface ApplicationBeanLocal {
    String getHello();
}

ApplicationBean.java:

@Stateless
@Local
public class ApplicationBean implements ApplicationBeanLocal {
    @Override
    public String getHello() {
        return "Hello from ApplicationBean";
    }
}

pom.xml dependencies for the JAX-RS application:

<dependencies>
    <dependency>
        <groupId>org.jboss.resteasy</groupId>
        <artifactId>resteasy-jaxrs</artifactId>
        <version>3.0.14.Final</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.jboss.resteasy</groupId>
        <artifactId>resteasy-cdi</artifactId>
        <version>3.0.14.Final</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>javax.enterprise</groupId>
        <artifactId>cdi-api</artifactId>
        <version>1.2</version>
        <scope>provided</scope>
    </dependency>

    <!-- The jar-module containing ApplicationBean and ApplicationBeanLocal -->
    <dependency>
        <groupId>test</groupId>
        <artifactId>testjar</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </dependency>
</dependencies>

TestApplication.java:

@ApplicationPath("service")
public class TestApplication extends Application {

    private final Set<Class<?>> resources = new HashSet<>();

    public TestApplication() {
        resources.add(UserServiceV1.class);
    }

    @Override
    public Set<Class<?>> getClasses() {
        return resources;
    }
}

UserServiceV1.java

@Path("user/v1")
@RequestScoped
@Produces(MediaType.APPLICATION_JSON)
public class UserServiceV1 {

    @Inject // Note that I'm referencing the interface, not the implementation
    private ApplicationBeanLocal appBean;

    @GET
    @Path("pingbean")
    @Produces(MediaType.APPLICATION_JSON)
    public Response pingAppBean() {
        final String message = appBean.getHello();
        return Response.status(Response.Status.OK).entity(message).build();
    }
}