---
date: 2024-10-30 18:12
updated: 2024-12-03 21:46
share: "true"
link: "false"
---

# 关于代理

## 代理的概述

### 什么是动态代理

==使用 jdk 的[[./反射机制|反射机制]]，创建对象的能力， 创建的是代理类的对象==。

- 动态：在程序执行时，调用 jdk 提供的方法才能创建代理类的对象。
- jdk 动态代理，必须有接口，目标类必须实现接口， 没有接口时，需要使用 cglib 动态代理

### 动态代理能做什么

==可以在不改变原来目标方法功能的前提下， 可以在代理中增强自己的功能代码。==

​例如实际开发中，你所在的项目中，有一个功能是其他人（公司的其它部门，其它小组的人）写好的，你可以使用。你发现这个功能，现在还缺点，不能完全满足我项目的需要。 我需要在gn.print()执行后，需要自己在增加代码。用代理实现 gn.print（）调用时， 增加自己代码， 而不用去改原来的 GoNong文件。

```java
// GoNong.class 
GoNong gn = new GoNong(); 
gn.print();
```

注意，不知道源代码，不能使用重写，而且你要用写好的功能想要去扩展功能，就只能先把写好的方法执行一边，然后再代理中扩展，重写的话，原来写好的方法也没了

### 什么是代理

#### 生活中的代理

代理就是中介、代购、商家等。

​例如留学中介就是一个代理，我想上美国的一所大学，但是我没办法去这个大学实地考察，也没办法要这个大学的联系方式，并且这个大学拒绝个人去访问它。但是这个大学在国内把招生业务委派给了一个留学中介。这样我就可以去和留学中介交谈，留学中介可以与美国大学联系。留学中介会从我这里收取额外的中介费。

​在以上的案例中，学校是目标，留学中介是代理，我是客户。它们具有以下特点：

- 中介和代理他们要做的事情是一致的： 招生。
- 中介是学校代理， 学校是目标。
- 我—中介（学校介绍，办入学手续）----美国学校。
- 中介是代理，不能白干活，需要收取费用。
- 代理不让你访问到目标。

#### 为什么要找中介

- 中介是专业的，方便
- 我现在不能自己去找学校。 我没有能力访问学校。 或者美国学校不接收个人来访。
- 买东西都是商家卖， 商家是某个商品的代理， 你个人买东西， 肯定不会让你接触到厂家的。

### 开发中的代理模式（代理）

​代理模式是指，当一个对象（目标）无法直接使用时，可以在该客户端和目标类之间创建一个中介，这个中介就是代理。

​在实际开发中有这样情况，有一个A类，有一个C类，但是C类不允许A类直接访问，这样我们可以创建一个A类和C类之间的B类，C类允许B类访问，这样我们可以在A类中访问B类，然后B类在访问C类，这样就相当于在A类中间接访问到了C类。其中==A类是客户类，B类是代理类，C类是目标类==。另外，我可以在B类中添加一些内容，意味着功能增强。

#### 使用代理模式的作用

- 功能增强： 在你原有的功能上，增加了额外的功能。 新增加的功能，叫做功能增强。 (例如，留学中介要收取额外的费用)
- 控制访问： 代理类不让你访问目标，例如商家不让用户访问厂家。（就跟房屋中介肯定不会给房东电话给你，否则它们怎么赚中介费）

### 实现代理的方式

> 实现代理的方式有：静态代理，动态代理

# 静态代理

## 什么是静态代理

​ 静态代理：代理类是==手工创建的==，代理的**目标类是固定的**。

## 静态代理的实现步骤

> 模拟一个用户购买u盘的行为，其中用户是客户端类，商家是代理类，厂家是目标类，商家和厂家都是卖U盘的，应该把卖U盘这个动作抽象为一个接口。

1.创建一个接口，定义卖u盘的方法， 表示你的厂家和商家做的事情。

```java
// 表示功能的，厂家，商家都要完成的功能 
public interface UsbSell { 
    //定义方法 参数 amount:表示一次购买的数量，暂时不用 
    //返回值表示一个u盘的价格。 
    float sell(int amount); 
    
    //可以多个其它的方法 
    //void print(); 
}
```

