Jersey服务类为接口时出现java.lang.NoSuchMethodException
在使用Jersey时,把提供服务的类设计为接口,配置在web.xml中,运行Tomcat之后访问服务出现异常:
java.lang.NoSuchMethodException: Could not find a suitable constructor in coursemanager.resource.CourseResource class.
情景
使用的bean:
package coursemanager.bean;
/**
* 课程
*/
public class Course {
/**
* 课程ID
*/
private Integer id;
/**
* 课程名称
*/
private String name;
/**
* 教授老师
*/
private String teacher;
/**
* 教室地点
*/
private String location;
/**
* 学生人数
*/
private Integer studentNum;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTeacher() {
return teacher;
}
public void setTeacher(String teacher) {
this.teacher = teacher;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public Integer getStudentNum() {
return studentNum;
}
public void setStudentNum(Integer studentNum) {
this.studentNum = studentNum;
}
public Course() {
}
public Course(String name, String teacher, String location, Integer studentNum) {
super();
this.name = name;
this.teacher = teacher;
this.location = location;
this.studentNum = studentNum;
}
public Course(Integer id, String name, String teacher, String location, Integer studentNum) {
super();
this.id = id;
this.name = name;
this.teacher = teacher;
this.location = location;
this.studentNum = studentNum;
}
@Override
public String toString() {
return "Course [id=" + id + ", name=" + name + ", teacher=" + teacher + ", location=" + location
+ ", studentNum=" + studentNum + "]";
}
}
声明服务的接口:
package coursemanager.resource;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import coursemanager.bean.Course;
/**
* 课程资源
*/
@Path("course")
public interface CourseResource {
/**
* 保存课程信息
* 接收json信息
* 返回课程的资源地址
* @param course 课程
* @return 成功新课程的id,失败返回"error"
*/
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.TEXT_PLAIN)
public Integer save(Course course);
/**
* 删除课程
* @param id 课程ID
* @return 成功返回"success",失败返回"error"
*/
@DELETE
@Path("{id:\\d+}")
public String delete(@PathParam("id") Integer id);
/**
* 更新课程信息
* @param course 课程
* @return 成功返回"success",失败返回"error"
*/
@PUT
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.TEXT_PLAIN)
public String update(Course course);
/**
* 获取特定ID的课程信息
* @param id 课程ID
* @return 课程
*/
@GET
@Path("{id:\\d+}")
@Produces(MediaType.APPLICATION_JSON)
public Course get(@PathParam("id") Integer id);
/**
* 通过课程名字查询课程信息
* @param name
* @return
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
public Course getByCourseName(@QueryParam("name") String name);
}
接口的实现类:
package coursemanager.impl;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import coursemanager.bean.Course;
import coursemanager.resource.CourseResource;
public class CourseResourceImpl implements CourseResource {
public static Integer courseId = 0;
public static List<Course> courses = new ArrayList<Course>();
public CourseResourceImpl() {
System.out.println("初始化5条记录");
// 添加5个记录
for (int i = 1; i <= 5; i++) {
Course course = new Course("课程" + i, "教师" + i, "地点" + i, 50);
addCourse(course);
}
}
@Override
public Integer save(Course course) {
addCourse(course);
return course.getId();
}
@Override
public String delete(Integer id) {
Iterator<Course> iter = courses.iterator();
while (iter.hasNext()) {
Course course = iter.next();
if (course.getId().equals(id)) {
courses.remove(course);
courseId--;
return "success";
}
}
return "error";
}
@Override
public String update(Course course) {
Iterator<Course> iter = courses.iterator();
while (iter.hasNext()) {
Course co = iter.next();
if (co.getId().equals(course.getId())) {
courses.remove(co);
courses.add(course);
return "success";
}
}
return "error";
}
@Override
public Course get(Integer id) {
Iterator<Course> iter = courses.iterator();
while (iter.hasNext()) {
Course course = iter.next();
if (course.getId().equals(id)) {
return course;
}
}
return null;
}
@Override
public Course getByCourseName(String name) {
Iterator<Course> iter = courses.iterator();
while (iter.hasNext()) {
Course course = iter.next();
if (course.getName().equals(name)) {
return course;
}
}
return null;
}
private void addCourse(Course course) {
courseId++;
course.setId(courseId);
courses.add(course);
}
}
在web.xml中的配置:
<servlet>
<servlet-name>Jersey Web Application</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>coursemanager</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey Web Application</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
访问http://localhost:8080/coursemanager/course/1,触发异常
严重: Servlet.service() for servlet [Jersey Web Application] in context with path [/coursemanager] threw exception [A MultiException has 1 exceptions. They are:
1. java.lang.NoSuchMethodException: Could not find a suitable constructor in coursemanager.resource.CourseResource class.
] with root cause
java.lang.NoSuchMethodException: Could not find a suitable constructor in coursemanager.resource.CourseResource class.
at org.glassfish.jersey.internal.inject.JerseyClassAnalyzer.getConstructor(JerseyClassAnalyzer.java:192)
at org.jvnet.hk2.internal.Utilities.getConstructor(Utilities.java:178)
at org.jvnet.hk2.internal.Utilities.justCreate(Utilities.java:988)
at org.jvnet.hk2.internal.ServiceLocatorImpl.create(ServiceLocatorImpl.java:962)
at org.jvnet.hk2.internal.ServiceLocatorImpl.createAndInitialize(ServiceLocatorImpl.java:1054)
at org.jvnet.hk2.internal.ServiceLocatorImpl.createAndInitialize(ServiceLocatorImpl.java:1046)
at org.glassfish.jersey.internal.inject.Injections.getOrCreate(Injections.java:173)
at org.glassfish.jersey.server.model.MethodHandler$ClassBasedMethodHandler.getInstance(MethodHandler.java:284)
at org.glassfish.jersey.server.internal.routing.PushMethodHandlerRouter.apply(PushMethodHandlerRouter.java:74)
at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:109)
at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:112)
at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:112)
at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:112)
at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:112)
at org.glassfish.jersey.server.internal.routing.RoutingStage.apply(RoutingStage.java:92)
at org.glassfish.jersey.server.internal.routing.RoutingStage.apply(RoutingStage.java:61)
at org.glassfish.jersey.process.internal.Stages.process(Stages.java:197)
at org.glassfish.jersey.server.ServerRuntime$2.run(ServerRuntime.java:318)
at org.glassfish.jersey.internal.Errors$1.call(Errors.java:271)
at org.glassfish.jersey.internal.Errors$1.call(Errors.java:267)
at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
at org.glassfish.jersey.internal.Errors.process(Errors.java:267)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:317)
at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:305)
at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1154)
at org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:473)
at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:427)
at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:388)
at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:341)
at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:228)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:292)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at coursemanager.filter.EncodingFilter.doFilter(EncodingFilter.java:36)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:616)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:528)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1100)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:687)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1520)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1476)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Unknown Source)
问题
通过网上资源,发现问题所在。CourseResource接口没有与它的实现关联起来。web.xml配置了服务类所在的包,Jersey会自动扫描包下的服务类。但是Jersey没有把服务接口与实现类对应起来。所以Could not find a suitable constructor in coursemanager.resource.CourseResource class.
解决
第一种 把接口和实现绑定
http://stackoverflow.com/questions/20148269/restful-service-interface-with-jersey中有种方法:
package coursemanager.config;
import javax.inject.Inject;
import org.glassfish.hk2.api.DynamicConfiguration;
import org.glassfish.hk2.api.Factory;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.hk2.utilities.binding.ServiceBindingBuilder;
import org.glassfish.jersey.internal.inject.Injections;
import org.glassfish.jersey.server.ResourceConfig;
import coursemanager.impl.CourseResourceImpl;
import coursemanager.resource.CourseResource;
public class RestConfig extends ResourceConfig {
@Inject
public RestConfig(ServiceLocator locator) {
super();
DynamicConfiguration c = Injections.getConfiguration(locator);
Object implInstance = new CourseResourceImpl();
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(CourseResource.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) {
}
}
}
然后就没有下文了,我尝试配置这个配置类:
<servlet>
<servlet-name>Jersey Web Application</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>coursemanager.config.RestConfig</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey Web Application</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<filter>
<display-name>EncodingFilter</display-name>
<filter-name>EncodingFilter</filter-name>
<filter-class>coursemanager.filter.EncodingFilter</filter-class>
</filter>
但是然并卵,没有报错,但是返回的是404。
第二种 使用配置类
查看org.glassfish.jersey.servlet.ServletContainer这个类的说明。初始参数名可以是javax.ws.rs.Application,需要提供继承了javax.ws.rs.core.Application的配置类。初始参数可以是jersey.config.server.provider.packages,就是我开始使用的参数名,Jersey会生成org.glassfish.jersey.server.ResourceConfig的实例,而ResourceConfig正是继承了Application。还有第三种,等下再说。
查看ResourceConfig的api,发现ResourceConfig的一个构造函数定义如下
ResourceConfig(Class<?>... classes)
Create a new resource configuration initialized with a given set of resource/provider classes.
我们不就可以使用这个构造函数注册我们的服务类吗!
package coursemanager.config;
import org.glassfish.jersey.server.ResourceConfig;
import coursemanager.impl.CourseResourceImpl;
public class RestConfig extends ResourceConfig {
public RestConfig() {
super(CourseResourceImpl.class);
}
}
<servlet>
<servlet-name>Jersey Web Application</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>coursemanager.config.RestConfig</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
测试是成功的!
第三种 直接配置服务类
ServletContainer的初始参数名还可以是jersey.config.server.provider.classnames,参数值是服务类。
配置如下:
<servlet>
<servlet-name>Jersey Web Application</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>coursemanager.impl.CourseResourceImpl</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
又成功了!
成功返回数据的截图: