怎么做
效果很简单,功能很强大,比如有下面这样一个接口:
public interface IUserService { public String update(String param); }
有地方不是需要这样一个接口实现吗?现在根本没有一个具体的类实现了它,但我可以无中生有得给创建一个:
Object obj = Proxy.newProxyInstance(DynamicProxyTest.class.getClassLoader(), new Class[] {IUserService.class}, new InvocationHandler() { @Override public Object invoke(Object target, Method method, Object[] params) throws Throwable { ///... return null; } }); IUserService service = (IUserService)obj; service.update("hello");
没错,得到的代理实例可以直接强转成IUserService。
然后就可以开心地进行方法调用了。
等会儿,不是没有地方实现接口吗?那调用方法调到哪儿去了呢?在创建代理时创建的InvocationHandler里:
new InvocationHandler() { @Override public Object invoke(Object target, Method method, Object[] params) throws Throwable { System.out.println(method.getParameterTypes()[0]); System.out.println(params[0]); return null; } }
这里把所有相关的信息都提供了,调用的那个方法(method),参数是什么(params)。然后就可以想干嘛干嘛了。
这里有几个东西要注意:
invoke方法的返回值,就是那个Object,其实就是指接口中方法“public String update(String param)”的返回值“String”。InvokeHandler的方法是共用的,所以只能搞一个Object,如果在这里瞎往外面返回值,是有可能报类型转换错误的(java.lang.ClassCastException)。
参数中的那个target不要乱用,它表示的是代理实例本身。你再去调它的方法那就死循环了。干嘛要传这么个东西在这里来坑人呢?
如果把接口改成这样:
public interface IUserService { public String prop = "a property"; public String update(String param); }
现在如果要访问这个prop属性就排上用场了:
public Object invoke(Object target, Method method, Object[] params) throws Throwable { System.out.println(method.getParameterTypes()[0]); System.out.println(params[0]); IUserService _this = (IUserService)target; System.out.println(_this.prop); return null; }
更简单地说,参数里的target充当着“this”的角色。没有它就实现不了上面的操作了。
有什么用
要用动态代理那肯定是因为接口本身的实现类没法直接确定。需要在运行时根据条件再动态判断应该怎么调用。举例:
Spring拦截器
Spring环境下,咱们拿到的Bean都是Spring提供的,所以Spring可以先给一个假的代理对象,等调用方法的时候,Spring先判断一下是不是要做些别的什么事情,然后在调真实Bean的方法,调用完真实对象方法之后还可以干些别的事情。Spring想怎么玩就怎么玩。
RPC调用
远程调用的情况下,本地就只有一个接口,而接口的实现不知道在远程什么位置。所以可以用代理来实现接口,等调用方法的时候,通过其他手段调用到远程服务,获取到值之后进行返回。