乐趣区

关于设计模式:设计模式专题之Proxy静态代理与动态代理

简介

用意:

为其余对象提供一种代理以管制整个对象的拜访。

次要解决:

在间接拜访对象时带来的问题,比如说:要拜访的对象在近程的机器上。在面向对象的零碎中,有些对象因为某些起因(比方对象创立开销很大,或者某些操作须要安全控制,或者须要过程外的拜访),间接拜访会给使用者或者系统结构带来很多麻烦,咱们能够在拜访此对象时加上一个对此对象的拜访层。

长处:

  1. 职责清晰
  2. 高扩展性
  3. 智能化

毛病:

  1. 因为在客户端和实在主题之间减少了代理对象,因而有些类型的代理模式可能会造成申请的处理速度变慢。
  2. 实现代理须要额定的工作,有些代理模式实现起来非常复杂。

应用场景:

  1. 近程代理
  2. 虚构代理
  3. Copy-on-Write 代理
  4. 爱护代理
  5. Cache 代理
  6. 防火墙代理
  7. 同步化代理
  8. 智能援用代理

实现

动态代理

动态代理在应用时,须要定义接口或者父类,被代理对象与代理对象一起实现雷同的接口或者是继承雷同父类。

接口

interface Movable {void move();
}

被代理类,实现该接口 move 办法

public class Tank implements Movable {
    /**
     * 模仿坦克挪动了一段儿工夫
     */
    @Override
    public void move() {System.out.println("Tank moving claclacla...");
        try {Thread.sleep(new Random().nextInt(10000));
        } catch (InterruptedException e) {e.printStackTrace();
        }
    }
}

代理类同样实现该接口,增加 Movable 属性,实现 move 办法外部调用传入的被代理类的 move 办法,在调用前后能够增加一些业务代码

class TankTimeProxy implements Movable {
    Movable m;
    public TankTimeProxy(Movable m) {this.m = m;}
    @Override
    public void move() {long start = System.currentTimeMillis();
        m.move();
        long end = System.currentTimeMillis();
        System.out.println(end - start);
    }
}

最初应用 main 办法调用代理类的 move 办法,如果须要多个代理类,能够多层嵌套应用

 class Demo{public static void main(String[] args) {Tank t = new Tank();
        TankTimeProxy ttp = new TankTimeProxy(t);
        //        TankLogProxy tlp = new TankLogProxy(ttp);
        ttp.move();}
}

动态代理总结:

  1. 能够做到在不批改指标对象的性能前提下,对指标性能扩大。
  2. 毛病:因为代理对象须要与指标对象实现一样的接口,所以会有很多代理类,类型太多。同时,一旦接口减少办法,指标对象与代理对象都要保护。

动静代理

特点:

  1. 代理对象,不须要实现接口
  2. 代理对象的生成,是利用 JDK 的 API,动静的内存中构建代理对象(没有接口的 cglib 代理)

被代理类,实现该接口 move 办法

public class Tank implements Movable {
    /**
     * 模仿坦克挪动了一段儿工夫
     */
    @Override
    public void move() {System.out.println("Tank moving claclacla...");
        try {Thread.sleep(new Random().nextInt(10000));
        } catch (InterruptedException e) {e.printStackTrace();
        }
    }
}

JDK 代理:

代理类所在的包:java.lang.reflect.Proxy

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
  • ClassLoader loader:指定以后指标对象应用类加载器
  • Class<?>[] interfaces:指标对象实现的接口的类型
  • InvocationHandler h:事件处理,执行指标对象的办法时会触发事件处理器,会把以后执行指标对象的办法作为参数传入

接口

interface Movable {void move();
}

被代理类,实现该接口 move 办法

public class Tank implements Movable {
    /**
     * 模仿坦克挪动了一段儿工夫
     */
    @Override
    public void move() {System.out.println("Tank moving claclacla...");
        try {Thread.sleep(new Random().nextInt(10000));
        } catch (InterruptedException e) {e.printStackTrace();
        }
    }
}

创立一个代理类的调用处理程序

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..");
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {before();
        Object o = method.invoke(m, args);
        after();
        return o;
    }
}

Demo 调用,生成指定接口的代理类实例

