深入分析代理模式

代理模式(Proxy)

特点

  对于被代理人来说, 这件事情是一定要做的, 但是我自己又不想做或者没有时间做
  对于代理人而言, 需要获取到被代理的人个人资料,并且只参与整个过程的某个或几个环节。

应用场景

  为其他对象提供一种代理以控制对这个对象的访问。
  从结构上来看和 Decorator 模式类似,但 Proxy 是控制,更像是一种对功能的限制,而 Decorator 是增加职责。Spring 的 Proxy 模式在 AOP 中有体现,比如JdkDynamicAopProxy 和 Cglib2AopProxy。
穷举:租房中介、 售票黄牛、 婚介、经纪人、 快递、 事务代理、 非侵入式日志监听

静态代理

  在代理之前,所有东西必须都是已知的(代理过程需要人工完成)。上代码:

//通用接口:人
public interface Person {

    public void findLove();

    public void findJob();

    //......
}
//被代理类:儿子
public class Son implements Person{

    public void findLove(){
		//找对象的要求
        System.out.println("找对象,肤白貌美大长腿");
    }

    @Override
    public void findJob() {

    }
}
//代理类:父亲
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("双方父母是不是同意");
    }

}

//测试类
public class StaticProxyTest {

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

输出结果:
在这里插入图片描述
  上述代码描述的场景,是父亲帮儿子找对象的场景。相当于代理模式,通过静态代理,我们需要在父亲对象中手动写findLove(),完成代理过程。缺点:很麻烦,不够智能化。

动态代理

  在代理之前,所有东西都是未知的。(自动化,智能化)。

jdk动态代理

  被代理类需要实现接口,代理类通过实现相同的接口,进行动态代理。上代码:

//场景:找工作
//被代理类 Jeremy
public class Jeremy implements Person{

    public void findLove(){
        System.out.println("高富帅");
        System.out.println("身高180cm");
        System.out.println("胸大,6块腹肌");

    }

    @Override
    public void findJob() {
        System.out.println("月薪20K-50k");
        System.out.println("找工作");
    }
}
//代理类 58同城
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;
    }
}
//测试类
public class JDKProxyTest {

    public static void main(String[] args) {

        try {
            Person obj = (Person)new JDK58().getInstance(new Jeremy());
            System.out.println(obj.getClass());
            obj.findJob();

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

            //通过反编译工具可以查看源代码
            byte [] bytes = ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{Person.class});
            FileOutputStream os = new FileOutputStream("D://$Proxy0.class");
            os.write(bytes);
            os.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

输出结果:
在这里插入图片描述

深入分析

反编译后的$Proxy0.class文件:

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

    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 {
            super.h.invoke(this, m2, (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, m4, (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, m0, (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, m3, (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("com.jeremy.www.pattern.proxy.staticed.Person").getMethod("findLove");
            m4 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("com.jeremy.www.pattern.proxy.staticed.Person").getMethod("findJob");
            m3 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

  源码分析可以参照代理模式源码分析,讲的不错。以后在jdk源码分析中,我也会写自己的文章,到时再修改链接。

  jdk动态代理是通过字节码重组来实现的。
  总结下代码中字节码重组的步骤:

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

cglib动态代理

  cglib代理类通过继承被代理类,达到代理的效果。

//被代理人:张三	代理业务:找对象
public class ZhangSan {
    public void findLove(){
        System.out.println("肤白貌美大象腿");
    }
}
//代理人:媒婆
public class CglibMeipo implements MethodInterceptor{

    public Object getInstance(Class<?> clazz) throws  Exception{

        Enhancer enhancer = new Enhancer();
        //要把哪个设置为即将生成的新类父类
        enhancer.setSuperclass(clazz);

        enhancer.setCallback(this);

        return  enhancer.create();

    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        //业务的增强

        System.out.println("我是媒婆:我要给你找对象,现在已经拿到你的需求");
        System.out.println("开始物色");

        methodProxy.invokeSuper(o,objects);

        System.out.println("如果合适的话,就准备办事");
        return null;
    }
}
//测试类
public class CglibTest {
    public static void main(String[] args) {
        try {
            ZhangSan obj = (ZhangSan)new CglibMeipo().getInstance(ZhangSan.class);
            obj.findLove();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

输出结果:
在这里插入图片描述

一图流

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值