怀旧网,博客详情:设计模式--代理模式

1、设计模式介绍

2、设计模式--单例模式

3、设计模式--工厂模式

4、设计模式--抽象工厂模式

5、设计模式--建造者模式

6、设计模式--原型模式

7、设计模式--适配器模式

8、设计模式--桥接模式

9、设计模式--代理模式

原创

设计模式--代理模式

代理模式简介

可以参看往期博客:网页链接

代理模式,是SpringAOP的低层实现

image-20240605170601753

静态代理模式

角色分析:

  • 抽象角色: 一般会使用接口或抽象类来解决(租房接口)

  • 真实角色: 被代理的角色(房东)

  • 代理角色: 代理真实角色,代理真实角色后,我们一般会做一些附属操作(中介)

  • 客户: 访问代理对象的人(租户)

代码实现:

// ------- 租房接口。代理对象与被代理对象,都实现这个接口 ------- 
public interface Rent {
    void rent();
}
// ------- 房东,实现租房接口 ------- 
public class Host implements Rent {
    @Override
    public void rent() {
        System.out.println("房东出租房子");
    }
}
/**
 * 代理对象
 * 使用组合将被代理的对象,集合进来(尽量不适用继承)
 */
public class Proxy implements Rent {
    Host host;  //房东:被代理的对象

    public Proxy(Host host) {   //构造方法中,传入房东
        this.host = host;
    }

    //中介代理房东出租房子,执行一些额外的操作
    @Override
    public void rent() {
        watchHouse();   //额外操作1.租房子
        host.rent();
        charge();       //额外操作2.收中介费
    }

    private void watchHouse(){
        System.out.println("中介带你看房");
    }
    private void charge(){
        System.out.println("中介带你看房");
    }
}
// ------------ 测试 -----------------
public static void main(String[] args) {
    Host host = new Host();     //房东要出租房子
    Proxy proxy = new Proxy(host);  //中介代理房东,与租户打交道
    proxy.rent();   //租户找中介,租房子
}

输出结果:
    中介带你看房
    房东出租房子
    中介带你看房

总结:

代理模式的好处:

  • 可以使真实角色的操作更加纯粹,不用去关注一些公共的业务
  • 公共业务交给代理角色,实现了业务的分工
  • 公共业务发生扩展的时候,方便集中管理

缺点

  • 一个真实角色就会产生一个代理角色,代码量会翻倍,开发效率会变低

静态代理加深理解:

image-20240605170739881

//1. ---------- 业务层的接口,增删改查方法 ---------- 
public interface IUserService {
    public void add();
    public void delete();
    public void modify();
    public void search();
}

//2. ------- 被代理的类:业务层具体实现 --------
public class UserService implements IUserService {
    @Override
    public void add() { System.out.println("新增方法"); }
    @Override
    public void delete() { System.out.println("删除方法"); }
    @Override
    public void modify() {  System.out.println("修改方法"); }
    @Override
    public void search() { System.out.println("查询方法"); }
}

//3.-----代理类(与被代理类,实现同一个接口),实现日志功能
public class UserServiceProxy implements IUserService{
    //被代理的类私有化,使用set方法注入
    private UserService userService;

    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    /**
     * 在执行原业务逻辑之前,给每个方法添加上日志
     */
    @Override
    public void add() {
        log("add");
        userService.add();
    }

    @Override
    public void delete() {
        log("delete");
        userService.delete();
    }

    @Override
    public void modify() {
        log("modify");
        userService.modify();
    }

    @Override
    public void search() {
        log("search");
        userService.search();
    }

    private void log(String msg){
        System.out.println("log日志:调用了"+msg+"方法");
    }
}
//4. ------------ 测试 -----------
public static void main(String[] args) {
    //具体业务的实现类
    UserService userService = new UserService();
    //将业务实现类,注入到代理类
    UserServiceProxy userServiceProxy = new UserServiceProxy();
    userServiceProxy.setUserService(userService);
    /*
    通过代理类,调用业务处理方法:
        在不修改业务处理类的情况下,添加日志
    */
    userServiceProxy.add();
}

动态代理模式

  • 动态代理与静态代理,角色组成一样(抽象角色、真实角色、代理角色)

  • 动态代理的代理类是动态生成的,不是我们写好的

  • 动态代理分为两大类:基于接口的动态代理,基于类的动态代理

    • 基于接口: JDK动态代理
    • 基于类: cglib
    • java字节码实现:javasist

动态代理两个重要的类:

  • Proxy:代理(生成动态代理实例)
  • InvocationHandler:调用处理程序(调用处理程序,并返回结果)

jdk18官方文档地址:JDK18API

InvocationHandler

