个人感悟之代理模式

  什么叫做代理模式:代理模式就是我们想要一个方法,我们不直接通过拥有这个方法的对象来获得,而是通过代理类来获得。为什么要用到代理呢,比如说我们所需要访问一个资源,从资源池里获得,我们肯定不想要去加载所有的资源,这时候我们可以通过使用代理的方法来。另外来说,根据开闭原则,我们对于一个系统更新的方式最好是拓展而不是修改,因此我们可以用代理来进行拓展。

静态代理

  比如说,我们想要租个房子,但是自己找是在是太麻烦了,我们可以把我们的需求给房产中介,让他们来帮我们完成这个工作。这就是一个比较简单的静态代理模式。静态代理模式的条件就是代理类和被代理类必须一起实现一个的接口或者继承一个父类,具体的实现代码如下所示:

  代理类和被代理类继承自同一个接口,如下

public interface IPerson {
    public void findHouse();
    public void findJob();
}

目标对象:

public class person implements IPerson {
    public void findHouse() {System.out.println("我要一个大房子");}
    public void findJob() {System.out.println("我要月薪20K以上");}
}

代理对象:

public class Agents implements IPerson {
    private IPerson IPerson;

    public Agents(IPerson IPerson) {
        this.IPerson = IPerson;
    }

    public void findHouse() {
        System.out.println("我是房产中介,请说出你的要求");
        IPerson.findHouse();
        System.out.println("Ok!,我已经帮你找到了");
    }

    public void findJob() {
        System.out.println("你的月薪要求是多少?");
        IPerson.findJob();
        System.out.println("按照你的期望80%能接受么?");
    }
}

我们可以通过一个测试来验证结果:

public class proxyTest {
    public static void main(String[] args) {
        Agents agents = new Agents(new person());
        agents.findHouse();
        agents.findJob();
    }
}
---------------------------------------------------------------------

我是房产中介,请说出你的要求
我要一个大房子
Ok!,我已经帮你找到了
你的月薪要求是多少?
我要月薪20K以上
按照你的期望80%能接受么?

  我们可以总结一下静态代理的特性:首先静态代理的优点就是我们可以在不改变目标对象的前提下就可以满足拓展功能,但是它的缺点也非常清楚,就是因为两个对象要实现同一个接口,所以说如果接口改变那么两个对象都要改变。而且对于一个代理对象,他只能帮助一类的目标对象。

 

动态代理

  我们在上面已经说明了静态代理的一些缺陷,那么接下来的动态代理或许可以克服这一个缺点,因为动态代理不需要实现同样的接口,代理对象的生成,是动态的在内存中构造代理对象,可以让我们自己去确定目标对象实现的接口。动态代理和静态代理最根本的区别就是:静态代理在代理之前所有的东西都是已知的,而动态代理在代理之前所有的东西都是未知的,只有到运行的时候才去加载。

JDK代理

  JDK动态代理的概念就是对于一个被代理的对象,我使用的并不是这个对象,而是在编译时新建的一个在内存中生成的类“$proxy0”。然后JDK动态代理需要我们的被代理对象要实现一个接口,是因为JDK动态代理只能对该类所实现接口中定义的方法进行代理。

目标对象

public class Person implements IPerson{
    public void findHouse() {}
    public void findJob() {
        System.out.println("要求1");
        System.out.println("要求2");
    }
}

代理对象

public class Agents implements InvocationHandler {
    private Person person;
    public Object getInstance(Person person) throws Exception {
        this.person = person;
        Class<?> clazz = person.getClass();
        //虽然注入的事一个类的引用,但是最后起作用的并不是这个引用
        return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
    }
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("我是房产中介");
        method.invoke(this.person, args);
        System.out.println("这个房屋比较合适");
        return null;
    }
}

测试类

