代理模式详细学习笔记

代理模式

代理模式(Proxy Pattern):为其他对象提供一种代理以控制对这个对象的访问。属于结构型模式。
在客户和目标对象间起中介作用,保护、增强目标对象,解耦,易扩展;但增加了系统复杂度,延长了处理时间。

在面向对象的编程之中,如果我们想要代理类和目标类可以实现相同的功能,有两种方式:

  1. 一个比较直观的方式,就是定义一个功能接口,然后让代理类和目标类来实现这个接口。
  2. 还有比较隐晦的方式,就是通过继承。因为如果代理类继承自目标类,这样代理类则拥有了目标类的功能,代理类还可以通过重写目标类中的方法,来实现多态。

一.JDK静态代理

方式:目标类和代理类同时实现一个目标接口,在代理类对象中持有一个目标接口类型的对象的引用,在代理类的方法中调用该目标接口的方法。

目标接口 Persion.java

public interface Person {
    void findHouse();  
}

目标类 Myself.java

public class Myself implements Person {
    public void findHouse() {
        System.out.println("想租房");
    }
}

代理类 Friend.java

public class Friend implements Person {
    private Person person;
    public Friend(Person person) {
        this.person = person;
    }
    public void findHouse() {
        System.out.println("我是朋友");
        this.person.findHouse();
        System.out.println("帮忙找到了");
    }
    
}

测试类 FriendProxyTest.java

public class FriendProxyTest {
    public static void main(String[] args) {
        Friend friend = new Friend(new Myself());
        friend.findHouse();
    }
}

缺点:采用硬编码的方式,一个代理类只能为一个目标类服务,造成系统臃肿,且不符合开闭原则。

二.JDK动态代理

机制:利用反射在运行时创建代理类。

1. 目标类实现一个目标接口(必需)。

目标类 租客 Tenant.java

public class Tenant implements Person {
    public void findHouse() {
        System.out.println("租房要求");
    }
}

2. 实现调用处理器接口InvocationHandler,声明目标接口类型的成员变量,重写invoke方法,在invoke方法中,反射调用成员变量的方法。

调用处理器类 房屋中介 JDKHouseAgent.java

public class JDKHouseAgent implements InvocationHandler {

    private Person target;

    public JDKHouseAgent (Person person) {
        this.target = person;
    }

    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("我是中介");
    }

    private void after() {
        System.out.println("这是找到的房子,中介费X元");
    }
}

3. 运行时,调用Proxy.newProxyInstance方法,指定目标类、目标接口数组、调用处理器,动态地创建一个代理类$proxy0的对象,它继承了Proxy类,实现了目标接口。

在调用$Proxy0代理类对象中的每一个方法时,在代码内部,都是直接调用了Proxy类持有的InvocationHandler成员的invoke方法,(此处InvocationHandler成员实际被指定为JDKHouseAgent类型),而invoke方法被我们在JDKHouseAgent类中重写了,它会根据传入的方法参数,反射调用目标类的目标方法。具体逻辑参见更下方的$Proxy0.class。?

测试类 JdkProxyTest.java

public class JdkProxyTest {

    public static void main(String[] args) {
        try {
            Person target = new Tenant();
            InvocationHandler handler = new JDKHouseAgent(target);
            //生成代理类的实例,这一步骤也可写在JDKHouseAgent类中,包装为一个getInstance方法
            Person obj = (Person) Proxy.newProxyInstance(
                    target.getClass().getClassLoader(), //指定目标类加载器
                    target.getClass().getInterfaces(),  //指定目标接口数组
                    handler);                           //指派调用处理器
            System.out.println("obj的类型 :" + obj.getClass());
            obj.findHouse(); //类型强转,才能调用到findHouse方法
            
            //将运行时生成的代理类字节码输出到磁盘
            byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Person.class});
            FileOutputStream fos = new FileOutputStream("$Proxy0.class");
            fos.write(bytes);
            fos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

运行时生成的字节码文件 $Proxy.class

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

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{v
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值