• [笔记]动态代理


    怎么做

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

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

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

  • 相关阅读:
    在nginx环境下搭建基于ssl证书的websocket服务转发,wss
    在nginx环境下搭建https服务,代理到本地web项目
    java CountDownLatch报错java.lang.IllegalMonitorStateException: null
    https本地自签名证书添加到信任证书访问
    10013: An attempt was made to access a socket in a way forbidden by its access permissions
    chrome 报错 ERR_CERT_AUTHORITY_INVALID
    SDKMAN一个基于命令行界面的SDK用户环境管理程序
    springboot放到linux启动报错:The temporary upload location [/tmp/tomcat.8524616412347407692.8111/work/Tomcat/localhost/ROOT/asset] is not valid
    netty-websocket-spring-boot-starter关闭报错 io/netty/channel/AbstractChannel$AbstractUnsafe io/netty/util/concurrent/GlobalEventExecutor
    HTML DOM addEventListener() 方法
  • 原文地址:https://www.cnblogs.com/at0x7c00/p/8006628.html
Copyright © 2020-2023  润新知