Java REST API GET请求返回500错误,请求排查原因
问题定位与解决方案
问题描述
我有一个关联Java应用的REST API,通过JDBC连接数据库,当前使用Swagger UI进行测试:
- POST请求完全正常
- 当PERSON表为空时,GET请求能返回正常响应
- 一旦PERSON表中有数据,发送GET请求会出现500服务器错误
错误栈追踪如下:
GET /api/user 500 Server Error java.lang.reflect.UndeclaredThrowableException: null at jdk.proxy2/jdk.proxy2.$Proxy16.getPersons(Unknown Source) at module.PersonModule.lambda$new$41b6884b$1(PersonModule.java:23) at io.jooby.internal.handler.DefaultHandler.apply(DefaultHandler.java:23) at io.jooby.internal.handler.WorkerHandler.lambda$apply$0(WorkerHandler.java:23) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) at java.base/java.lang.Thread.run(Thread.java:833) Caused by: java.lang.NoSuchMethodException: no such constructor: domain.Person.<init>()void/newInvokeSpecial at java.base/java.lang.invoke.MemberName.makeAccessException(MemberName.java:974) at java.base/java.lang.invoke.MemberName$Factory.resolveOrFail(MemberName.java:1117) at java.base/java.lang.invoke.MethodHandles$Lookup.resolveOrFail(MethodHandles.java:3649) at java.base/java.lang.invoke.MethodHandles$Lookup.findConstructor(MethodHandles.java:2750) at org.jdbi.v3.core.mapper.reflect.internal.BeanPropertiesFactory$BeanPojoProperties$PropertiesHolder.<init>(BeanPropertiesFactory.java:202) at org.jdbi.v3.core.config.JdbiCaches.lambda$declare$0(JdbiCaches.java:49) at org.jdbi.v3.core.config.JdbiCaches$1.lambda$get$1(JdbiCaches.java:63) at java.base/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1708) at org.jdbi.v3.core.config.JdbiCaches$1.get(JdbiCaches.java:63) at org.jdbi.v3.core.mapper.reflect.internal.BeanPropertiesFactory$BeanPojoProperties.getProperties(BeanPropertiesFactory.java:81) at org.jdbi.v3.core.mapper.reflect.internal.PojoMapper.specialize0(PojoMapper.java:99) at org.jdbi.v3.core.mapper.reflect.internal.PojoMapper.specialize(PojoMapper.java:80) at org.jdbi.v3.core.result.ResultSetResultIterator.<init>(ResultSetResultIterator.java:38) at org.jdbi.v3.core.result.ResultIterable.lambda$of$0(ResultIterable.java:54) at org.jdbi.v3.core.result.ResultIterable.stream(ResultIterable.java:228) at org.jdbi.v3.core.result.ResultIterable.collect(ResultIterable.java:284) at org.jdbi.v3.sqlobject.statement.internal.ResultReturner$CollectedResultReturner.mappedResult(ResultReturner.java:275) at org.jdbi.v3.sqlobject.statement.internal.SqlQueryHandler.lambda$configureReturner$0(SqlQueryHandler.java:61) at org.jdbi.v3.sqlobject.statement.internal.CustomizingStatementHandler.invoke(CustomizingStatementHandler.java:178) at org.jdbi.v3.sqlobject.statement.internal.SqlQueryHandler.invoke(SqlQueryHandler.java:27) at org.jdbi.v3.sqlobject.internal.SqlObjectInitData$1.lambda$invoke$0(SqlObjectInitData.java:132) at org.jdbi.v3.core.internal.Invocations.invokeWith(Invocations.java:44) at org.jdbi.v3.core.internal.Invocations.invokeWith(Invocations.java:26) at org.jdbi.v3.core.LazyHandleSupplier.lambda$invokeInContext$1(LazyHandleSupplier.java:77) at org.jdbi.v3.core.internal.Invocations.invokeWith(Invocations.java:44) at org.jdbi.v3.core.internal.Invocations.invokeWith(Invocations.java:26) at org.jdbi.v3.core.LazyHandleSupplier.invokeInContext(LazyHandleSupplier.java:76) at org.jdbi.v3.sqlobject.internal.SqlObjectInitData$1.call(SqlObjectInitData.java:138) at org.jdbi.v3.sqlobject.internal.SqlObjectInitData$1.invoke(SqlObjectInitData.java:132) at org.jdbi.v3.sqlobject.SqlObjectFactory.lambda$attach$2(SqlObjectFactory.java:110) at org.jdbi.v3.core.internal.OnDemandExtensions.lambda$invoke$5(OnDemandExtensions.java:98) at org.jdbi.v3.core.internal.exceptions.Unchecked.lambda$function$4(Unchecked.java:76) at org.jdbi.v3.core.internal.OnDemandExtensions.invoke(OnDemandExtensions.java:98) at org.jdbi.v3.core.internal.OnDemandExtensions.lambda$createProxy$2(OnDemandExtensions.java:82) at org.jdbi.v3.core.Jdbi.callWithExtension(Jdbi.java:476) at org.jdbi.v3.core.Jdbi.withExtension(Jdbi.java:463) at org.jdbi.v3.core.internal.OnDemandExtensions.lambda$createProxy$3(OnDemandExtensions.java:82) ... 8 common frames omitted Caused by: java.lang.NoSuchMethodError: domain.Person: method 'void <init>()' not found at java.base/java.lang.invoke.MethodHandleNatives.resolve(Native Method) at java.base/java.lang.invoke.MemberName$Factory.resolve(MemberName.java:1085) at java.base/java.lang.invoke.MemberName$Factory.resolveOrFail(MemberName.java:1114) ... 43 common frames omitted
相关代码片段:
- PersonModule中报错的第23行(
return dao.getPersons();):
public PersonModule(PersonDAO dao){ path("/api/user", () -> { get("", ctx -> { return dao.getPersons(); }); post("", ctx -> { Person person = ctx.body().to(Person.class); if (dao.getPerson(person.getEmail()) == null) { dao.savePerson(person); return ctx.send(StatusCode.CREATED); } else { return ctx .setResponseCode(StatusCode.UNPROCESSABLE_ENTITY) .render(new ErrorMessage("That email already exists in the system")); } }); }); }
- JDBI的PersonDAO中获取人员列表的方法:
@Override @SqlQuery("SELECT * FROM PERSON ORDER BY EMAIL") @RegisterBeanMapper(Person.class) public Collection<Person> getPersons();
- Person类(仅展示构造函数,包含常规getter和setter):
public Person(Integer personID, String email, String password, String firstName, String lastName, String phone, String mobile, String industry, String bio, String city, Boolean active, String findOut, String role, String notes) { this.personID = personID; this.email = email; this.password = password; this.firstName = firstName; this.lastName = lastName; this.phone = phone; this.mobile = mobile; this.industry = industry; this.bio = bio; this.city = city; this.active = active; this.findOut = findOut; this.role = role; this.notes = notes; }
备注:之前测试GET请求时,数据库有数据也能正常工作,DAO的单元测试无错误。
错误分析
从错误栈的核心原因**java.lang.NoSuchMethodError: domain.Person: method 'void <init>()' not found**可以看出:
- 你使用的
@RegisterBeanMapper是JDBI的Bean映射器,它的工作逻辑是先通过无参构造函数创建Person对象实例,再调用对应的setter方法为属性赋值。 - 当前Person类只定义了带参构造函数,没有默认的无参构造函数,导致JDBI无法实例化Person对象,从而抛出异常。
- 表为空时GET请求正常,是因为没有数据需要映射,JDBI不会触发对象实例化流程;单元测试可能直接使用带参构造创建对象,所以不会触发这个问题。
解决方法
有两种可行的解决方案:
方案1:添加无参构造函数
在Person类中添加一个无参构造函数,保证JDBI的Bean映射器可以正常实例化对象:
public Person() { // 无参构造函数,无需任何逻辑 }
方案2:改用构造函数映射
如果不想添加无参构造函数,可以将@RegisterBeanMapper替换为@RegisterConstructorMapper,让JDBI直接使用带参构造函数来映射数据库结果:
@Override @SqlQuery("SELECT * FROM PERSON ORDER BY EMAIL") @RegisterConstructorMapper(Person.class) public Collection<Person> getPersons();
注意:使用该方案时,数据库查询结果的列名需要和Person构造函数的参数名(按顺序或名称匹配)对应,确保映射正确。
内容的提问来源于stack exchange,提问作者othnuk
相关产品推荐
相关产品推荐

