23种设计模式(4)-代理模式

介绍

阅读菜鸟教程-代理模式后,得到如下信息:

意图:为其他对象提供一种代理以控制这个对象的访问

主要解决:在某些访问对象时带来的问题,比如:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。

何时使用:想在访问一个类时做一些控制

如何解决:增加中间层

关键代码:实现与被代理类组合

应用实例:

  • windows里面的快捷方式
  • 买火车票不一定要去火车站买,也可以去代售点
  • 一张支票或银行存单是账户中资金的代理
  • Spring的AOP

静态代理

角色分析:

  • 抽象角色:一般使用接口或者抽象类来实现
  • 真实角色:被代理的角色
  • 代理角色:代理真实角色;代理真实角色后,一般会做些附属的操作
  • 客户:使用代理角色来进行一些操作

image-20211103201403438

代码实现

Rent.java:抽象角色

1
2
3
public interface Rent {
void rent();
}

Host.java:被代理的真实角色

1
2
3
4
5
6
public class Host implements Rent {
@Override
public void rent() {
System.out.println("我要出租房子!");
}
}

Proxy:代理角色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Proxy implements Rent {

private Host host;

public void setHost(Host host) {
this.host = host;
}

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

//后面还可以添加其他的附属操作(方法)(比如一些公共操作,这些操作完全可以交由非被代理类的角色去完成)
}

Client:客户(真实角色,也用于测试)

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Client {
public static void main(String[] args) {
Host host = new Host();

//new一个代理类
Proxy proxy = new Proxy();
//配置好代理类
proxy.setHost(host);

//使用代理类进行出租
proxy.rent();
}
}

通过代码,可以清晰看到:Host的rent()动作由Proxy代理角色执行了

优、缺点

优点:

  • 可以使得我们的真实角色更加纯粹,不再关注一些公共的事情
  • 公共的业务可以交由代理角色来完成(即在代理类中添加其他的方法)
  • 公共业务发生扩展时,变得更加集中和方便

缺点:

  • 由于一个被代理类,就对应着一个代理类,那么类多了,代理类就多了,工作量变大了,开发效率会降低(因此出现了动态代理)

动态代理

image-20211103201450376

区别:动态代理的代理类是动态生成的;静态代理的代理类是我们写好的

本质:利用反射机制在运行时创建代理类

动态代理主要分为两类:基于接口的动态代理(JDK动态代理)、基于类的动态代理(cglib)

基于接口的动态代理

核心:java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。(注意是java.lang.reflect包下的)

Proxy类:提供了创建对象的静态方法,这些对象充当接口实例

  • Proxy provides static methods for creating objects that act like instances of interfaces but allow for customized method invocation
  • 例如:To create a proxy instance for some interface Foo:
  • 已经提供的静态方法:
    image-20211103201120648

InvocationHandler接口:

  • Each proxy instance has an associated invocation handler.
  • When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.
  • 唯一的方法
    image-20211103195109442

代码实现

步骤:

  • 写好接口
  • 写好被代理类
  • 创建代理模式(关键步骤:分两步:)
    • 第一步:生成代理类对象(Proxy已经提供了相应的静态方法)
    • 第二步:事务处理程序(继承InvocationHandler接口,并重写invoke方法)
    • 注意:上面两步骤可以都写在事务处理器的类中,也可以分开写(本文就全写在一个类中了,这样以后调用的时候比较方便)

ProxyInvocationHandler.java:生成代理类,创建相应的事务处理程序

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
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);
}

//②重写事务处理程序的invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
seeHouse();
//通过反射机制调用rent对象的method方法,方法的参数为args
Object result = method.invoke(rent, args);
fare();
return result;

}

//可以自己添加公共方法
//看房
public void seeHouse() {
System.out.println("带房客看房");
}

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

Client.java:客户(真实角色,也用于测试)

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Client {
public static void main(String[] args) {
//真实角色
Host host = new Host();
//代理实例的调用处理程序
ProxyInvocationHandler handler = new ProxyInvocationHandler();
//将真实角色放置进去!
handler.setRent(host);
//获得代理实例
Rent proxy = (Rent) handler.getProxy(); //这边需要强转一下,否则调用不了Rent的方法
proxy.rent();
}
}

image-20211103204551976

分步解释

①

1
2
3
4
5
6
7
8
9
10
//①生成代理类
public Object getProxy() {
/*
* 三个参数(依次):
* loader:类加载器
* interfaces:代理的接口
* InvocationHandler:事务处理程序
*/
return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);
}

②

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//②重写事务处理程序的invoke方法
/* 参数:
* proxy:调用该方法的代理实例,method:被代理的对象的方法,args:方法的参数
* 返回:
* Object方法返回值
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
seeHouse();
//通过反射机制调用rent对象的method方法,方法的参数为args
Object result = method.invoke(rent, args);
fare();
return result;
}

总结

动态代理,代理的是一个接口;

  • 所以可以代理所有实现这一接口的类;

静态代理,代理的是一个类;

  • 所以出现一个类实现了一个接口,如果想要使用代理类,就需要对应写一个代理类
Contents
  1. 1. 介绍
  2. 2. 静态代理
    1. 2.1. 代码实现
    2. 2.2. 优、缺点
  3. 3. 动态代理
    1. 3.1. 基于接口的动态代理
    2. 3.2. 代码实现
    3. 3.3. 分步解释
      1. 3.3.1. ①
      2. 3.3.2. ②
  4. 4. 总结
|