JAVA静态代理和动态代理

静态代理和动态代理

1. 概述

代理对象可以在调用者和目标对象之间起中介作用,代理对象可以对目标对象的功能进行改写或增强。在java代码中可以体现为不修改源代码而对源代码进行改写。以达到代理作用。

1.1 分类

  1. 静态代理
  2. 动态代理

1.2 四要素

JAVA静态代理和动态代理

  1. 调用者: 你。
  2. 代理对象: 联想电脑代理商/黄牛。
  3. 目标对象: 电脑工厂/12306。
  4. 抽象对象: 代理对象和目标对象都共有的接口,保证代理对象和真实对象都有相应的方法,如电脑代理商和电脑工厂都需要有卖电脑的功能。

2. 静态代理

由程序员创建代理类,再对其编译。在程序运行前,代理类的.class文件就已经存在了。

案例:卖电脑

  • 抽象对象:Providable接口
/**
* 提供商品的接口
*/
public interface Providable{
    // 卖电脑
    void sellComputer(double price);
    // 维修电脑
    void repairComputer(double price);
}
  • 真实对象:ComputerFactory类
public class ComputerFactory implements Providable {
    @Override
    public void sellComputer(double price) {
        System.out.println("电脑工厂卖出一台电脑,价格: " + price);
    }
    @Override
    public void repairComputer(double price) {
        System.out.println("电脑工厂修好一台电脑,价格: " + price);
    }
}
  • 静态代理对象:ComputerSeller类
public class ComputerSeller implements Providable {
    private Providable provider;
    
    ComputerSeller(Providable provider){
        this.provider = provider;
    }
    
    @Override
    public void sellComputer(double price) {
        double realPrice = price * 0.5;
        System.out.println("推销员获得一台电脑,进价: " + realPrice);
        System.out.println("推销员卖出一台电脑,价格: " + price);
    }
    
    @Override
    public void repairComputer(double price) {
        double realPrice = price * 2;
        System.out.println("推销员拿电脑给电脑工厂维修,收取顾客费用: " + realPrice);
        System.out.println("电脑工厂修好一台电脑,价格: " + price);
    }
}
  • 调用者:Customer类
public class Customer {
    public static void main(String[] args) {
        //创建真实对象
        Providable provider = new ComputerFactory();
        //创建代理对象:注入真实对象
        Providable proxy = new ComputerSeller(provider);
        //调用方法
        proxy.sellComputer(2000);
        proxy.repairComputer(1200);
    }
}

缺点

  1. 一个真实对象必须对应一个代理对象,如果大量使用会导致类的急剧膨胀。
  2. 如果抽象对象中方法很多,则代理对象也要编写大量的代码。

3. 动态代理

在运行时动态地创建代理对象。不必自己创建类对象,代码量较少,可以弥补静态代理对象的不足。

3.1 相关api

Proxy类中的方法

static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)
作用:动态生成代理对象
ClassLoader loader	与真实对象相同的类加载器
Class[] interfaces	代理类或真实类所有实现的接口
InvocationHandler h 调用代理对象的接口,需要重写接口中的方法,实现真实对象中每个方法的调用。
返回Object 也就是生成的代理对象
参数 说明
loader 与真实对象相同的类加载器
interfaces 代理类或真实类所有实现的接口。
h 调用代理对象的接口,需要重写接口中的方法,实现真实对象中每个方法的调用。
返回Object 也就是生成的代理对象

InvocationHandler接口的invoke方法

Object invoke(Object proxy, Method method, Object[] args)
作用:接口中这个方法会调用多次,真实对象中的每个代理方法都会调用一次
参数 说明
proxy 动态生成的代理对象,不要在方法中直接调用,不然会出现递归死循环的调用。
method 真实对象的方法。
args 代理对象调用方法时传递的参数。
返回Object 方法的返回值。

3.2 开发步骤

  1. 首先需要存在抽象对象,定义所有的功能
  2. 真实对象实现抽象对象所有的功能
  3. 通过Proxy类,创建代理对象,调用代理方法
  4. 在InvocationHandler的invoke对代理的方法有选择的修改或不修改真实对象原有的方法

3.3 改写卖电脑案例

  • 抽象对象:Providable接口
package com.zmysna.proxy;

public interface Providable{
    // 卖电脑
    void sellComputer(double price);
    // 维修电脑
    void repairComputer(double price);
}
  • 真实对象:ComputerFactory类
package com.zmysna.proxy;

public class ComputerFactory implements Providable {

    @Override
    public void sellComputer(double price) {
        System.out.println("电脑工厂卖出一台电脑,价格: " + price);
    }

    @Override
    public void repairComputer(double price) {
       System.out.println("电脑工厂修好一台电脑,价格: " + price);
    }
}
  • 调用者:Customer类 (调用者中动态创建对象)
package com.zmysna.proxy;

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

public class Customer {
    public static void main(String[] args) {
        //创建真实对象
        Providable provider = new ComputerFactory();
        //创建动态代理对象,对sellComputer方法进行重写。
        Providable proxy = (Providable) Proxy.newProxyInstance(provider.getClass().getClassLoader(), new Class[]{Providable.class}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //重写sellComputer方法
                if(Objects.equals(method.getName(),"sellComputer")){
                    double price = (double)args[0];
                    double realPrice = price * 0.5;
                    System.out.println("推销员获得一台电脑,进价: " + realPrice);
                    System.out.println("推销员卖出一台电脑,价格: " + price);
                    return null;
                }else return method.invoke(provider,args); //其他方法正常执行
             
            }
        });
        proxy.sellComputer(100);
        proxy.repairComputer(50);

    }
}

运行结果

推销员获得一台电脑,进价: 50.0
推销员卖出一台电脑,价格: 100.0
电脑工厂修好一台电脑,价格: 50.0