关于java:Proxy-JDK动态代理

32次阅读

共计 4483 个字符,预计需要花费 12 分钟才能阅读完成。

简介:

他是指一个对象 A 通过持有另一个对象 B,能够具备 B 同样的行为的模式,在 jdk 的动静代理中,B 往往实现了一些接口,A 也会去实现接口,然而实际上 B 是真正的接口的实现类,而 A 是通过 B 和接口等等一些参数生成的一个类(也就是代理类),它不仅具备与 B 同样的行为,它还能够加强 B,在调用 B 的办法前后都做些其余的事件,实际上能够了解为把本人交给他人,让他人来管制本人的行为。最平凡的利用应该就是 spring 的 aop 了,而代理又分为动静代理和动态代理,上面咱们便一一来介绍。

应用场景:

这玩意儿的利用曾经很宽泛了,比方 aop 是吧 :),当你想在调用一个办法的前后干点啥的时候,比方记录日志等,应用代理总比改每个办法的代码来的不便一些

动态代理:

当时由码农编写好代码,也就是在编译时就曾经将接口,被代理类,代理类等确定下来。在程序运行之前,代理类的.class 文件就曾经生成

模式实例:

写一个简略的动态代理的例子。我这儿举一个比拟毛糙的例子,一个不欠缺的坦克大战小游戏,如果我要在测试的时候记录一下坦克什么时候开动、什么时候进行,然而我不能再回过头去源代码外面增加这些代码。那该怎么办呢,如果咱们应用代理,该怎么操作呢,请听如下解说。

1、Movable 接口
interface Movable{void move();
}
2、Tank 坦克类

因为坦克类代码过多,咱们就简略模仿一下坦克的挪动,Tank 类实现 Movable 接口。Tank 能够具体实现挪动的动作。

public class Tank implements Movable {
    /**
 * 模仿坦克挪动了一段时间
 */
 @Override
 public void move() {System.out.println("Tank moving claclacla......");
 try{Thread.sleep(new Random().nextInt(10000));
 }catch (Exception e){e.printStackTrace();
 }
    }
    public static void main(String[] args) {new TankTimeProxy(new TankLogProxy(new Tank())).move();}
}
3、TankLogProxy 代理类

TankLogProxy,这个类也实现了 Movable 接口,然而还另外持有一个 Movable(持有 Movable 的起因是能够对实现了 Movable 的一系列类做代理操作,比方 Tank)。

/** * 坦克代理类,也实现了 Movable 接口,保留一个 Tank 实体,这样既能够代理坦克产生行为
 * */
class TankLogProxy implements Movable {
    Tank t;
 public TankLogProxy(Tank t) {this.t = t;}
    @Override
 public void move() {System.out.println("start moving......");
 t.move();
 System.out.println("stopped");
 }
}
4、测试
public static void main(String[] args) {new TankLogProxy(new Tank()).move();}

这里并没有间接调用 Tank(被代理对象)来执行挪动的行为,而是通过代理对象来代理执行。这就是代理模式。

在 jdk 中,代理模式最次要的就是有一个公共接口(Movable),一个具体的类(Tank),一个代理类(TankLogProxy), 代理类持有具体类的实例,代为执行具体类实例办法。那么咱们在代理过程中就能够加上一些其余用处。就这个例子来说,咱们能够在坦克开动之前和完结之后进行一系列的日志记录,通过代理模式很轻松就能办到

长处

1、协调调用者和被调用者,在肯定水平上升高了零碎的耦合度。
2、在代理的过程中咱们能够对被代理对象做一些加强

毛病

1、因为在客户端和实在主题之间减少了代理对象,因而有些类型的代理模式可能会造成申请的处理速度变慢。
2、动态代理对一些简单的类可能显得不是很灵便

动静代理:

后面介绍了动态代理,尽管动态代理模式很好用,然而动态代理还是存在一些局限性的,比方应用动态代理模式须要程序员手写很多代码。一旦须要代理的类中办法比拟多,或者须要同时代理多个对象的时候,这无疑会减少很大的复杂度。