public static void main(String[] args) {
        try {
            IPerson object = (IPerson) new Agents().getInstance(new Person());
            //这个时候生成的object是一个$proxy0类。
            object.findJob();
            //object.findHouse();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
---------------------------------------------------------------------------

我是房产中介
要求1
要求2
这个房屋比较合适

  这个过程是怎么做到的呢?其实这个过程就叫做字节码重组:1、拿到被代理对象的引用,通过反射获得它的所有接口。2、通过JDKproxy类生成一个新的类,这个新的类要实现被代理类的所有接口。3、在代码中把自己添加的业务逻辑由一定的逻辑代码去调用。4、编译新生成的Java代码->.class文件,再重新加载到JVM中运行。

  之前有提过,我们新生成的对象是一个在内存中创建的类叫$Proxy0,在JDK中有规定:$开头的都是自动生成的。后面的那个0是属于一个编号。我们可以通过反编译工具来查看一下源代码。

byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{Person.class});
FileOutputStream os = new FileOutputStream("/Users/jolivan/Documents/workspace(java)/***/src/main/java/$Proxy0.class");
os.write(bytes);

  这样我们可以在指定位置找到这个文件,我们可以把它打开,会得到下面这样的代码(我只截取了部分用到的)

public final class $Proxy0 extends Proxy implements Person {
    private static Method m1;
    private static Method m3;
    private static Method m9;
    private static Method m2;
    private static Method m6;
    private static Method m5;
    private static Method m4;
    private static Method m8;


    public final void wait(long var1, int var3) throws InterruptedException {
        try {
            super.h.invoke(this, m5, new Object[]{Long.valueOf(var1), Integer.valueOf(var3)});
        } catch (RuntimeException | InterruptedException | Error var5) {
            throw var5;
        } catch (Throwable var6) {
            throw new UndeclaredThrowableException(var6);
        }
    }

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

    public final Class getClass() throws  {
        try {
            return (Class)super.h.invoke(this, m8, (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", new Class[]{Class.forName("java.lang.Object")});
            m3 = Class.forName("sourceCode.designPatterns.proxy.jdk.Person").getMethod("findHouse", new Class[0]);
            m9 = Class.forName("sourceCode.designPatterns.proxy.jdk.Person").getMethod("notify", new Class[0]);
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m6 = Class.forName("sourceCode.designPatterns.proxy.jdk.Person").getMethod("wait", new Class[]{Long.TYPE});
            m5 = Class.forName("sourceCode.designPatterns.proxy.jdk.Person").getMethod("wait", new Class[]{Long.TYPE, Integer.TYPE});
            m4 = Class.forName("sourceCode.designPatterns.proxy.jdk.Person").getMethod("findJob", new Class[0]);
            m8 = Class.forName("sourceCode.designPatterns.proxy.jdk.Person").getMethod("getClass", new Class[0]);
            m10 = Class.forName("sourceCode.designPatterns.proxy.jdk.Person").getMethod("notifyAll", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
            m7 = Class.forName("sourceCode.designPatterns.proxy.jdk.Person").getMethod("wait", new Class[0]);
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

  我们可以看到,这就生成了一个新的类,我们看到最下面可以发现,他给定了我们目标对象类所有的方法(包括从Object继承的)给以编号,比如说我们要用到的findjob(),同时也可以看到其他的方法。然后我们找到findJob()这块,可以来定性分析一下。

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

  我们进入这个方法里面,super就是指父类,也就是Proxy这个类,h是指的Proxy内部自己定义的一个InvocationHandler变量对象,他是怎么获得的呢?是通过代理类的getInstance方法,会返回一个newProxyInstance,我们传入的this,就是获得的h。然后再上面代码中.invoke就是我们代理类中的重写的那个invoke方法,然后传入的参数第一个是我们新生成的类,第二个是方法,这里就是findjob方法,最后那个数组是参数,因为没有参数所以传入空。这里我们就执行了这个invoke方法。

  我们可以自己写一个Proxy类和InvocationHandler类来手动实现这个过程。

 

cglib动态代理

  这中方法其实实现思想大致一样的,但是生成的类是目标对象的子类。

目标对象

public class Person {
    public void findJob() {
        System.out.println("我的要求");
    }
}

代理对象

public class cglibAgents implements MethodInterceptor{

    public Object getInstance(Class<?> clazz) throws Exception{
        Enhancer enhancer = new Enhancer();
        //要把哪个类作为即将生成的新类的父类
        enhancer.setSuperclass(clazz);
        //说明谁来调用
        enhancer.setCallback(this);
        return enhancer.create();
    }
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("说出需求");
        methodProxy.invokeSuper(o,objects);
        System.out.println("做完了");
        return null;
    }
}

测试类

public class cglibTest {
    public static void main(String[] args) {
        cglibAgents agents = new cglibAgents();

        try {
            Person obj =(Person) agents.getInstance(Person.class);
            obj.findJob();
            System.out.println(obj.getClass());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
-----------------------------------------------------

说出需求
我的要求
做完了
class sourceCode.designPatterns.proxy.cglib.Person$$EnhancerByCGLIB$$5dc20608

这时候,我们再来确定一下动态代理的特点:在代理之前,所有的东西都是未知的。

posted @ 2018-05-21 20:52  jolivan  阅读(372)  评论(0编辑  收藏  举报