fondo
En los escenarios de llamadas de interfaz RPC o en el uso de escenarios de proxy dinámicos, ocasionalmente no se produce una Excepción de Ejecución de Trawable, o en un escenario de reflexión, se produce una InvocetException, que es inconsistente con la excepción que esperamos, y la información de excepción real está oculta en un stack más profundo. Este artículo se centrará en el análisis de UndeclaredThowableException
Dar una conclusión primero
Cuando se usa la interfaz de proxy dinámico JDK, si se lanza una excepción detectada durante la ejecución del método, pero la firma del método no declara la excepción, se envolverá en la clase proxy como un no declarado.
Restaurar el problema
// Definición de interfaz Interface public IService {void foo () lanza SQLException;} public class ServiceImpl implementa iservice {@Override public void foo () lanza SQLException {Throw New Sqlexception ("Probo lanzar una excepción verificada"); }} // proxy dinámico clase pública iserviceProxy implementa invocationHandler {objetivo de objeto privado; IserviceProxy (objetivo de objeto) {this.target = target; } @Override public Object Invoke (Proxy de objeto, método Método, objeto [] args) arroja lanzar {return Method.invoke (target, args); }} public class Mantest {public static void main (string [] args) {iservice service = new ServiceImpl (); Iservice ServiceProxy = (iservice) proxy.newproxyInstance (servicio.getClass (). GetClassLoader (), servicio.getClass (). GetInterfaces (), nuevo iserviceProxy (servicio)); intente {ServiceProxy.foo (); } catch (Exception e) {E.PrintStackTrace (); }}}Ejecute el mantenimiento anterior y la pila de excepciones es
java.lang.reflect.undeclaredThrowableException en com.sun.proxy. $ proxy0.foo (fuente desconocida) en com.learn.reflect.maintest.main (mantenente.java:16) causado por: java.lang.reRe.invocationTargetException en Sun.reflect.nativeMethodaceMpl) en Sun.reflect.nativemethodaccessorImpl.invoke (nativemethodaccessorImpl.java:62) en sun.reflect.delegatingMethodaccessorImpl.invoke (delegatingmethodaccessorsiM.Java:43) en java.lang.reflect.method.invoke (Method.Java:49) com.learn.reflect.iserviceProxy.invoke (iserviceProxy.java:19) ... 2 Morecaused por: java.sql.sqlexception: pruebo una excepción verificada en com.learn.reflect.serviceIm.foo (ServiceImpl.Java:11) ... 7 más
Lo que esperamos es
java.sql.sqlexception: pruebo una excepción verificada en com.learn.reflect.serviceImpl.foo (ServiceImpl.java:11) ...
Análisis de causa
En la restauración del problema anterior, la verdadera SQLException está envuelta en dos capas, primero envuelta por InvocationTargetException, y luego envuelta por Und -DeClaredThowableException. Entre ellos, InvocationTargetException es la excepción detectada, y UndeclaredThowableException es la excepción de tiempo de ejecución. ¿Por qué está envuelto? También comienza con la clase proxy generada por proxy dinámico.
El proxy dinámico JDK generará la clase de implementación específica de la interfaz delegada en tiempo de ejecución. Generamos manualmente el archivo de clase a través de proxyGenerator, y luego usamos la idea para analizar el archivo de clase para obtener la clase proxy específica: interceptar la parte:
Public Final Clase IserviceProxy $ 1 extiende el proxy implementa iservice {Método estático privado M1; Método estático privado M2; Método estático privado M3; Método estático privado M0; Public iserviceProxy $ 1 (InvocationHandler Var1) lanza {super (var1); } public final void foo () lanza sqlexception {try {super.h.invoke (this, m3, (object []) null); } catch (runtimeException | sqlexception | error var2) {throw var2; } catch (Throwable Var3) {Throw New Und -DeClaredThrowableException (VAR3); }} static {try {m1 = class.forname ("java.lang.object"). getMethod ("igual", nueva clase [] {class.forname ("java.lang.object")}); m2 = class.forname ("java.lang.object"). getMethod ("toString", nueva clase [0]); m3 = class.forname ("com.learn.reflect.iservice"). getMethod ("foo", nueva clase [0]); m0 = class.forname ("java.lang.object"). getMethod ("hashcode", nueva clase [0]); } Catch (nosuchmethodexception var2) {tire nosuchmethoderror (var2.getMessage ()); } Catch (ClassNotFoundException var3) {tire newLassDefFoundError (var3.getMessage ()); }}}Al llamar al método FOO de la "clase delegada", el método FOO de la clase proxy iserviceproxy $ 1 realmente se llama, y la lógica principal de la clase proxy es llamar al método Invoke del InvocationHandler. La lógica del manejo de excepciones es lanzar directamente RuntimeException, la excepción declarada por la interfaz, y el error se lanza, y otras excepciones se envuelven como no declarado. En este punto, tal vez ya lo hayas entendido, tal vez tengas una pregunta, en la implementación de la interfaz, de hecho, es tirar una nueva Sqlexception, ¿por qué todavía está envuelto? Veamos el método de Invoke de IserviceProxy. Ejecuta directamente el método de destino a través de la reflexión. Este es el problema. Method.Invoke (Object obj, objeto ... args) La declaración del método se ha explicado que si el método de destino arroja una excepción, se envolverá como una invocación TargetException. (Para más detalles, consulte Javadoc)
Por lo tanto, el resumen es: en la implementación del método específico, se lanza SQLException y se refleja y se envuelve como una invocación TargetException. Esta es una excepción verificada. Cuando la clase proxy maneja la excepción, encuentra que la excepción no se declara en la interfaz, por lo que se empaqueta como una concepción no declarable.
Solución
En el cuerpo del método Invoke que implementa InvocationHandler, intente atrapar el método. Invoke (Target, Args); Llame y tire la causa de la invocación TargetException. Ahora mismo:
@Override public Object Invoke (Proxy de objeto, método Método, objeto [] args) lanza lando {try {return Method.invoke (target, args); } catch (InvocationTargetException e) {Throw E.GetCause (); }}Fuera de tema
¿Por qué una excepción verificada no declarada en la clase proxy se convierte en una concepción no declarable? Debido al principio de herencia de Java: cuando una subclase anula la clase principal o un método que implementa la interfaz principal, la excepción lanzada debe estar dentro de la lista de excepciones admitida por el método original. La clase proxy implementa la interfaz principal o sobrescribe el método de clase principal
referirse a
https://www.ibm.com/developerworks/cn/java/j-lo-proxy1/index.html#icomments
Resumir
Lo anterior es todo el contenido de este artículo. Espero que el contenido de este artículo tenga cierto valor de referencia para el estudio o el trabajo de todos. Si tiene alguna pregunta, puede dejar un mensaje para comunicarse. Gracias por su apoyo a Wulin.com.