问题
Can I create a restful service with interface and implementation class?
If so, will all JAX-RS related imports go into the interface?
I am using jersey2.4 and jetty8.1.
Here is my MyService
interface:
package foo.bar;
@Path("/abc")
public interface MyService {
@GET
@JSONP
@Path("/method/{id}")
public MyResponse getStuff(@PathParam("id") Integer id);
}
And an implementation of MyServiceImpl
that interface
package foo.bar.impl;
public class MyServiceImpl implements MyService {
public MyServiceImpl() {}
@Override
public MyResponse getStuff(Integer id) {
// do stuff
return MyResponse;
}
}
Here's the web.xml file:
<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.
What I am doing wrong?
回答1:
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 reused 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>
来源:https://stackoverflow.com/questions/20148269/restful-service-interface-with-jersey