代理模式.md 5.9 KB
Newer Older
ツぃ☆ve芜情's avatar
ツぃ☆ve芜情 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
**代理模式**是一种结构型设计模式, 让你能够提供对象的替代品或其占位符。 代理控制着对于原对象的访问, 并允许在将请求提交给对象前后进行一些处理。

## 静态代理

- 抽象角色:一般会使用接口或者抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理着你是角色,代理真实角色后,我们一般会做一些复数操作
- 客户:访问代理对象的人

以租房举例:

![](img/静态代理.png)

【抽象角色】

```java
public interface Rent {
    public void rent();
}
```

【真实角色】

```
public class Host implements Rent{
    @Override
    public void rent() {
        System.out.println("房东要租房子了");
    }
}
```

【代理角色】

```java
public class Agent implements Rent{
    private Host host;

    public Agent(Host host) {
        this.host = host;
    }

    public void seeHouse(){
        System.out.println("中介带你看房");
    }

    public void fare(){
        System.out.println("收中介费");
    }

    @Override
    public void rent() {
        host.rent();
    }
}
```

【客户】

```java
public class Client {
    public static void main(String[] args) {
        Host host = new Host();
        Agent proxy = new Agent(host);
        proxy.seeHouse();
        proxy.rent();
        proxy.fare();
    }
}
```

输出结果:

```
中介带你看房
房东要租房子了
收中介费
```

**代理模式的优点:**

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

**缺点**

- 一个真实角色就会产生一个代理角色,代码量会翻倍

## 动态代理

- 动态代理和静态代理角色一样
- 动态代理的代理类是动态生成的,不是我们直接写好的
- 动态代理分两大类:**基于接口的动态代理****基于类的动态代理**
  - 基于接口:JDK 动态代理
  - 基于类:cglib
  - java 字节码实现:javasist

还是以租房为例:

### JDK 动态代理

需要了解两个类:`Proxy``InvocationHandler`

【代理角色】

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

// 这个类自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {

    // 被代理的接口
    private Rent rent;

    public void setRent(Rent rent) {
        this.rent = rent;
    }

    // 动态生成代理对象
    public Object getProxy() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);
    }

    @Override
    // 处理代理实例,并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理中介带你看房");
        Object result = method.invoke(rent, args);
        System.out.println("收中介费");
        return result;
    }
}
```

【客户】

```java
public class Client2 {
    public static void main(String[] args) {
        // 真实角色
        Host host = new Host();
        // 代理角色:现在还没有
        ProxyInvocationHandler pih = new ProxyInvocationHandler();

        // 通过调用程序处理角色来处理我们要调用的接口对象
        pih.setRent(host);

        // 生成代理类实例,即代理对象
        Rent proxy = (Rent) pih.getProxy();
        proxy.rent();
    }
}
```

**通用动态代理处理类**

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

public class ProxyHandler implements InvocationHandler {

    // 被动态代理的接口
    private Object target;

    public void setTarget(Object target) {
        this.target = target;
    }
	
    // 动态生成代理对象
    public Object getProxy() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    @Override
    // 处理代理实例,并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // before call method do something
        Object result = method.invoke(target, args);
        // after call method do something

        return result;
    }
}
```

### CGLib 动态代理

CGLib 采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。JDK 动态代理与 CGLib 动态代理均是实现 Spring AOP 的基础。

【jar包】

```xml
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>
```

【代理类】

```java
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CGLibDynamicProxy implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

        if (method.getName().equals("rent")) {
            System.out.println("代理中介带你看房");
            methodProxy.invokeSuper(o, args);
            System.out.println("收中介费");
        }

        return null;
    }

    public Object getInstance(){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Host.class); // 这里不需要搞个接口了,cglib支持基于类的动态代理
        enhancer.setCallback(this);
        return enhancer.create();
    }
}
```

【客户】

```java
public class Client3 {
    public static void main(String[] args) {
        CGLibDynamicProxy proxy = new CGLibDynamicProxy();
        Rent rent = (Rent) proxy.getInstance();
        rent.rent();
    }
}
```