在使用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 courses = new ArrayList();
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 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 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 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 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 Applicationservlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainerservlet-class>
<init-param>
<param-name>jersey.config.server.provider.packagesparam-name>
<param-value>coursemanagerparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>Jersey Web Applicationservlet-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
然后就没有下文了,我尝试配置这个配置类:
<servlet>
<servlet-name>Jersey Web Applicationservlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainerservlet-class>
<init-param>
<param-name>javax.ws.rs.Applicationparam-name>
<param-value>coursemanager.config.RestConfigparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>Jersey Web Applicationservlet-name>
<url-pattern>/*url-pattern>
servlet-mapping>
<filter>
<display-name>EncodingFilterdisplay-name>
<filter-name>EncodingFilterfilter-name>
<filter-class>coursemanager.filter.EncodingFilterfilter-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 Applicationservlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainerservlet-class>
<init-param>
<param-name>javax.ws.rs.Applicationparam-name>
<param-value>coursemanager.config.RestConfigparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
测试是成功的!
ServletContainer的初始参数名还可以是jersey.config.server.provider.classnames,参数值是服务类。
配置如下:
<servlet>
<servlet-name>Jersey Web Applicationservlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainerservlet-class>
<init-param>
<param-name>jersey.config.server.provider.classnamesparam-name>
<param-value>coursemanager.impl.CourseResourceImplparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
又成功了!