设计模式——代理模式

本文介绍了代理模式的概念、特点和UML结构图,通过租房中介、代买火车票等场景阐述其应用场景。在Java中,动态代理分为JDK动态代理和CGLIB动态代理,分别解析了它们的工作原理和实现过程,并对比了两者之间的区别。
摘要由CSDN通过智能技术生成

简介

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

代理模式一般具有以下三个特点:1) 需要具有两个角色,执行者与被代理人; 2)对于被代理人来说,这件事情是一定要做的,但是我自己又不想做或者没有时间做,找代理 ; 3)需要获取到被代理的人个人资料;

代理模式,归根到底做的是字节码重组。它可以在每一个方法调用之前加一些代码,在方法调用之后再加一些代码。

UML结构图

Subject类,定义了 RealSubject和Proxy的共用接口,这样就可以在任何使用RealSubject的地方使用Proxy。

abstract class Subject
{
    public abstract void Request();
}

RealSubject类,定义Proxy所代表的真实实体

class RealSubjec : Subject{
    public override void Request() {
        console.WriteLine("真实的请求");
    }
}

 Proxy类,保存一个引用使得代理可以访问实体,并提供一个与Subject的接口相同的接口,这样代理就可以用来替代实体。

class Proxy : Subject {
    RealSubject realSubject;
    public override void Request() {
        if(realSubject == null) {
            realSubject = new RealSubject();
        }
        realSubject.Request();
    }
}

客户端代码

static void Main(string[] args) {
    Proxy proxy = new Proxy();
    proxy.Request();
    Console.Read();
}

应用场景

比如租房中介场景中的中介与你、代买火车票场景中的黄牛与你、通过媒婆找对象场景中的媒婆与你。

在我们经常使用的Spring框架中,通过AOP实现的事物代理就是通过代理模式实现的。

动态代理实战

这里我们实现的是动态代理。在java中,动态代理分两种实现方式,一种是jdk的动态代理,一种是cglib的动态代理。

jdk动态代理

JDK动态代理所用到的代理类在程序调用到代理类对象时才由JVM真正创建,JVM根据传进来的 业务实现类对象 以及 方法名 ,动态地创建了一个代理类的class文件并被字节码引擎执行,然后通过该代理类对象进行方法调用。我们需要做的,只需指定代理类的预处理、调用后操作即可。

首先,我们要定义一个接口,来声明被代理人要执行的操作。

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

其次,被代理人要实现这个接口,具体要怎么做。

public class Xingxing implements Person{
    private String sex = "女";
    private String name = "xingxing";
    public void findLove() {
        System.out.println("我叫:" + name +",性别:"+ sex+",我找对象的要求是:");
        System.out.println("身高要求180cm以上,体重70kg");
        System.out.println("有房有车的");
        System.out.println("高富帅");
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

再者,代理要持有被代理人的引用,执行代理操作。

public class Meipo implements InvocationHandler{
    private Person target;
    // 获取被代理人的个人资料
    public Object getInstance(Person target) throws Exception {
        this.target = target;
        Class clazz =target.getClass();
        System.out.println("被代理对象的class是:" + clazz);
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),clazz.getInterfaces(),this);
    }
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("我是媒婆:您的性别是:" + this.target.getSex() +"得给您找个异性才行");
        System.out.println("开始进行海选.....");
        System.out.println("-------------");
        method.invoke(this.target, args);
        System.out.println("-----------");
        System.out.println("如果合适的话,就准备办事");
        return null;
    }
}

最后,客户端调用代理,实现被代理人的请求。

public static  void main(String[] args) {
        // 直接调用
        // new Xingxing().findLove();
        // 通过jdk代理实现
        try {
            Person obj = (Person)new Meipo().getInstance(new Xingxing());
            System.out.println(obj.getClass());
            obj.findLove();

            //原理
            //1. 拿到被代理对象的引用,然后获取他的接口
            //2. JDK代理重新生成一个类,同时实现我们给的代理对象所实现的接口
            //3. 把被代理对象的引用也拿到了
            //4. 重新动态生成一个class字节码
            //5. 然后编译

            // 获取字节码内容,并输出
            byte[] data = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{obj.getClass()});
            FileOutputStream os = new FileOutputStream("$Proxy0.class");
            os.write(data);
            os.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

 JDK动态代理的代理对象在创建时,需要使用业务实现类所实现的接口作为参数(因为在后面代理方法时需要根据接口内的方法名进行调用)。如果业务实现类是没有实现接口而是直接定义业务方法的话,就无法使用JDK动态代理了。并且,如果业务实现类中新增了接口中没有的方法,这些方法是无法被代理的(因为无法被调用)。

cglib动态代理

cglib是针对类来实现代理的,原理是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理。因为采用的是继承,所以不能对final修饰的类进行代理。 

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

接口,看下代理类的实现

public class YMMeipo implements MethodInterceptor {

    public Object getInstance(Class clazz) throws Exception {
        // 通过反射机制,给他实例化
        Enhancer enhancer = new Enhancer();
        // 把父类设置为谁
        // 这一步就是告诉cglib,生成的子类需要继承哪个类
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        // 第一步 生成源代码
        // 第二步 编译成class文件
        // 第三步 加载到jvm中,并返回被代理对象
        return enhancer.create();

    }
    // 同样是做了字节码重组这样一件事
    // 对于使用API的用户来说,无感知
    @Override
    public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("我是媒婆:得给您找个异性才行");
        System.out.println("开始进行海选.....");
        System.out.println("-------------");
        // 这个object的引用是由cglib给我们new出来的
        //cglib new 出来以后的对象,是被代理对象的子类(继承了我们自己写的类)
        //oop,在new子类之前,实际上默认先调用了我们super()方法的
        // new子类的同时,必须先new出来父类,这就相当于是间接的持有了我们父类的引用
        //子类重写了父类的所有的方法
        //我们改变子类对象的某些属性,是可以间接的操作到父类的属性的
        methodProxy.invokeSuper(obj, objects);
        System.out.println("-----------");
        System.out.println("如果合适的话,就准备办事");
        return null;
    }
}

最后,客户端调用这样实现就完成了。

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

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

区别

 JDK动态代理是通过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法;CGlib动态代理是通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值