[笔记]动态代理

怎么做

效果很简单,功能很强大,比如有下面这样一个接口:

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调用

远程调用的情况下,本地就只有一个接口,而接口的实现不知道在远程什么位置。所以可以用代理来实现接口,等调用方法的时候,通过其他手段调用到远程服务,获取到值之后进行返回。

原文地址:https://www.cnblogs.com/at0x7c00/p/8006628.html