restful service interface with jersey

2019-01-20 02:07发布

问题:

Can I create a restful service with interface and implementation class? and all JAX-RS related imports go into the interface? i am using jersey2.4 and jetty8.1.

here is my interface:

MyService.java

package foo.bar; 

@Path("/abc")
public interface MyService {

     @GET
     @JSONP
     @Path("/method/{id}")
     public MyResponse getStuff(@PathParam("id") Integer id);

}

MyServiceImpl.java

package foo.bar.impl;

public class MyServiceImpl implements MyService {

     public MyServiceImpl() {}

     @Override
     public MyResponse getStuff(Integer id) {
         // do stuff
         return MyResponse;
     }
}

here's my web.xml:

<servlet>
    <servlet-name>Scivantage REST Service</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
    <init-param>
        <param-name>jersey.config.server.provider.packages</param-name>
        <param-value>foo.bar</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

I registered this service provider package (foo.bar) but it complains saying this -- javax.servlet.ServletException: A MultiException has 1 exceptions. They are:|1. java.lang.NoSuchMethodException: Could not find a suitable constructor in foo.bar.MyService class.|

When I tried with implementation class package (foo.bar.impl), it complains saying this -- I get HTTP ERROR 404; doesn't do anything else; no exceptions on console

When I tried both -- it complains the same as above:

javax.servlet.ServletException: A MultiException has 1 exceptions. They are:|1. java.lang.NoSuchMethodException: Could not find a suitable constructor in foo.bar.MyService class.|

Can you please help? What I am doing wrong?

回答1:

Although this is a question posted a year ago, here's a solution I came across after a few trials (I'm working with jetty 9 and jersey 2.13): instead of annotate the interface (with @Path("/abc")), try to annotate the implementation class instead. I think this makes good sense since interface are 'abstract' and not supposed to be bound to physical paths. This way, the interface can be re-used in different paths.



回答2:

If you want to use interfaces with JAX-RS annotation you can no longer scan a package with the web.xml

<param-name>jersey.config.server.provider.packages</param-name>
<param-value>XXX</param-value>

You need to manually bind your interface with your resource implementation

bind(YourResource.class).to(YourResourceImpl.class);

Reason for this :

We decided for performance reasons that during scanning the interfaces will be ignored. Also we fixed that Jersey will not try to instantiate interfaces.

https://java.net/jira/browse/JERSEY-1004



回答3:

I was struggling with the "Could not find a suitable constructor" issue as well. I wanted to put all of my annotations (including @Path) on my interfaces. I was able to make it work by managing the lifecycle of the resources myself rather than have Jersey instantiate them.

For example, if you had YourImplementation which implements YourRestInterface, you'd do something like this to register an instance of the implementation with Jersey:

public class RestConfig extends ResourceConfig {

    @Inject
    public RestConfig(ServiceLocator locator) {
        super();

        DynamicConfiguration c = Injections.getConfiguration(locator);
        Object implInstance = new YourImplementation();
        ServiceBindingBuilder<Object> bb = Injections.newFactoryBinder(new BeanFactory(locator, implInstance));
         // tell Jersey to use the factory below to get an instance of YourRestInterface.class
        bb.to(YourRestInterface.class);
        Injections.addBinding(bb, c);

            c.commit();
    }

    private static class BeanFactory implements Factory<Object> {

        private ServiceLocator locator;
        private Object bean;

        BeanFactory(ServiceLocator locator, Object bean)
        {
            this.locator = locator;
            this.bean = bean;
        }

        @Override
        public Object provide() {
               // have Jersey inject things annotated with @Context
            locator.inject(bean);
            return bean;
        }

        @Override
        public void dispose(Object instance) {
        }

    }
}


回答4:

In the class ResourceConfig, there is a constructor like this

ResourceConfig(Class<?>... classes)

The constructor create a new resource configuration initialized with a given set of resource/provider classes.
So you can extend ResourceConfig to register the implementation class.

public class RestConfig extends ResourceConfig {

    public RestConfig() {
        // register the implementation class
        super(MyServiceImpl.class);
    }

}

Then, configure web.xml.

<servlet>
    <servlet-name>Scivantage REST Service</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
    <init-param>
        <param-name>javax.ws.rs.Application</param-name>
        <!-- the path of RestConfig -->
        <param-value>foo.bar.RestConfig</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

But the simplest way is that register the implementation class in web.xml.

<servlet>
    <servlet-name>Scivantage REST Service</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
    <init-param>
        <param-name>jersey.config.server.provider.classnames</param-name>
        <!-- the path of implementation class -->
        <param-value>foo.bar.impl.MyServiceImpl</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>


回答5:

Yes you can use the interface to annotate. In our application we have implemented by this way. following quote is taken from Jersy specifications.

JAX-RS annotations MAY be used on the methods and method parameters of a super-class or an implemented interface. Such annotations are inherited by a corresponding sub-class or implementation class method provided that method and its parameters do not have any JAX-RS annotations of its own. Annotations on a super-class take precedence over those on an implemented interface. If a subclass or implementation method has any JAX-RS annotations then all of the annotations on the super class or interface method are ignored

I think in your case the error because of you may have missed mapping please check. <servlet-mapping> <servlet-name>api</servlet-name> <url-pattern>/api/*</url-pattern> </servlet-mapping>