关于设计模式:设计模式专题之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+高并发+各大学习思维脑图+面试汇合

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理