学习笔记-proxy代理模式

proxy代理模式

proxy代理模式,让我们可以通过中间代理类去操作被代理类的一些处理,让操作者不直接操作对象类

就好比明星和经纪人的关系(参考别人博客的说法),除了明星自己的独特的演出,或者唱歌,需要调用对象本身,其余的拉赞助,接通告等其它的事情可以交给经纪人去做.
这里的经济人就相当于代理

为什么要用代理模式

当我们要对某一个方法进行增强时,比如对某个方法计算他的效率,打印开始结束日志等,我们可能会在每次调用一个方法的时候,去执行一次执行时间的统计,或者日志的打印
(比如多个明星,就需要跟着多个经纪人分开跑—>这里如果有个经济公司,统一管理这些明星)

package it.luke.proxy.v00;

public class Url_req {

    public void filter(){
        System.out.println("filter ...");
    }

   public static void main(String[] args) {
        Url_req url_req = new Url_req();


        long start = System.currentTimeMillis();
        System.out.println(start);
        url_req.filter();
        System.out.println(System.currentTimeMillis() - start);
   }

}

如果需要统计多个方法的话,就需要重复的对统计方法进行调用

package it.luke.proxy.v00;

public class Url_req {

    public void filter(){
        System.out.println("filter ...");
    }

    public void filter02(){
        System.out.println("filter02 ...");
    }
    public static void main(String[] args) {
        Url_req url_req = new Url_req();


        long start = System.currentTimeMillis();
        System.out.println(start);
        url_req.filter();
        System.out.println(System.currentTimeMillis() - start);

        long start1 = System.currentTimeMillis();
        System.out.println(start);
        url_req.filter02();
        System.out.println(System.currentTimeMillis() - start1);
    }
}

如果是多个类的话,就需要分别对多个类的执行方法进行重复调用,代码变得庞大,而且繁琐

这个时候可以通过代理模式,将被调用的类,通过代理类进行调用

静态代理

下面是一个简单的代理的实现

package it.luke.proxy.v01;

/**
 * 一个请求url的对象,实现filter方法
 */
public class Url_req implements FilterInter {
    @Override
    public void filter() {
        System.out.println("running with filter ...");
    }
}


/**
 * 通过代理类的方式,调用被代理类的方法
 * 可以附加额外的一些方法
 */
class Proxy_url implements FilterInter{

    //通过聚合的方式引入对象(代理多个明星)
    Url_req url_req ;

    public void setUrl_req(Url_req url_req) {
        this.url_req = url_req;
    }

    /**
     * 模拟经纪人拉明星1去演出
     */
    @Override
    public void filter() {
        url_req.filter();
    }
    public static void main(String[] args) {
        Proxy_url proxy_url = new Proxy_url();

        //除了调用被代理类的方法,还可以调用其它的方法进行补充(经纪人还可以做一些其它的,整理行程...)
        System.out.println("开始调用代理方法....");

        //调用代理类,让代理类去调用被代理类的方法(经纪人拉着明星去演出)
        proxy_url.filter();

        //代理类的其余方法(经纪人叫车接送明星回去)
        System.out.println("代理结束...");
    }
}

以上是静态代理,需要聚合被代理类,然后实现被代理实现的接口,然后才能通过代理类去调用被代理类的方法

如果是针对多个不同的类的不同方法,可以通过实现不同的接口,对被代理类进行聚合

package it.luke.proxy.v01;

/**
 * 一个请求url的对象,实现filter方法
 */
public class Url_req implements FilterInter {
    @Override
    public void filter() {
        System.out.println("running with filter ...");
    }
}
class Url_spo implements FilterInter {

    @Override
    public void filter() {
        System.out.println("respone filter running ...");
    }
}
/**
 * 通过代理类的方式,调用被代理类的方法
 * 可以附加额外的一些方法
 */
class Proxy_url implements FilterInter{

    //通过聚合的方式引入对象(代理多个明星)
    //明星1
    Url_req url_req ;
    //明星2
    Url_spo url_spo ;
    public void setUrl_spo(Url_spo url_spo) {
        this.url_spo = url_spo;
    }

    public void setUrl_req(Url_req url_req) {
        this.url_req = url_req;
    }
    /**
     * 模拟经纪人拉明星去演出
     */
    @Override
    public void filter() {
        url_req.filter();
        url_spo.filter();
    }

    public static void main(String[] args) {
        Proxy_url proxy_url = new Proxy_url();

        //除了调用被代理类的方法,还可以调用其它的方法进行补充(经纪人还可以做一些其它的,整理行程...)
        System.out.println("开始调用代理方法....");

        //调用代理类,让代理类去调用被代理类的方法(经纪人拉着明星去演出)
        proxy_url.filter();

        //代理类的其余方法(经纪人叫车接送明星回去)
        System.out.println("代理结束...");
    }
}

小记:主要用的的都是多态的思想,让代理类实现和被代理类一样的接口,拿到相同的方法名,然后引入被代理类到代理对象中

动态代理

什么是动态代理,和静态代理不同的是,静态代理的代理类是需要自己去实现被代理类的实现接口,通过实现接口去拿到和被代理类一样的方法名

而动态代理,是程序帮你生成代理类,和静态代理一样,需要被代理类的接口,拿到相同的方法名,然后将一些有经纪人进行操作的"杂物"封装到invoke 中,通过代理类去调用

通过调用java 提供的Proxy这个类,通过newProxyInstance,获取一个代理实例,要获取这个实例需要传三个参数,通过着三个参数可以定位到被代理的类,然后通过反射的方法,对被代理类进行处理.

//ClassLoader loader, 用来加载被代理类
//Class[] interfaces, 被代理类的实现接口
//InvocationHandler h  行为处理器

InvocationHandler 行为处理器这个参数主要是要重写invoke方法,因为在动态代理生成后,底层再构建代理类的时候,会自动调用invoke这个方法.

可以通过"jdk.proxy.ProxyGenerator.saveGeneratedFiles" true 来查看保存在本地的生成代理文件

package it.luke.proxy.v02;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 动态代理
 */
public class Dynamic_proxy {
    /*
    动态代理,顾名思义,自己进行调用代理,不在需要自己去指定被代理类的实现
     */
    public static void main(String[] args) {

//        System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles","true");

        //通过设置这个参数,可以将程序动态生成的代理保存下来 不同的java 版本可能设置的参数不一样,有坑-->java8
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

        Url_req url_req = new Url_req();
        //ClassLoader loader, 用来加载被代理类
        //Class[] interfaces, 被代理类的实现接口
        //InvocationHandler h  行为处理器
        FilterInter o = (FilterInter)Proxy.newProxyInstance(Url_req.class.getClassLoader(), new Class[]{FilterInter.class},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                        System.out.println("methon " + method.getName() + "start");
                        Object invoke2 = method.invoke(url_req,args);

                        System.out.println("methon " + method.getName() + "end");
                        return invoke2;
                    }
                });
        o.filter();
    }

}

代码小记:

先准备好被代理类的类加载器(确保被代理的时候能够正确的调用到被代理类的方法)
获取被代理类的方法实现接口(确保生成的代理类拥有和被代理类一样的方法名…底层通过匹配MethonName 来调用类加载器加载的类对象对应的方法—>用到反射)
准备好执行逻辑 invoke() 需要传入一个实现了InvocationHandler接口的对象,并重写invoke()方法(invoke在代理类生成的时候,同时生成在代理类中)

最后通过调用生成的代理对象,来实现对被代理对象的处理

为了更直观的查看到动态生成的代理对象,我们将动态生成的临时代理对象保存下来
System.getProperties().put(“sun.misc.ProxyGenerator.saveGeneratedFiles”, “true”);

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.sun.proxy;

import it.luke.proxy.v02.FilterInter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements FilterInter {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m4;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void filter() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void filter_parm(Integer var1) throws  {
        try {
            super.h.invoke(this, m4, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("it.luke.proxy.v02.FilterInter").getMethod("filter");
            m4 = Class.forName("it.luke.proxy.v02.FilterInter").getMethod("filter_parm", Class.forName("java.lang.Integer"));
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

动态生成的代理类对象可以看到,在调用代理类中的filter的时候,调用了父类的h.invoke方法,其实就是前面实现InvocationHandler接口的时候定义的invoke()方法

底层是通过asm框架进行代理对象的构建,可以直接修改二进制码(待了解)

缺陷:通过jdk api进行动态代理,要求被代理类要实现接口,并且在接口中定义方法

CGlib

除了jdk proxy 方法可以进行动态代理,java还提供了CGlib
jdk 是通过接收被代理类的接口的方式匹配到被代理类的方法
而CGlib是通过继承被代理类,通过重写的方式进行代理…

  • 待补充

你可能感兴趣的:(Java设计模式,java,设计模式)