年轻人不讲码德之深入理解代理模式

399 阅读4分钟

什么是代理模式

代理模式的定义:由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。

静态代理

接下来用java代码举例一个儿子找对象,父亲帮忙相亲的静态代理的例子。 接口

public interface Person {

    void findLove();
}

被代理类

public class Son implements Person {
    @Override
    public void findLove(){
        System.out.println("Son找对象要肤白貌美");
    }
}

** 代理类**

public class Father implements Person{

    private Person person;

    public Father(Person person) {
        this.person = person;
    }

    public void findLove(){
        System.out.println("父亲物色对象");
        this.person.findLove();
        System.out.println("双方同意,确认关系");
    }
}

测试用例

public class FatherproxyTest {

    public static void main(String[] args) {
        Father father = new Father(new Son());
        father.findLove();
    }
}

运行结果 在这里插入图片描述

java静态代理优缺点

优点: 在客户端与目标对象之间起到一个中介作用和保护目标对象的作用; 代理对象可以扩展目标对象的功能; 能将客户端与目标对象分离,在一定程度上降低了系统的耦合度,增加了程序的可扩展性

其主要缺点是: 会造成系统设计中类的数量增加 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢; 增加了系统的复杂度; 那么如何解决以上提到的缺点呢?答案是可以使用动态代理方式

动态代理

动态代理就是,在程序运行期,创建目标对象的代理对象,并对目标对象中的方法进行功能性增强的一种技术。在生成代理对象的过程中,目标对象不变,代理对象中的方法是目标对象方法的增强方法。可以理解为运行期间,对象中方法的动态拦截,在拦截方法的前后执行功能操作。

java动态代理例子

Gril类

public class Gril  implements Person {
    @Override
    public void findLove() {
        System.out.println("高富帅");
        System.out.println("身高180");
        System.out.println("要求程序员");
    }
}

Matchmaker类

public class Matchmaker implements InvocationHandler {

    private Person target;
    public Object getInstance(Person person){
        this.target = person;
        Class<?> clazz = target.getClass();
        return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            before();
        Object obj = method.invoke(this.target, args);
            after();
        return obj;
    }
    private void before(){
        System.out.println("媒婆根据筛选");
        System.out.println("开始收钱");
    }
    private void after(){
        System.out.println("OK成功,拿到尾款");
    }
}

** 测试类**

public class ProxyTest {
    public static void main(String[] args) throws IOException {
        Person obj = (Person) new Matchmaker().getInstance(new Gril());
        obj.findLove();
      
   }
}

运行结果 在这里插入图片描述

java动态代理原理

有人肯定很好奇,JDK是怎么实现的,为什么能够调用到invoke方法 在这里插入图片描述 根据上面的debug,我们可以发现,在程序运行期间创建了一个对象为proxy0的,那我们将它打输出来,看看里面的内容。 下面将上诉代码进行一下修改。

    Person obj = (Person) new Matchmaker().getInstance(new Gril());
        obj.findLove();
        // 生成代理类的字节码
        byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Person.class});
        // 输出到相关路径下
        FileOutputStream fos = new FileOutputStream("E://$Proxy0.class");
        fos.write(bytes);
        fos.close();

$Proxy0

public final class $Proxy0 extends Proxy implements Person {
  // 四个静态方法 下方静态初始化
    private static Method m1;
    private static Method m3;
    private static Method m2;
    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 findLove() throws  {
        try {
          // 调用invoke方法
            super.h.invoke(this, m3, (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 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);
        }
    }
	静态初始化m0-m4
    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            // 被代理的方法
            m3 = Class.forName("proxy.Person").getMethod("findLove");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

上述代码的构造方法可以看到,传入了InvocationHandler var1,传入进去的就是Matchmaker,因为matchmaker实现了InvocationHandler,另外在findLove方法里有 super.h.invoke(this, m3, (Object[])null); 因此这也是为什么调用findlove方法,底层还执行了**invoke**方法的原因。

动态代理使用场景

SpringAOP

Spring框架中我们常用的AOP面向切面编程,其底层原理就是运用的动态代理,通过生成一个代理类,来进行方法执行前后的增强,例如日志输出,懒加载等。 @Before 前置增强

@AfterReturning 返回增强

@After 后置增强

@AfterThrowing 异常增强

@Around 环绕增强

Spring中以上的几种增强都是通过动态代理的方式,具体源代码就需要通过大家自己去看了。

世界晚安!