设计模式之代理模式(java实现)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/harryptter/article/details/84861939

代理模式(Proxy):结构型的设计模式,目的是为其他对象提供一种代理以控制对这个对象的访问。

即,它的思想是控制类或者接口对外的功能。

代理模式分为静态代理模式和动态代理模式两种。

在Spring中代理模式常见的是在AOP模块中,比如 JdkDynamicAopProxy 和 Cglib2AopProxy。

先说说静态代理。

举一个生活化的例子,现在有一个接口Person,儿子类(Son)实现person接口,person里面有四个方法,找工作(findJob()),租房子(rent()),找对象(findLove()),购物(buy()),现在儿子需要找对象,但他没有时间,他的父亲(Father类)接代理了这个功能,帮他物色对象。

下面是类图:

 具体实现:

Person接口和儿子类:

package Proxy.StaticProxy;

public interface Person {
    public void findLove();
    public void rent();

    public void buy();

    public void findJob();
}
package Proxy.StaticProxy;

public class Son implements Person {
    public void findLove(){
        System.out.println("肤白貌美");
        System.out.println("身高160");
    }

    public void rent(){

    };

    public void buy(){

    };

    public void findJob(){
        System.out.println("月薪2W");
        System.out.println("世界500强");

    };
}

父亲类:

package Proxy.StaticProxy;

public class Father {
    private Person person;

    //没办法扩展
    public Father(Person person){
        this.person = person;
    }

    //目标对象的引用给拿到
    public void findlove(){
        System.out.println("根据你的要求物色");
        this.person.findLove();
        System.out.println("双方父母是不是同意");
    }

}

测试类:

package Proxy.StaticProxy;

public class StaticProxyTest {
    public static void main(String[] args) {

        Father father = new Father(new Son());
        father.findlove();
    }
}

上面是测试执行结果。 可以发现静态代理的扩展会比较麻烦,如果需要扩展的话就需要在代理类中写新的方法。

所以衍生了动态代理的方式。动态代理有两种实现方式,一种是通过java反射reflect包的Proxy方法,一种是通过第三方依赖包cglib。

这里先说下Proxy方法。

通过Proxy方法实质上是通过Proxy先生成一个临时java文件,然后生成对应的.class文件,这个文件里面具体的目的就是实现所要代理类的各种指向的方法。

我这边先举例吧。依旧是以上的例子,不过我们现在选择的是代理找工作这个方法,创建一个JDK58的类,它是专门负责找工作的。

JDK58:

package Proxy.Jdk;

import Proxy.StaticProxy.Person;

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

public class JDK58 implements  InvocationHandler {
    private Person target;

    public Object getInstance(Person target) throws Exception{
        this.target = target;

        Class<?> clazz = target.getClass();

        return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(), this);

    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("我是58:我要给你找工作,现在已经拿到你的需求");
        System.out.println("开始物色");

        method.invoke(this.target,args);

        System.out.println("如果合适的话,就准备面试");

        return  null;
    }
}

增加测试类:

package Proxy.Jdk;

import Proxy.StaticProxy.Person;
import Proxy.StaticProxy.Son;
import sun.misc.ProxyGenerator;

import java.io.FileOutputStream;

public class JDK58Test {
    public static void main(String[] args) {
        try {
            Person obj = (Person)new JDK58().getInstance(new Son());
            System.out.println(obj.getClass());
            obj.findJob();
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

执行结果:

虽然用了代理Proxy,但我们并不知道它干了什么,所以,我们用一个反编译的方法, ProxyGenerator,来看看它到底生成了什么。

修改上面的测试类:

package Proxy.Jdk;

import Proxy.StaticProxy.Person;
import Proxy.StaticProxy.Son;
import sun.misc.ProxyGenerator;

import java.io.FileOutputStream;

public class JDK58Test {
    public static void main(String[] args) {
        try {
            Person obj = (Person)new JDK58().getInstance(new Son());
            System.out.println(obj.getClass());
            obj.findJob();

            //原理:
            //1、拿到被代理对象的引用,并且获取到它的所有的接口,反射获取
            //2、JDK Proxy类重新生成一个新的类、同时新的类要实现被代理类所有实现的所有的接口
            //3、动态生成Java代码,把新加的业务逻辑方法由一定的逻辑代码去调用(在代码中体现)
            //4、编译新生成的Java代码.class
            //5、再重新加载到JVM中运行
            //以上这个过程就叫字节码重组

            //JDK中有个规范,只要要是$开头的一般都是自动生成的

            //通过反编译工具可以查看源代码
            byte [] bytes = ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{Person.class});
            FileOutputStream os = new FileOutputStream("/Users/****/Documents/projects/Patterns/projectStudy/out/production/projectStudy/Proxy/$Proxy0.class");//这里是要存放生成class文件的地址,我这边是mac电脑,所以是这样的地址,****是自己电脑的名字,如果是windows电脑的话,那么应该带有盘符,例如E://
            os.write(bytes);
            os.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
执行之后,在生成的target包里面可以找到一个$Proxy0.class文件,在IDE里面打开可以看到是以下这样的:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

import Proxy.StaticProxy.Person;
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 Person {
    private static Method m1;
    private static Method m5;
    private static Method m4;
    private static Method m2;
    private static Method m6;
    private static Method m3;
    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 void rent() throws  {
        try {
            super.h.invoke(this, m5, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

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

    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 buy() throws  {
        try {
            super.h.invoke(this, m6, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

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

    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"));
            m5 = Class.forName("Proxy.StaticProxy.Person").getMethod("rent");
            m4 = Class.forName("Proxy.StaticProxy.Person").getMethod("findLove");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m6 = Class.forName("Proxy.StaticProxy.Person").getMethod("buy");
            m3 = Class.forName("Proxy.StaticProxy.Person").getMethod("findJob");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

这里会涉及到一部分类加载器和反射的知识。可以看到,但用Proxy代理之后,它会生成一个类,这个类里面实现有person类里面的所有方法,外加一个hashcode的方法,所以,当调用具体方法的时候,也就直接指向过去了。这个也就是代理模式的思想了。

cglib的话,实质和proxy差不太多,可以参考这篇文章CGLIB(Code Generation Library) 介绍与原理

猜你喜欢

转载自blog.csdn.net/harryptter/article/details/84861939