繁体   English   中英

为什么Kotlin收到这样的UndeclaredThrowableException而不是ParseException?

[英]Why Kotlin receives such an UndeclaredThrowableException rather than a ParseException?

我有一个extension方法,在Kotlin中将字符串转换为Date。

fun String.convertToDate() : Date {
  var pattern: String = "dd-mm-yyyy"
  val dateFormatter = SimpleDateFormat(pattern)
  return dateFormatter.parse(this) // parse method throw ParseException
}

这是我试图捕获可能的异常的代码。

    try {
        "22---2017".convertToDate()
    } catch (ex: ParseException) {
        // ParseException supposed to be caught in this block

        logger.error("Parse exception occur")      
    } catch (ex: Exception) {

        // ParseException caught in this block

        logger.error("Exception occur")            
    }

ParseException捕获到捕获Exception的最后一个块。 但它应该在ParseException块中捕获吗? 我在这里错过了什么?

=== 更新 ===

我正在开发一个Spring MVC项目。 我已经在简单的独立kotlin程序中运行代码,其中它的行为相应。 但在我的春季项目中,它表现不同。 我给出了完整的代码, ControllerService层。

调节器

@PostMapping
@PreAuthorize("hasAnyRole('USER','ROLE_USER','ROLE_ADMIN','ADMIN')")
fun postAttendance(@RequestBody attendanceJson: AttendanceJsonWrapper,
                   request: HttpServletRequest): ResponseEntity<*> {
    val organization = getOrganizationFromSession(request)
    try {
        val attendanceBook: AttendanceBook = attendanceService.post(attendanceJson, organization.id!!)
        logger.info("Post successfully attendance book {}", attendanceBook)
    } catch (ex: SameDateAttendanceException) {
        logger.error("Duplicate attendance entry found at date [{}]", attendanceJson.date, attendanceJson.classId)
        return responseConflict(attendanceJson)
    } catch (ex: java.text.ParseException) {
        ex.printStackTrace()
        logger.error("Parse exception occur")
        return responseError(ErrorObject(
                attendanceJson,
                "date",
                Constants.INVALID_DATE_FORMAT,
                Constants.EXPECTED_DATE_FORMAT))
    } catch (ex: Exception) {
        ex.printStackTrace()
        logger.error("Exception occur")
        if (ex.cause is ParseException) {
            logger.info("What the hell is happening")
        }
    return responseOK(attendanceJson)
}

服务

@Service
open class AttendanceService constructor(val attendanceRepository: AttendanceRepository) {
@Transactional
open fun post(attendanceJsonWrapper: AttendanceJsonWrapper, orgId: Long): AttendanceBook {
    // ParseException should thrown from this line.
    val _attendanceDate = attendanceJsonWrapper.date.convertToDate()
    // Other logic goes here
    return attendanceRepository.save(attendanceBook)
}
}

日志

对于在生成日志28--2017来自前端的给定输入28--2017

2017-06-28 02:36:52.942 ERROR 4632 --- [nio-8080-exec-1] c.l.c.rest.AttendanceRestController      : Exception occur
2017-06-28 02:52:32.485  INFO 2796 --- [io-8080-exec-10] c.l.c.rest.AttendanceRestController      : What the hell is happening

例外

java.lang.reflect.UndeclaredThrowableException
at com.lynas.service.AttendanceService$$EnhancerBySpringCGLIB$$7b42c004.post(<generated>)
at com.lynas.controller.rest.AttendanceRestController.postAttendance(AttendanceRestController.kt:34)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:661)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.boot.web.filter.ApplicationContextHeaderFilter.doFilterInternal(ApplicationContextHeaderFilter.java:55)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.boot.actuate.trace.WebRequestTraceFilter.doFilterInternal(WebRequestTraceFilter.java:110)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:317)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:114)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:124)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:214)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:177)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:105)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:106)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:478)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:80)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:799)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:861)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1455)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.text.ParseException: Unparseable date: "28--2017"
at java.text.DateFormat.parse(DateFormat.java:366)
at com.lynas.util.UtilKt.convertToDate(Util.kt:56)
at com.lynas.service.AttendanceService.post(AttendanceService.kt:23)
at com.lynas.service.AttendanceService$$FastClassBySpringCGLIB$$2b941a4b.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:738)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673)
... 103 more

