java动态代理UndeclaredThrowableException InvocationTargetException

[size=small]
原文链接:说说动态代理中碰到的一个小问题 [url]https://my.oschina.net/GivingOnenessDestiny/blog/153300[/url]

JDK内置 Proxy类和 InvocationHandler接口来提供动态代理的实现。在实现连接池的时候动态代理就可以派上用场了。通过代理接管close方法, connectoin关闭的时候就不需要真正关闭,而只是放回连接池,具体实现原理可以参考红薯关于连接池的文章。

我要写的呢是关于在测试使用动态代理时碰到的一个问题,先看我的代码:

首先是接口 IFunction

public interface IFunction {
void doSomething () throws IllegalStateException;
}
接口实现 FunctionImpl

public class FunctionImpl implements IFunction {
@Override
public void doSomething() throws IllegalStateException {
// 方法什么也不做, 只抛异常
throw new IllegalStateException();
}
}
拦截 IFunctioin 的动态代理类 FunctionHandler

public class FunctionHandler implements InvocationHandler{

private IFunction fun;

public FunctionHandler(IFunction function) {
this.fun = function;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(fun, args);
}
}
最后是简单的调用

public class Client {

public static void main(String[] args) {
IFunction fun = (IFunction) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
new Class[]{IFunction.class}, new FunctionHandler(new FunctionImpl()));
try {
fun.doSomething();
} catch (IllegalStateException e) {
e.printStackTrace();
}
}
}
如此,一个简单的动态代理完成了, 一眼瞥上去没什么问题, 可惜一运行就抛异常了

Exception in thread "main" java.lang.reflect.UndeclaredThrowableException
at com.sun.proxy.$Proxy0.doSomething(Unknown Source)
at designpattern.proxy.Client.main(Client.java:18)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Caused by: java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at designpattern.proxy.FunctionHandler.invoke(FunctionHandler.java:21)
... 7 more
Caused by: java.lang.IllegalStateException
at designpattern.proxy.FunctionImpl.doSomething(FunctionImpl.java:9)
... 12 more
很奇怪啊,接口只声明了IllegalStateException, 结果却抛出了 InvocationTargetException 及 UndeclaredThrowableException.

接下来我们看看这两个不速之客是如何产生的。

首先这两个异常肯定是接口实现类抛出 IllegalStateException 引起的, 于是可以定位到 java.lang.reflect.Method 的 invoke(Object obj, Object... args), 该方法文档已经说明: 当代理的方法抛出异常时 invoke(Object obj, Object... args) 方法会抛出 InvocationTargetException 异常, 也就是说我的 IllegalStateException 此时会被包装成 InvocationTargetException。

好,现在已经知道 InvocationTargetException 是因为Method反射机制包装产生的。

接下来再看 UndeclaredThrowableException 如何产生。

在 InvocationHandler 声明的方法 invoke(Object proxy, Method method, Object[] args) 的文档中有这么一句话

Throwable the exception to throw from the method invocation on the proxy instance. The exception's type must be assignable either to any of the exception types declared in the throws clause of the interface method or to the unchecked exception types java.lang.RuntimeException or code java.lang.Error. If a checked exception is thrown by this method that is not assignable to any of the exception types declared in the throws clause of the interface method, then an UndeclaredThrowableException containing the exception that was thrown by this method will be thrown by the method invocation on the proxy instance.

这里也就是说被代理接口的方法在执行的时候抛出的受检异常必须是接口定义中声明的异常, 如果抛出的受检异常未被接口声明, 那么此时这个异常就会被包装成UndeclaredThrowableException。

那么也就清楚了,之前已经看到Method.invoke()时抛出了异常InvocationTargetException 恰好不在 接口声明的异常范围内, 因此动态代理执行的时候会抛出异常 UndeclaredThrowableException。

对于这个问题可以改良下 FunctionHandler 的代码就可解决

public class FunctionHandler implements InvocationHandler{

private IFunction fun;

public FunctionHandler(IFunction function) {
this.fun = function;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
return method.invoke(fun, args);
} catch (InvocationTargetException e) {
throw e.getCause();
}
}
}
这样再次运行测试得到的结果如下

java.lang.IllegalStateException
at designpattern.proxy.FunctionImpl.doSomething(FunctionImpl.java:9)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at designpattern.proxy.FunctionHandler.invoke(FunctionHandler.java:23)
at com.sun.proxy.$Proxy0.doSomething(Unknown Source)
at designpattern.proxy.Client.main(Client.java:18)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
两个不速之客已经消失了, "老板再也不用担心我的异常", 哈哈。

[/size]
<think>好的,用户遇到了MyBatis配置解析时的日志实现设置错误,涉及BuilderException、LogException和InvocationTargetException。我需要根据提供的引用内容来分析可能的原因和解决方案。 首先,引用[1]提到InvocationTargetExceptionUndeclaredThrowableException是反射包中的异常,用于包装其他异常。这说明在MyBatis运行过程中,可能通过反射调用某些方法时,底层抛出了异常,被这两个类包装。因此,用户的问题可能涉及到日志实现的类在反射调用时出现问题。 引用[2]和[3]讨论了MyBatis解析配置文件和处理别名的过程。在解析配置文件时,如果指定的日志实现类别名不正确或无法找到对应的类,可能会导致ClassNotFoundException,进而引发BuilderException。例如,用户可能在配置文件中错误地指定了日志实现的别名,或者在依赖中没有正确引入日志库。 接下来,我需要考虑常见的日志实现配置错误。MyBatis支持多种日志实现,如SLF4J、Log4j2等,但需要正确配置。如果用户没有在配置文件中正确指定日志实现,或者依赖冲突,可能会导致ClassNotFound或NoClassDefFoundError,这些异常可能被包装成InvocationTargetException,最终由MyBatis抛出BuilderException。 解决方案可能包括:检查日志配置是否正确,确认依赖是否存在且版本兼容,查看异常堆栈以确定根本原因。例如,用户可能需要在mybatis-config.xml中正确设置<setting name="logImpl">,并确保对应的日志库已添加到类路径中。此外,处理InvocationTargetException时,需要调用getCause()获取原始异常,以便更准确地定位问题。 最后,需要确保用户了解如何正确配置MyBatis的日志实现,并处理依赖冲突问题。同时,提醒用户在遇到BuilderException时,仔细查看根本原因,而不仅仅是表面的异常信息。</think>### 逐步解答 MyBatis配置解析时出现`BuilderException`、`LogException`或`InvocationTargetException`,通常与日志实现配置错误或反射调用异常有关。以下是具体分析和解决方案: --- ### **1. 检查日志实现配置** MyBatis通过`logImpl`配置项指定日志实现(如SLF4J、Log4j2等),若配置错误会导致`BuilderException`。 - **配置文件示例**(`mybatis-config.xml`): ```xml <configuration> <settings> <setting name="logImpl" value="SLF4J"/> <!-- 或 LOG4J2、STDOUT_LOGGING等 --> </settings> </configuration> ``` - **关键点**:`value`需与支持的日志框架名称严格匹配[^3]。 - **常见错误**:拼写错误(如`Slf4j`应为`SLF4J`)或未添加对应依赖。 --- ### **2. 验证依赖冲突** 若配置正确但仍报错,可能是依赖缺失或版本冲突: - **SLF4J示例**(Maven依赖): ```xml <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.36</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <!-- 或其他具体实现 --> <version>1.7.36</version> </dependency> ``` - **Log4j2示例**: ```xml <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值