2.创建厂家类，实现1步骤的接口

```java
//目标类：金士顿厂家, 不接受用户的单独购买。 
public class UsbKingFactory implements UsbSell { 
	@Override 
	public float sell(int amount) { 
		System.out.println("目标类中的方法调用 , UsbKingFactory 中的sell "); 
		//一个128G的u盘是 85元。 
		//后期根据amount ，可以实现不同的价格，例如10000个，单击是80， 50000个75 
		return 85.0f; 
	} 
}
```

3.创建商家，就是代理类，也需要实现1步骤中的接口。

```c
// taobao是一个商家，代理金士顿u盘的销售。 
public class TaoBao implements UsbSell { 
	// 声明 商家代理的厂家具体是谁 
	// private修饰为了控制访问，不让客户知道厂家是谁 
	private UsbKingFactory factory = new UsbKingFactory(); 
	
	@Override 
	// 实现销售u盘功能 
	public float sell(int amount) { 
		// 向厂家发送订单，告诉厂家，我买了u盘，厂家发货 
		float price = factory.sell(amount); //厂家的价格。 
		// 商家 需要加价， 也就是代理要增加价格。 
		price = price + 25; //增强功能，代理类在完成目标类方法调用后，增强了功能。 
		// 在目标类的方法调用后，你做的其它功能，都是增强的意思。 
		System.out.println("淘宝商家，给你返一个优惠券，或者红包"); 
		
		// 增加的价格 
		return price; 
	} 
} 

public class WeiShang implements UsbSell { 
	//代理的是 金士顿，定义目标厂家类 
	private UsbKingFactory factory = new UsbKingFactory(); 
	
	@Override 
	public float sell(int amount) { 
		//调用目标方法 
		float price = factory.sell(amount); 
		//只增加1元 
		price = price + 1; 
		return price; 
	} 
}
```

4.创建客户端类，调用商家的方法买一个u盘。

```java
public class Customer { 
	public static void main(String[] args) { 
		// 通过淘宝卖 
		// 下载了淘宝的app 
		/* 
		TaoBao taoBao = new TaoBao(); 
		// 通过代理类，实现购买u盘，增加了优惠券，红包等等 
		float price = taoBao.sell(1); 
		System.out.println("淘宝的价格：" + price); 
		*/ 
		
		// 取得微商的联系方式 
		WeiShang weiShang = new WeiShang(); 
		// 通过微商代理，实现购买u盘，增加了优惠券，红包等等
		 float price = weiShang.sell(1); 
		 System.out.println("通过微商购买的价格："+ price); 
	} 
}
```

分析问题：

- 以上实例，若只有一个目标类，如金士顿厂家，商家只需要和这个厂家联系就行。但是如果又有一个闪迪厂家，那么得重写一套代理类，因为一个代理类中只能指定一个厂家，如以上的淘宝类，制定了厂家是金士顿，就不能在指定闪迪厂家，只能重新新建一个代理类，在这个代理类中指定厂家是闪迪，微商也是一样，要来一个专门代理闪迪的微商。如果有100个厂家，那么就要写200个代理类。
- 如果UsbSell接口多了个退货功能，那么所有的目标类和代理类都要修改。

## 静态代理的优缺点

### 优点

- 实现简单
- 容易理解

### 缺点

（当你的项目中，目标类和代理类很多时候，有以下的缺点：）

- 当目标类增加了，代理类可能也需要成倍的增加。 代理类数量过多。
- 当你的接口中功能增加了，或者修改了，会影响众多的实现类，厂家类，代理都需要修改。影响比较多。

简而言之，静态代理容易理解，代码好写，但是各个类之间**耦合度太高**，一旦扩展就有问题

# 动态代理

## 什么是动态代理

​ 动态代理就是在程序执行过程（动态）中，使用jdk的[[./反射机制|反射机制]]，创建代理类对象， 并动态的指定要代理目标类。换句话说： ==动态代理是一种创建java对象的能力==，让你不用自己写代理类的java程序，然后new对象。而是直接用jdk创建代理类对象。这样我不关心代理类是谁，我只知道它能增强功能，并且通过它我可以找到目标类。（类似暗下交易）

