说说动态代理中碰到的一个小问题

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

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

首先是接口 IFunctionide

public interface IFunction {
    void doSomething () throws IllegalStateException;
}

接口实现 FunctionImplpost

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);
    }
}

最后是简单的调用this

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();
        }
    }
}

如此,一个简单的动态代理完成了, 一眼瞥上去没什么问题, 惋惜一运行就抛异常了.net

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, 结果却抛出了 InvocationTargetExceptionUndeclaredThrowableException.代理

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

首先这两个异常确定是接口实现类抛出 IllegalStateException 引发的, 因而能够定位到 java.lang.reflect.Methodinvoke(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)

两个不速之客已经消失了, "老板不再用担忧个人异常", 哈哈。

我的博客地址: http://hellofalcon.com/post/p-jdkdynamicproxy/

相关文章
相关标签/搜索