java.lang.reflect Interface InvocationHandler:是由代理实例的 调用处理程序 实现的接口。每个代理实例,都有一个关联的调用处理程序。在代理实例上调用方法时,方法调用将被 编码并分派 到其调用处理程序的invoke方法

只有一个方法: Object invoke(Object proxy, Method method , Object[] args ) 作用:处理代理实例上的方法调用,并返回结果 image-20240605170903955

image-20240605170911054

Proxy类的方法,只有以下四个:

image-20240605170921066

动态代理用到的方法:

image-20240605170929591

//1.------------- 租房接口。代理对象与被代理对象,都实现这个接口 ------------
public interface Rent {
    void rent();
}
//2.------------- 房东,实现租房接口 ------------- 
public class Host implements Rent {
    @Override
    public void rent() {
        System.out.println("房东出租房子");
    }
}
/**
 * MyInvocationHandler:代理实例的调用处理程序,继承了InvocationHandler接口
 * 用这个类,自动生成代理类(上面的UserServiceProxy 也是代理类)
 */
public class MyInvocationHandler implements InvocationHandler {

    //1.被代理的接口:
    private Rent rent;
    public void setRent(Rent rent) {
        this.rent = rent;
    }
    //2.生成代理类
    public Object getProxy(){
        /**
         * ClassLoader loader      //定义 代理类 的类加载器
         * Class<?>[] interfaces    //代理类 实现的接口列表
         * InvocationHandler h      //将方法调用分派到的  调用处理程序
         */
        return Proxy.newProxyInstance(rent.getClass().getClassLoader(),rent.getClass().getInterfaces(),this);
    }

    //3.处理代理程序,并返回结果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        seeHouse();
        //动态代理的本质,就是利用反射机制实现
        Object result = method.invoke(rent, args);
        charge();
        return result;
    }
    private void seeHouse(){
        System.out.println("中介带你看房子");
    }
    private void charge(){
        System.out.println("收取中介费");
    }
}

// ---------------------- 测试 ---------------------------
public static void main(String[] args) {
    //1.真实角色
    Host host = new Host(); //房东
    //2.调用处理程序
    MyInvocationHandler mih = new MyInvocationHandler();
    //3.通过调用处理程序,处理我们要调用的【接口实现类对象】
    mih.setRent(host);
    //4.动态得到代理类,并强转为 Rent 接口类型:
    //      这里的rentProxy就是动态生成的,并没有定义这个类(不像之前的 UserServiceProxy )
    Rent rentProxy = (Rent) mih.getProxy();
    //5.1 调用代理实例的 rent() 方法,
    //5.2 被编码并转发到 调用处理程序(mih)的invoke方法上
    rentProxy.rent();
}
输出结果:
    中介带你看房子
    房东出租房子
    收取中介费

下面是动态代理的工具类,将被代理的接口,换成Object

/**
 * 将被代理的接口(上面的Rent),换成Object。可以代理所有的Object子类
 * 用这个类,自动生成代理类(上面的UserServiceProxy 也是代理类)
 */
public class DynamicInvocationHandler implements InvocationHandler {

    //1.被代理的接口:
    private Object target;
    public void setTarget(Object target) {
        this.target = target;
    }

    //2.生成代理类
    public Object getProxy(){
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }

    //3.处理代理程序,并返回结果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //动态代理的本质,就是利用反射机制实现
        printLog(method.getName());
        Object result = method.invoke(target, args);
        return result;
    }
    //日志方法
    private void printLog(String msg){
        System.out.println("执行了 "+msg+" 方法");
    }
}

// ---------------------- 测试 ---------------------------
public static void main(String[] args) {
    //万能代理类:
    //1.真实对象
    UserService userService = new UserService();
    //2.调用处理程序
    DynamicInvocationHandler dih = new DynamicInvocationHandler();
    //3.通过调用处理程序,处理我们要调用的【接口实现类对象】
    dih.setTarget(userService);
    //4.动态得到代理类,并强转类型
    //      只能转换成接口,不能强转成 实现类
    IUserService proxy = (IUserService) dih.getProxy();
    //5.通过代理类,执行方法
    proxy.add();
    proxy.delete();
}

输出结果:
    执行了 add 方法
    新增方法
    执行了 delete 方法
    删除方法

动态代理的好处:

  • 具有静态代理的所有好处
  • 一个动态代理类,代理的是一个接口。一般就是对应一类业务
  • 一个动态代理类,可以代理多个类,只要实现了同一个接口

image-20240605171012169

  • 平台作者:怀旧(联系作者)
  • QQ:444915368
  • 邮箱:444915368@qq.com
  • 电话:17623747368
  • 评论

    登录后才可以进行评论哦!

    回到顶部 留言