## 动态代理的优点

- 动态代理中目标类即使很多，但是代理类数量可以很少，当你修改了接口中的方法时，不会影响代理类（压根就不用写代理类了，这个代理类是jdk创建的，只是起到一个中介的功能）。符合OCP原则
- 不用创建代理类文件，代理的目标类是灵活的，可以随意给不同目标创建代理

## 动态代理的两种实现方式

1. jdk动态代理（理解）： 使用java反射包中的类和接口实现动态代理的功能。
   - 反射包 java.lang.reflect , 里面有三个类 ： InvocationHandler , Method, Proxy.
2. cglib动态代理（了解）: cglib是第三方的工具库， 创建代理对象。
   - cglib的原理是继承， cglib通过继承目标类，创建它的子类，在子类中重写父类中同名的方法， 实现功能的修改。
   - 因为cglib是继承，重写方法，所以要求目标类不能是final的， 方法也不能是final的。
   - cglib的要求目标类比较宽松， 只要能继承就可以了。cglib在很多的框架中使用，比如 mybatis ，spring框架中都有使用。

注意：**jdk动态代理必须有接口，对于无接口类，必须使用cglib来为它创建动态代理**

## 动态代理开发中实例

```java
// 目标完成的功能
public interface HelloService {

    //打印报告， 报表
    int print(String name);
}


// 目标类
public class GoNeng implements HelloService {
    @Override
    public int print(String name) {
        System.out.println("其它人写好的个功能方法");
        return 2;
    }
}


// 代理完成的功能
public class MyInvocationHandler implements InvocationHandler {

    private Object target = null;

    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        //我在项目中记录数据库，
        //调用目标方法， 执行print()得到 2
        Object res = method.invoke(target,args); //2
        
        // 代理进行“功能增强”
        //需要乘以 2的结果  
        if( res != null){
            Integer num = (Integer)res;
            res = num * 2;
        }

        return res;
    }
}


// 测试类
public class MyApp {

    public static void main(String[] args) {
       // GoNeng gn = new GoNeng();
        //int num = gn.print("销售");
       // System.out.println("num="+num);
		
        // 创建目标类
        GoNeng goNeng = new GoNeng();
        // 代理完成的功能
        InvocationHandler handler = new MyInvocationHandler(goNeng);
		// 这里表示目标类必须实现一个接口，否则不能用jdk实现动态代理，只能用cglib实现动态代理
        System.out.println("goNeng.getClass().getInterfaces()="+goNeng.getClass().getInterfaces()[0].getName());
        HelloService proxy = (HelloService) Proxy.newProxyInstance( goNeng.getClass().getClassLoader(),
                goNeng.getClass().getInterfaces(),handler);

        // 代理proxy的print方法实际上是接口中的，因为再创建代理是，把目标类的构造器和实现的接口都给了代理，并且代理完成的功能也给了代理
        // 代理执行print方法就去handler的invoke方法，把print方法给了method，参数“市场”给了args
        int num = proxy.print("市场");
        System.out.println("我们期望的 num ==" + num);
		
        // 总之proxy，handler，目标类之间是相关联的。
    }
}
```

## 实现代理的步骤

- 定义业务接口和实现：习惯性地首先定义一个接口，然后提供一个或多个实现。
- 创建调用处理器：实现`InvocationHandler`接口的类，它定义了代理实例的调用处理程序。
- 生成代理实例：通过调用`Proxy.newProxyInstance()`，传入目标类的类加载器、接口数组和调用处理器来创建。

# 动态代理与静态代理的比较

两者的核心差异在于：静态代理的代理类是==编译期就确定==下来的，而动态代理的代理类是在==运行时动态创建==的。

#### 静态代理

**静态代理通常要求程序员手动编写代理类**。这就意味着，每增加一个代理目标就得增加一个代理类，工作量大，且不具备通用性。静态代理的优势在于可以直观地看到代理逻辑，也有利于错误的排查。

#### 动态代理

**动态代理不需要显示地编写代理类**，系统通过反射等机制在运行时创建代理对象，极大地提高了代码的灵活性和可复用性。动态代理的使用使得单一代理类可以代理多种类型的主体。
