一天攻克一个知识点 —— 设计模式之动态代理

一、设计模式之代理设计

代理设计是在Java开发中使用较多的一种设计模式,所谓的代理设计模式就是指一个代理主体操作真实主体,真实主体操作具体业务,代理主体负责给具体业务添砖加瓦。

就好比在生活中你有一套房子想要出租(你=真实主体),你找了房产中介(中介=代理主体)。真实主体负责提供房屋和收钱,代理主体负责带客户看房 、扣除手续费。租客只管租房子和付款,至于谁提供的房子并不关心。

我们发现,只需要定义一个租赁的接口,真实主体与代理主体都可以实现此接口,然后再由代理主体操作真实主体即可,上面的要求可以形成如下的代码:

1. 定义租赁接口

java 复制代码
package com.javabasic.proxy;

/**
 * 定义抽象接口
 */
public interface House {

    /**
     * 定义抽象方法 租赁
     */
    void rent();
}

2. 定义真实主体 - 房东

java 复制代码
package com.javabasic.proxy;

/**
 * 定义真实主体 房东
 */
public class Owner implements House{

    @Override
    public void rent() {
        System.out.println("房东提供房子,收取租金200元/月");
    }
}

3. 定义代理主体 - 中介

java 复制代码
package com.javabasic.proxy;

/**
 * 定义代理主体 中介
 */
public class Agent implements House{

    // 接收真实主体 房东
    private House house;

    /**
     * 通过构造方法,让中介与房东之间产生联系。
     * 根据Java动态绑定机制,House运行时的真实类型是房东类型,就可以获取到房东的出租行为。
     * @param house
     */
    public Agent(House house){
        this.house = house;
    }

    /**
     * 中介对房东出租业务进行增强
     */
    @Override
    public void rent() {
        rentBefore();
        house.rent(); // 房东 出租
        rentAfter();
    }

    private void rentBefore(){
        System.out.println("中介负责寻找租客、带看、保存租客信息、签合同、收取租金。");
    }

    private void rentAfter(){
        System.out.println("中介负责租客退租后的清扫工作。");
    }

}

4. 主程序 中介出租房屋

二、动态代理

上面讲解的代理机制的操作,属于静态代理,因为每一个代理类只能为一个接口服务,这样程序开发中必然会产生过多的代理类。最好的做法是可以通过一个代理类完成全部的代理功能,那么此时就必须使用动态代理功能。

Java 中想要实现动态代理的机制,则需要java.lang.reflect.InvocationHandler接口java.lang.reflect.Proxy类的支持。

InvocationHandler接口的定义如下:

java 复制代码
package java.lang.reflect;

public interface InvocationHandler {

    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类。

java 复制代码
	/*
		ClassLoader loader : 类加载器
		Class<?>[] interfaces : 得到真实类实现的全部接口
		InvocationHandler h : 得到InvocationHandler子类实例
	*/
    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        ...
    }

房屋租赁代码修改如下 :

1. 需要房东实现租赁的接口

java 复制代码
public interface House {

    /**
     * 定义抽象方法 租赁
     */
    void rent();
}

public class Owner implements House{

    @Override
    public void rent() {
        System.out.println("房东提供房子,收取租金200元/月");
    }
}

2. 创建代理类(InvocationHandler / Proxy)

java 复制代码
package com.javabasic.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Agent2 implements InvocationHandler {

    // 接收真实主体
    private Object obj;

    // 对外提供绑定真实主体方法
    public Object bind(Object obj){
        this.obj = obj;
        // 通过真实对象创建代理对象并返回给调用者。
        /*
             Proxy.newProxyInstance : 三个参数
                    类加载器 :  this.obj.getClass().getClassLoader()
                    真实类型实现的所有接口 : this.obj.getClass().getInterfaces()
                    代理类  : this
             return 代理对象
         */
        Object object = Proxy.newProxyInstance(this.obj.getClass().getClassLoader(),
                this.obj.getClass().getInterfaces(), this);
        System.out.println("bind之后object真实类型 = " + object.getClass().getName());
        return object;
    }

    /**
     * 代理类会执行的方法。
     * @param proxy 代理主体
     * @param method 调用的方法
     * @param args 方法的参数
     *
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("proxy真实类型 = " + proxy.getClass().getName());
        
        rentBefore();
        method.invoke(this.obj, args); // 通过反射执行真实主体方法
        rentAfter();
        return null;
    }

    private void rentBefore(){
        System.out.println("中介负责寻找租客、带看、保存租客信息、签合同、收取租金。");
    }

    private void rentAfter(){
        System.out.println("中介负责租客退租后的清扫工作。");
    }
}

3. 主程序 出租

相关推荐
路在脚下@11 分钟前
spring boot的配置文件属性注入到类的静态属性
java·spring boot·sql
森屿Serien14 分钟前
Spring Boot常用注解
java·spring boot·后端
苹果醋31 小时前
React源码02 - 基础知识 React API 一览
java·运维·spring boot·mysql·nginx
Hello.Reader2 小时前
深入解析 Apache APISIX
java·apache
菠萝蚊鸭2 小时前
Dhatim FastExcel 读写 Excel 文件
java·excel·fastexcel
旭东怪2 小时前
EasyPoi 使用$fe:模板语法生成Word动态行
java·前端·word
007php0072 小时前
Go语言zero项目部署后启动失败问题分析与解决
java·服务器·网络·python·golang·php·ai编程
∝请叫*我简单先生2 小时前
java如何使用poi-tl在word模板里渲染多张图片
java·后端·poi-tl
ssr——ssss3 小时前
SSM-期末项目 - 基于SSM的宠物信息管理系统
java·ssm
一棵星3 小时前
Java模拟Mqtt客户端连接Mqtt Broker
java·开发语言