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. 总结
|