Jersey服务类为接口时出现java.lang.NoSuchMethodException

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>

又成功了!

成功返回数据的截图:
这里写图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值