代理类在程序运行时创立的代理形式被成为动静代理。咱们下面动态代理的例子中,代理类 (TankLogProxy) 是当时定义好的,在程序运行之前就曾经编译实现。然而动静代理,代理类并不是在 Java 代码中定义的,而是在运行时依据咱们在 Java 代码中的“批示”动静生成的。相比于动态代理,动静代理的劣势在于能够很不便的对代理类的函数进行对立的解决,而不必批改每个代理类中的办法

模式实例:

在 java 的 java.lang.reflect 包下提供了一个 Proxy 类和一个 InvocationHandler 接口,通过这个类和这个接口能够生成 JDK 动静代理类和动静代理对象

1、Movable 接口
interface Movable{void move();
}
2、Tank 坦克类

因为坦克类代码过多,咱们就简略模仿一下坦克的挪动,Tank 类实现 Movable 接口。Tank 能够具体实现挪动的动作。

public class Tank implements Movable {
    /**
 * 模仿坦克挪动了一段时间
 */
 @Override
 public void move() {System.out.println("Tank moving claclacla......");
 try{Thread.sleep(new Random().nextInt(10000));
 }catch (Exception e){e.printStackTrace();
 }
    }
    public static void main(String[] args) {new TankTimeProxy(new TankLogProxy(new Tank())).move();}
}
3、TimeProxy 实现 InvocationHandler 接口
class TimeProxy implements InvocationHandler{
    Movable m;
 public TimeProxy(Movable m) {this.m = m;}
    public void before(){System.out.println("method start..");
 }
    public void after(){System.out.println("method stop..");
 }
 
 /** *Object proxy 是生成的代理对象, Method method 是 Movable 接口的 move,通过反射获取, Object[] args 是实在对象中调用办法的参数 */
    @Override
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {before();
 Object o = method.invoke(m,args);
 after();
 return o;
 }
}
4、测试
public static void main(String[] args) {Tank tank = new Tank();
    /**
     生成代理对象
     Tank.class.getClassLoader(): 应用哪个类加载器来加载生成的代理对象
     new Class[]{Movable.class}: 被代理类实现了哪些接口
     new TimeProxy(tank): 代理的具体逻辑解决
    */
 Movable m = (Movable) Proxy.newProxyInstance(Tank.class.getClassLoader(),
 new Class[]{Movable.class},
 new TimeProxy(tank));
 m.move();}

动静代理的劣势在于能够很不便的对代理类的办法进行对立的解决,而不必批改每个代理类中的办法。因为所有被代理执行的办法,都是通过在 InvocationHandler 中的 invoke 办法调用的,所以咱们只有在 invoke 办法中对立解决,就能够对所有被代理的办法进行雷同的操作了。
动静代理原理图:

cglib:生成代理

package com.mashibing.proxy.cglib;
import java.lang.reflect.Method;
import java.util.Random;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class Main {public static void main(String[] args) {Enhancer enhancer = new Enhancer();
 enhancer.setSuperclass(Tank.class);
 enhancer.setCallback(new TimeMethodInterceptor());
 Tank tank = (Tank)enhancer.create();
 tank.move();}
}
class TimeMethodInterceptor implements MethodInterceptor{
    @Override
 public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println(o.getClass().getSuperclass().getName());
 System.out.println("before");
 Object result = null;
 result = methodProxy.invokeSuper(o,objects);
 System.out.println("after");
 return result;
 }
}
class Tank{public void move() {System.out.println("Tank moving claclacla...");
 try{Thread.sleep(new Random().nextInt(10000));
 } catch (Exception e){e.printStackTrace();
 }
    }
}

总结:jdk 的动静代理,总的来说
1、Proxy 通过传递给它的参数(interfaces/invocationHandler)生成代理类 $Proxy0
2、Proxy 通过传递给它的参数(ClassLoader)来加载生成的代理类 $Proxy0 的字节码文件

参考大神之作:https://www.cnblogs.com/java-chen-hao/p/10643744.html

正文完
 0