public static void main(String[] args) {Tank tank = new Tank();
    Movable m = (Movable)Proxy.newProxyInstance(Tank.class.getClassLoader(),
            new Class[]{Movable.class}, //tank.class.getInterfaces()
            new TimeProxy(tank)    );
    m.move();}

上述演示的时生成指定接口的代理类示例,然而 JDK 中时,通过 JDK 代理或者 cglib 代理在运行时主动生成到内存中的。

JDK 动静代理原理

生成动静代理文件

办法一:

  1. 必须在 main 办法中执行,间接用 junit 的 test 办法调用无奈生成
  2. JDK1.8 之后在 main 办法最后面减少 System.getProperties().put(“jdk.proxy.ProxyGenerator.saveGeneratedFiles”,”true”);
  3. JDK1.8 及其之前在 main 办法最后面减少 System.getProperties().put(“sun.misc.ProxyGenerator.saveGeneratedFiles”,”true”);

办法二:

  1. 运行时退出 jvm 参数 -Djdk.proxy.ProxyGenerator.saveGeneratedFiles=true
  2. 运行时退出 jvm 参数 -Dsun.misc.ProxyGenerator.saveGeneratedFiles=true

如何确定是哪个参数, 查看 ProxyGenerator 类内容,如下:

而后执行后就会在当前目录下 com 包中生成代理类的 class 文件。

而后。。。

这就是源码过程了,先上图

截图发现图片太多就算了。。。

Movable m = (Movable)Proxy.newProxyInstance(Tank.class.getClassLoader(),
        new Class[]{Movable.class},
        new TimeProxy(tank));

/*
 * Look up or generate the designated proxy class and its constructor.
 */
Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);

return proxyCache.sub(intf).computeIfAbsent(
    loader,
    (ld, clv) -> new ProxyBuilder(ld, clv.key()).build());

Constructor<?> build() {Class<?> proxyClass = defineProxyClass(module, interfaces);
    final Constructor<?> cons;
    try {cons = proxyClass.getConstructor(constructorParams);
    } catch (NoSuchMethodException e) {throw new InternalError(e.toString(), e);
    }
    AccessController.doPrivileged(new PrivilegedAction<Void>() {public Void run() {cons.setAccessible(true);
            return null;
        }
    });    
return cons;
}

/* 
* Generate the specified proxy class. 
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces.toArray(EMPTY_CLASS_ARRAY), accessFlags);

final byte[] classFile = gen.generateClassFile();

addProxyMethod(hashCodeMethod, Object.class);
addProxyMethod(equalsMethod, Object.class);
addProxyMethod(toStringMethod, Object.class);

methods.add(pm.generateMethod());

最最最初是通过 asm 来实现的

Cglib 动静代理

  1. 创立 Enhancer 对象
  2. 用 Enhancer 对象设置代理类的父类(被代理类)
  3. 创立回调对象(回调类实现 MethodInterceptor 接口)
  4. 用 Enhancer 对象设置回调对象
  5. 用 Enhancer 对象创立代理对象

创立一个拦截器,相当于 JDK 的 InvocationHander

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

创立动静代理

/**
 * CGLIB 实现动静代理不须要接口 
*/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();}
}

生成 cglib 代理的 class,上面两个任选其一

System.setProperty("cglib.debugLocation", "./cg");

System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "./cg");

或者在 idea -> run -> edit configurations 中设置 vm options

-Dcglib.debugLocation=./cg

其余对于代理

正向代理:

  • 晓得指标地址,但无奈间接拜访,须要通过代理才能够拜访。例如 IP 代理机场等。
  • 正向代理的过程,暗藏了实在的客户端。客户端的申请服务都被代理服务器代替来申请。

反向代理:

  • 不晓得指标地址,代理服务器就相当于指标服务器,由代理服务器承受所有申请后,转发给外部其余服务器。例如拜访百度 www.baidu.com 然而不晓得理论拜访是哪台服务器。

最全学习笔记大厂真题 + 微服务 +MySQL+ 分布式 +SSM 框架 +Java+Redis+ 数据结构与算法 + 网络 +Linux+Spring 全家桶 +JVM+ 高并发 + 各大学习思维脑图 + 面试汇合

退出移动版