最近学习 动态代理,遇见UndeclaredThrowableException异常,记录一下
异常情况
情况是我自定义了两个异常,然后使用JDK的动态代理处理事务防止数据出问题,然而在servlet里居然捕捉不到我自定义的异常
具体核心代码如下:
servlet代码
AccountService accountService = (AccountService)Proxy.newProxyInstance(AccountService.class.getClassLoader(),
new Class<?>[]{AccountService.class}, new AccountProxy(new AccountServiceImpl()));
try {
accountService.tranfer(fromactno,toactno,money);
/*accountService.tranfer(fromactno,toactno,money);*/
//到这里说明成功
//调度View做页面显示
response.sendRedirect(request.getContextPath()+"/success.jsp");
} catch (AppException e) {
//到这里说明失败
//调度View做页面显示
request.setAttribute("error",e.getMessage());
request.getRequestDispatcher("/error.jsp").forward(request,response);
} catch (MoneyNotEnoughException e) {
//到这里说明失败
//调度View做页面显示
request.setAttribute("error",e.getMessage());
request.getRequestDispatcher("/error.jsp").forward(request,response);
}
协助代理类的类的invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Connection conn = DButil.getConnection();
conn.setAutoCommit(false);//开启事务
Object invoke = method.invoke(target, args);//调用的目标代码
conn.commit();//提交事务
return invoke;
}
异常情况:
原因
通过源码文档翻译可知
Throwable – 从代理实例的方法调用中抛出的异常。异常的类型必须可以分配给接口方法的throws
子句中声明的任何异常类型,或者可以分配给未经检查的异常类型java.lang.RuntimeException或
java.lang.Error 。如果此方法抛出了一个不能分配给接口方法的throws子句中声明的任何异常类
型的已检查异常,则包含此方法抛出的异常的UndeclaredThrowableException将由方法调用抛出代
理实例。
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
简单来说该方法抛出的检查型异常,不属于runtimeexception和error,也没有在throws中定义,这个异常会被归类到Throwable,而自定义的异常是继承 Exception 更加广泛的检查型异常,通过JDK动态代理代码调用我们发现,该方法会抛出 InvocationTargetException的检查型异常,称为调用目标异常,而我们自定义的异常将会被InvocationTargetException收录,如果没有通过throw定义抛出,则此方法抛出UndeclaredThrowableException异常
解决方法
知道原因就好办,自定义的异常会被InvocationTargetException收录,我们就可以在invoke方法中捕捉该异常,通过throw将异常信息抛出,就可以从servlet中精确捕捉自定义的异常
如下
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Connection conn = null;
Object invoke = null;
try {
conn = DButil.getConnection();
conn.setAutoCommit(false);//开启事务
invoke = method.invoke(target, args);//调用的目标代码
conn.commit();//提交事务
} catch (InvocationTargetException e) {
if (conn != null) {
conn.rollback();//遇到异常回滚事务
}
throw e.getCause();//异常抛出
}finally {
DButil.close(conn,null,null);//关闭资源
}
return invoke;
}