UndeclaredThrowableException是由kotlin引起的。 为什么? 我们知道kotlin没有检查异常。

UndeclaredThrowableException的文档说:

如果其调用处理程序的invoke方法抛出已检查的异常,则由代理实例的方法调用抛出

另一方面,Kotlin可以抛出任何异常但在java中它们将是:unchecked / checked异常和错误。 我们知道几乎所有流行的框架都是基于java.reflect包创建的,包括java.reflect.Proxy

简而言之,当kotlin函数抛出一个java 检查异常并且不声明它可能抛出的异常时。 然后调用一个java 代理,你可能会收到这样一个UndeclaredThrowableException

在java中你可以声明一个checked-exception将抛出如下:

//                v--- it is a checked exception in java
int read() throws IOException{/**/}

感谢@ glee8e指出我的错误。 你也可以在kotlin中抛出一个异常,因为kotlin没有throws关键字所以你必须使用@Throws声明一个异常将抛出:

@Throws(IOException::class)
fun read():Int{/**/}

让我们在kotlin中重现UndeclaredThrowableException

//throws a UndeclaredThrowableException takes the original IOException as its cause
// because java.lang.Runnable don't declare any checked exception at all
//                                                      |
//                                                      v
Runnable::class.proxying(::throwsAJavaCheckedException).run()


// throws the original IOException directly because java.util.concurrent.Callable
// has declared that it will be throwing a checked Exception
//                                                      |
//                                                      v
Callable::class.proxying(::throwsAJavaCheckedException).call()

fun throwsAJavaCheckedException(proxy:Any, method:Method, args:Array<Any>?): Any? {
    throw IOException();
}

typealias Invocation = (Any, Method, Array<Any>?) -> Any?;

fun <T:Any> KClass<T>.proxying(handler:Invocation) = cast(Proxy.newProxyInstance(
        ClassLoader.getSystemClassLoader(),
        arrayOf(java),
        handler
));

如何避免这个问题?

如果函数是由您自己编写的,那么解决方案就更简单了。 是的,声明该函数将抛出一个已检查的异常。 例如:

@Throws(ParseException::class)
fun convertToDate(){/**/}

或者allopen一样写一些gradle-plugin,我把它命名为allthrows

你也可以做出一些妥协。 如果你不确定像spring这样的框架会发生什么,你应该将你的调用包装成一个helper方法。 例如:

val task = Runnable::class.proxying(::throwsAJavaCheckedException)

//  v-- the result return by catch-block immediately if no exception occurs
val result : Unit = catch(task::run).by { actual: Throwable ->
    val exceptional: Unit = Unit;
    //    v--- you can choose return an exceptional value or rethrow the exception
    when (actual) {
        is RuntimeException -> exceptional
        is ParseException -> logger.info(acutal)
        else -> throw actual
    }
}


val result : Unit? = catch(task::run).only { actual:Throwable ->
// only handle the exception don't return the exceptional value
    logger.info(actual);
}

inline fun <T> catch(crossinline block: () -> T): () -> T {
    return { block(); };
}

inline fun <T> (() -> T).by(exceptionally: (Throwable) -> T): T {
    return only { exceptionally(it) }!!
}

inline fun <T : R, R> (() -> T).only(exceptionally: (Throwable) -> R): R? {
    try {
        return invoke();
    } catch(e: UndeclaredThrowableException) {
        return exceptionally(e.cause ?: e);
    } catch(e: Exception) {
        return exceptionally(e);
    }
}

您的服务似乎在与您的控制器不同的调用上下文中运行。 由于服务抛出异常,您无法在控制器中捕获它; 看起来你直接调用服务,但由于代码注入,你真的不是。 所以会发生的是调用上下文(通常是一个线程)以异常结束。 这会被转换为UndeclaredThrowableException ,并将原始异常作为原因。

有两种方法可以解决这个问题:

  1. 在生成异常的服务中本地捕获异常;
  2. 在单独的try / catch中捕获UndeclaredThrowableException ,然后重新抛出原因。

第一个选项应该是首选,但要求您处理服务中的异常。 第二个看起来太像我的黑客,但它不需要在服务而不是控制器中设置异常处理。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM