设计模式(六)-代理模式

什么是代理模式?

代理模式(Proxy),为其他对象提供一种代理以控制对这个对象的访问。

可以说,生活中处处能见到代理模式,经纪人、中介、黄牛,甚至以前的媒婆都是代理模式。就拿经纪人来说吧,流量巨星蔡徐坤,就是那个喜欢唱、跳、rap和篮球的男人。他的经纪人葛福鸿(特地百度的)就是代理模式。现在来假设一下葛福鸿为蔡徐坤签下NBA中国形象大使的过程,以此来了解代理模式的原理:首先,蔡徐坤火肯定有她的道理,说明她很努力,那么她肯定背后下了很多功夫来提升自己的技能(唱、跳、rap和篮球),换句话说她很忙,没时间亲自去找商业合作,包括和谈、签订合同、收款等事宜。当然去往其他城市也需要定好行程,订好机票,酒店等一系列的事情。
从中我们可以看到这些事情都是蔡徐坤必须要做的,但是像找商业合作等事情,他肯定不擅长或者压根就不会做,而安排行程这些麻烦事,蔡徐坤显然懒得做,也没时间做。这不,经济人就出现了,并且很好的解决了这些问题。有了葛福鸿这个经纪人,蔡徐坤就可以心无旁骛的努力磨炼自己的技能,只在需要出场的时候表演一二就可以了。
还有,像蔡徐坤这种大牌明星,找他谈合作的人多如牛毛,要是他自己一一接谈,那肯定累的够呛。于是,所有的电话都会打给经纪人,经纪人会进行合理的筛选,最终选出符合蔡徐坤身份的代言活动,这或许就是NBA形象大使的由来了吧!
还有,我们不要小瞧了经纪人葛福鸿的能力,作为职业经纪人的他甚至可以同时作为多个明星的经纪人,就比方说他可以同时担任蔡徐坤和鹿晗的经纪人。

看了以上的例子我们就大概明白了代理模式的特点了,总结一下:
满足代理模式应用的三个必要条件:
1、两个角色:被代理对象,执行者
2、注重过程,必须要做,被代理对象不想做或没时间做
3、需要获取到被代理对象的信息

代理模式最大的优点就是在访问对象时引入一定程度的间接性,因这种间接性而附加各种用途。

代理模式结构图:
在这里插入图片描述

  • 分类
  • 静态代理(静态定义代理类)
  • 动态代理 (动态生成代理类)
    • JDK自己的动态代理
    • javaassit字节码操作库实现
    • CGLIB
    • ASM(底层使用指令,可维护性差)

代码示例:

静态代理的代码示例
  • 定义一个接口
       public interface Tenement {
        		    // 寻找房客
        		    void findTenant();
        		    // 带人看房
        		    void surveyHourse();
        		    // 签合同
        		    void signContract(); // 只能和房主签o
        		    // 收定金
        		    void collectMoney();
        		    // 交付钥匙
        		    void giveKey();
        		}
  • 真实的实现类
public class RealTenement implements Tenement {
		    @Override
		    public void findTenant() {
		        System.out.println("RealTenement.findTenant()");
		    }
		
		    @Override
		    public void surveyHourse() {
		        System.out.println("RealTenement.surveyHourse()");
		    }
		
		    @Override
		    public void signContract() {
		        System.out.println("RealTenement.signContract(房东亲自签的哦)");
		    }
		
		    @Override
		    public void collectMoney() {
		        System.out.println("RealTenement.collectMoney()");
		    }
		
		    @Override
		    public void giveKey() {
		        System.out.println("RealTenement.giveKey()");
		    }
		}
  • 代理类
public class ProxyTenement implements Tenement{
		    private Tenement tenement;
		
		    public ProxyTenement(Tenement tenement) {
		        super();
		        this.tenement = tenement;
		    }
		
		    @Override
		    public void findTenant() {
		        System.out.println("ProxyTenement.findTenant()");
		    }
		
		    @Override
		    public void surveyHourse() {
		        System.out.println("ProxyTenement.surveyHourse()");
		    }
		
		    @Override
		    public void signContract() {
		        // 调用真实对象的方法
		        tenement.signContract();
		    }
		
		    @Override
		    public void collectMoney() {
		        System.out.println("ProxyTenement.findTenant()");
		    }
		
		    @Override
		    public void giveKey() {
		        System.out.println("ProxyTenement.giveKey()");
		    }
		}
  • 客户端测试类
public class Client {
		    public static void main(String[] args) {
		        Tenement tenement = new RealTenement();
		        ProxyTenement proxyTenement = new ProxyTenement(tenement);
		        proxyTenement.findTenant();
		        proxyTenement.surveyHourse();
		        proxyTenement.signContract();
		        proxyTenement.collectMoney();
		        proxyTenement.giveKey();
		    }
		} 

总结:
代理类与真实类实现同一个接口,而代理类无法自行处理某些事件时,通过传递的真实类的对象引用调用真实类的方法;而客户只需要关注接口即可。

动态代理代码示例

一、JDK自带的动态代理实现

java.lang.reflect.Proxy

  • 作用:动态生成代理类和对象

java.lang.reflect.InvocationHandler

  • 可以通过invoke实现对真实角色的代理访问
  • 每次通过proxy生成代理类对象时都要指定对应的处理器类

代码示例:

接口、真实角色类 与上相同

  • Handler类
public class TenementHandler implements InvocationHandler {
		
		    Tenement realTenement;
		
		    public TenementHandler(Tenement realTenement) {
		        super();
		        this.realTenement = realTenement;
		    }
		
		    @Override
		    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		        method.invoke(realTenement,args);
		        return null;
		    }
		}
  • client类
public class Client {
		    public static void main(String[] args) {
		        // 创建真实对象
		        Tenement realTenement = new RealTenement();
		        // 创建Handler
		        TenementHandler handler = new TenementHandler(realTenement);
		        // 生成代理类和对象
		        Tenement proxy =  (Tenement) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class[]{Tenement.class},handler);
		        // 通过返回的代理类对象 调用方法
		        proxy.findTenant();
		        proxy.surveyHourse();
		        proxy.signContract();
		        proxy.collectMoney();
		        proxy.giveKey();
		    }
		}

动态代理的调用过程
在这里插入图片描述
深入分析jdk动态代理的实现原理

原理
1、拿到被代理对象的引用和接口
2、jdk代理重新生成一个类,实现代理对象实现的接口
3、拿到代理队形的引用
4、重新动态生成一个class字节码
5、直接加载

代码示例(实现JDK代理):

定义一个接口 Person

public interface Person {
    void findLove();
//    String getSex();
//    String  getName();
}

创建一个Zhangsan类实现 Person

public class Zhangsan  implements Person{
    private  String sex = "女";
    private  String name = "隔壁老王";
    public void findLove() {
        System.out.println("我叫:"+this.name+"性别:"+this.sex+"我的要求:");
        System.out.println("高富帅");
        System.out.println("有房有车");
        System.out.println("身高180cm以上,体重70KG");
    } 
}

自定义MyInvocatiionHandle

public interface MyInvocationHandle {
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

自定义加载类 MyClassLoader

public class MyClassLoader extends ClassLoader{
    private File baseDir;
    public  MyClassLoader(){
         String  basePath = MyClassLoader.class.getResource("").getPath();
         this.baseDir = new File(basePath);
    }
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        String className = MyClassLoader.class.getPackage().getName()+"."+name;
        if(baseDir!=null){
            File classFile = new File(baseDir,name.replaceAll("\\.","/")+".class");
            if(classFile.exists()){
                FileInputStream in =null;
                ByteArrayOutputStream out =null;
                try{
                    in = new FileInputStream(classFile);
                    out  =new ByteArrayOutputStream();
                    byte[] buff = new byte[1024];
                    int len;
                    while ( (len= in.read(buff))!=-1){
                        out.write(buff,0,len);
                    }
                    return defineClass(className,out.toByteArray(),0,out.size());
                }catch (Exception e){
                    e.printStackTrace();
                }finally {
                    if(null!=in&&null!=out){
                        try {
                            in.close();
                            out.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        // 编译完以后删除
                        classFile.delete();
                    }
                }
            }
        }
        return null;
    }
}

自定义生成代理类 MyProxy

public class MyProxy {
    private  static String ln ="\r\n";
    public static Object newProxyInstance(MyClassLoader classLoader,
                                          Class<?>[] interfaces,
                                          MyInvocationHandle h)
            throws IllegalArgumentException
    {
        // 1、生成源代码
        String proxySrc = generateSrc(interfaces[0]);
        // 2、将生成的源代码输出到磁盘,保存为.java文件
        try {
            String filePath = MyProxy.class.getResource("").getPath();
            File f = new File(filePath + "$Proxy0.java");
            FileWriter fw = new FileWriter(f);
            fw.write(proxySrc);
            fw.flush();
            fw.close();
            // 3、编译源代码生成.class文件
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager manager  =  compiler.getStandardFileManager(null,null,null);
            Iterable iterable = manager.getJavaFileObjects(f);
            JavaCompiler.CompilationTask task = compiler.getTask(null,manager,null,null,null,iterable);
            task.call();
            manager.close();
            // 4、将class文件中的内容,动态加载到JVM中来

            // 5、返回被代理后的代理对象
            Class proxyClass   = classLoader.findClass("$Proxy0");
            Constructor c= proxyClass.getConstructor(MyInvocationHandle.class);
            // 读完以后删除
            f.delete();
            return c.newInstance();
        }catch (Exception e){
            e.printStackTrace();
        }


        return null;
    }
    private static String generateSrc(Class<?>  interfaces){
        StringBuffer src = new StringBuffer();
        src.append("package src.main.java.custom;"+ln);
        src.append("import java.lang.reflect.Method;"+ln);
        src.append("public class $Proxy0 implements " + interfaces.getName()+"{"+ln);
        src.append("MyInvocationHandle h;"+ln);

        src.append("public $Proxy0(MyInvocationHandle h){"+ln);
        src.append("this.h=h;"+ln);
        src.append("}"+ln);

        for (Method m:interfaces.getMethods()){
           src.append("public" + m.getReturnType()+ " " + m.getName() + "() throws Throwable {"+ln );
           src.append("try {"+ln);
           src.append("Method m = "+interfaces.getName()+".class.getMethod(\"" +m.getName()+"\",new Class[]{});" +ln );
           src.append("this.h.invoke(this,m,null);"+ln);
           src.append("}catch(Exception e){}"+ln);
           src.append("}"+ln);
        }
        src.append("}");
        return src.toString();
    }
}

定义代理执行类实现自定义的MyInvocationHandle

public class MyMeipo implements MyInvocationHandle {
    private Person target;  //代理对象的引用作为成员变量保存下来
    // 获取被代理的资料
    public Object getInstance(Person target) throws Exception{
        this.target = target;
        Class clazz = target.getClass();
        System.out.println("被代理队象的class是:"+clazz);
        return MyProxy.newProxyInstance(new MyClassLoader(),clazz.getInterfaces(),this);
    }

   @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//        System.out.println("你的性别是:"+this.target.getSex()+"得给你找个异性才行");
        System.out.println("开始你的筛选。。。");
        System.out.println("----------------------------");
        System.out.println("如果合适就弄事");
//        this.target.findLove();
         method.invoke(this.target,args);  // 反射调用
        return null;
    }
}

测试类TestCustom

public class TestCustom {
    public static void main(String[] args) {
        try {
            Person object = (Person)new Meipo().getInstance(new Zhangsan());
            System.out.println("代理对象是:"+object);
            object.findLove();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

二、cglib的动态代理实现原理
原理:
cglib代理与jdk代理的最大不同在于,cglib代理不需要实现一个接口,cglib通过创建一个类继承被代理类从而实现对代理对象的间接访问。

代码实例:
首先创建一个具体的类 Wangnima

public class Wangnima {
    public void findLove(){
        System.out.println("肤白貌美大长腿");
    }
}

创建一个代理类 Meipo

public class Meipo implements MethodInterceptor {
    //疑问
    // 好像没有持有被代理对象的引用??
    public Object getInstance(Class clazz) throws Exception {
        Enhancer enhancer = new Enhancer();
        // 设置父类
        // 这一步就是告诉cglib生成的子类应该继承哪个类
        enhancer.setSuperclass(clazz);
        // 设置回调
        enhancer.setCallback(this);
        return enhancer.create();
    }

    /**
     * @Author DevinLei
     * @Description //实现原理:生成一个类继承被代理对象
     **/
    // 同样是做了字节码重组的事情
    // 但是对java用户来说无感知
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("我是你妈");
        System.out.println("开始你的筛选。。。");
        System.out.println("----------------------------");
        // 这个obj的引用是由cglib给new出来的
        // cglib new出来的对象,是被代理对象的子类(继承了我们自己写的那个类)
        // oop在new 子类之前,实际上是默认先调用了super()方法的
        // 也就是说new 子类之前先new 父类,这就间接持有了父类的引用
        // 子类重写了父类的所有方法
        // 我们改变子类对象的某些属性时,是可以间接操作父类属性的
        proxy.invokeSuper(obj,args);  // 特别注意:这里如果是invoke()则会陷入死循环中
        System.out.println("如果合适就弄事");
        return null;
    }
}

创建一个测试类 :TestCglibProxy

public class TestCglibProxy {
    public static void main(String[] args) {
        //jdkd的动态代理是通过接口来强制转换的
        // 生成以后的对象,可以强制装换为接口

        // cglib的动态代理是通过生成一个被代理对象的的子类,然后重写父类方法
        // 生成以后的对象可以强制转换为被代理的对象(也就是自己写的类)
        // 子类引用赋值给父类
        try {
            Wangnima obj = (Wangnima)new Meipo().getInstance(Wangnima.class);
            obj.findLove();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

代理模式的应用

  • 安全代理:屏蔽真是角色的直接访问
  • 远程加速:通过代理类处理远程方法调用
  • 延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象
  • spring AOP ,Mybatis中都使用了代理模式。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值