设计模式(五)之结构型模式-代理模式

一、代理模式

为什么要学习代理模式?

  • 因为这就是SpringAOP的底层!【SpringAOP 和SpringMVC】

分类:

  • 静态代理
  • 动态代理

image-20210628225450082

1、静态代理

角色分析:

  • 抽象角色:一般会使用接口或者抽象类来解决—租房
  • 真实角色:被代理的角色—房东
  • 代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作。—中介
  • 客户:访问代理对象的人—租客

1.1 房东直租

1.1.1 Rent接口

抽象角色:租房接口

1
2
3
4
//租房
public interface Rent {
public void rent();
}

1.1.2 Landlord类

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

1.1.3 Client类

1
2
3
4
5
6
7
//租客
public class Client {
public static void main(String[] args) {
Landlord landlord = new Landlord();
landlord.rent();
}
}

结果:

1
房东要出租房子!!!

分析:

  • 实际情况,我们一般无法直接找到房东,需要通过中介,才能找到房东,因此上面需要修改。

1.2 通过中介租房

1.2.1 Rent接口

1
2
3
4
//租房
public interface Rent {
public void rent();
}

1.2.2 Landlord类

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

1.2.3 Proxy类

新增代理类

下列代码优先使用了组合,而非继承

1
private Landlord landlord;

因为代理中介,也需要租房,所以实现了Rent接口

最后,代理也添加了一些附属操作,比如看房、签合同、收中介费

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
//中介,代理
public class Proxy implements Rent{
private Landlord landlord;

public Proxy(){
}

public Proxy(Landlord landlord) {
this.landlord = landlord;
}

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

//看房
public void seeHouse(){
System.out.println("中介带你看房!");
}
//签合同
public void hetong(){
System.out.println("签租赁合同!");
}
//收中介费
public void fee(){
System.out.println("收中介费!");
}

}

1.2.4 Client类

1
2
3
4
5
6
7
8
9
10
11
//租客
public class Client {
public static void main(String[] args) {
//房东要出租房子
Landlord landlord = new Landlord();
//代理,中介帮房东租房子,但是呢?代理角色一般会有一些附属操作
Proxy proxy = new Proxy(landlord);
//租客不用面对房东,直接找中介租房即可
proxy.rent();
}
}

结果:

1
房东要出租房子!!!

代码步骤

  1. 接口
  2. 真实角色
  3. 代理角色
  4. 客户端访问代理角色

2、加深理解

以常见的用户的增删改查为例

2.1 UserService接口

1
2
3
4
5
6
public interface UserService {
public void add();
public void delete();
public void update();
public void query();
}

2.2 UserServiceImpl类

实现UserService接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//真实对象
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("增加了一个用户!");
}

@Override
public void delete() {
System.out.println("删除了一个用户!");
}

@Override
public void update() {
System.out.println("修改了一个用户!");
}

@Override
public void query() {
System.out.println("查询了一个用户!");
}
}

2.3 Client类

客户端调用类

1
2
3
4
5
6
7
public class Client {
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();
userService.add();
}
}

结果:

1
增加了一个用户!

分析:

  • 若想在每一个方法上面,都增加一个日志,则上述UserServiceImpl会代码如下:

    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
    //真实对象
    public class UserServiceImpl implements UserService {
    @Override
    public void add() {
    System.out.println("使用了add方法");
    System.out.println("增加了一个用户!");
    }

    @Override
    public void delete() {
    System.out.println("使用了delete方法");
    System.out.println("删除了一个用户!");
    }

    @Override
    public void update() {
    System.out.println("使用了update方法");
    System.out.println("修改了一个用户!");
    }

    @Override
    public void query() {
    System.out.println("使用了query方法");
    System.out.println("查询了一个用户!");
    }
    }

    结果:

    1
    2
    使用了add方法
    增加了一个用户!

    若有多个实现类,则需要在每一个实现类中都要添加log日志,特别的繁琐,且违背了开闭原则,对修改关闭

    因此,可以通过添加代理类来实现该功能,且遵循开闭原则。

使用代理类

2.4.1 UserService接口

1
2
3
4
5
6
public interface UserService {
public void add();
public void delete();
public void update();
public void query();
}

2.4.2 UserServiceImpl类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//真实对象
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("增加了一个用户!");
}

@Override
public void delete() {
System.out.println("删除了一个用户!");
}

@Override
public void update() {
System.out.println("修改了一个用户!");
}

@Override
public void query() {
System.out.println("查询了一个用户!");
}
}

2.4.3 UserServiceProxy类

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
public class UserServiceProxy implements UserService {

private UserServiceImpl userService;

//通过set方式实现注入
public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}

@Override
public void add() {
log("add");
userService.add();
}

@Override
public void delete() {
log("delete");
userService.delete();
}

@Override
public void update() {
log("update");
userService.update();
}

@Override
public void query() {
log("query");
userService.query();
}

//日志方法
public void log(String msg){
System.out.println("【Debug】使用了"+msg+"方法!");
}
}

2.4.4 Client类

1
2
3
4
5
6
7
8
9
public class Client {
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();

UserServiceProxy proxy = new UserServiceProxy();
proxy.setUserService(userService);
proxy.add();
}
}

结果:

1
2
【Debug】使用了add方法!
增加了一个用户!

分析:

​ 通过添加代理类的方式,使用组合,通过spring的bean注入,将想要添加的日志信息在代理类中进行编写,原先的Impl实现类不需要做任何的修改,符合开闭原则。

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

缺点:

  • 一个真实角色就会产生一个代理角色;代码量会翻倍,开发效率会变低~

代理图解

image-20210630232703981

2、动态代理

  • 动态代理和静态代理角色一样
  • 动态代理的代理类是动态生成的,不是我们直接写好的
  • 动态代理分为两大类:1)基于接口的动态代理;2)基于类的动态代理
    • 基于接口—JDK动态代理
    • 基于类:cglib
    • java字节码实现:基于Javassist,应用于JBoss应用服务器,

需要了解两个类:Proxy:代理、InvocationHandler:调用处理程序

2.1 动态代理所需类的介绍

2.1.1 InvocationHandler

结合JDK1.8文档来查看

image-20210702235912599

2.1.2 Proxy类

image-20210705232047719

2.2 基本动态代理实例讲解

2.2.1 Rent接口

1
2
3
4
//租房
public interface Rent {
public void rent();
}

2.2.2 Landlord类

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

2.2.3 ProxyInvocationHandler类

分析:

​ (1)创建一个类,实现InvocationHandler接口

​ (2)从JDK1.8的文档中,可以看出,InvocationHandler接口只有一个方法

image-20210705224903331

​ 该方法的主要作用是处理代理实例,并返回结果。

1
2
3
4
5
//处理代理实例,并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}

​ (3)使用Proxy类,来返回指定接口的代理类的实例

image-20210705225112917

image-20210705225348482

​ 参照上面的简单例子,可以写出下面的代码。

1
2
3
4
5
6
7
8
9
10
11
12
 //被代理的接口
private Rent rent;

//后续需要在客户端进行赋值
public void setRent(Rent rent) {
this.rent = rent;
}

//生成得到代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this);
}

​ (4)利用反射,来实现动态代理

1
2
3
4
5
6
7
//处理代理实例,并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//动态代理的本质,就是试用反射机制实现!
Object result = method.invoke(rent,args);
return result;
}
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
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 {
//动态代理的本质,就是试用反射机制实现!
Object result = method.invoke(rent,args);
return result;
}
}

2.2.4 Client类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Client {
public static void main(String[] args) {
//真实角色
Landlord landlord = new Landlord();
//代理角色:现在还没有
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//InvocationHandler是由代理实例的调用处理程序实现的接口 。
//每个代理实例都有一个关联的调用处理程序。 当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的invoke方法。
//通过调用程序处理角色,来处理我们要调用的接口对象
pih.setRent(landlord);

Rent proxy = (Rent)pih.getProxy();//这里的proxy就是动态生成的,我们并没有写~
proxy.rent();
}
}

结果:

1
房东要出租房子!!!

2.2.5 修改ProxyInvocationHandler类

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
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 {
seeHouse();
//动态代理的本质,就是试用反射机制实现!
Object result = method.invoke(rent,args);
fare();
return result;
}

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

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

结果(客户端不变):

1
2
3
中介带看房子!
房东要出租房子!!!
收中介费!

2.3 通用代理实例讲解

以之前某一个设计模式介绍的增删改查为例

2.3.1 ProxyInvocationHandler类

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
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

//等会我们会用这个类,自动生成代理类
public class ProxyInvocationHandler 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 {
//动态代理的本质,就是试用反射机制实现!
Object result = method.invoke(target,args);
return result;
}
}

2.3.2 Client类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import com.ldg.GoF23.proxy.demo02.UserService;
import com.ldg.GoF23.proxy.demo02.UserServiceImpl;

public class Client {
public static void main(String[] args) {
//真实角色
UserServiceImpl userService = new UserServiceImpl();

//代理角色:不存在
ProxyInvocationHandler pih = new ProxyInvocationHandler();

pih.setTarget(userService);//设置要代理的对象
UserService proxy = (UserService) pih.getProxy();//动态生成代理类

proxy.add();

}
}

结果:

1
增加了一个用户!

2.3.3 增加日志—ProxyInvocationHandler类

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
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

//等会我们会用这个类,自动生成代理类
public class ProxyInvocationHandler 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 {
log(method.getName());
//动态代理的本质,就是试用反射机制实现!
Object result = method.invoke(target,args);
return result;
}

public void log(String msg){
System.out.println("执行了"+msg+"方法!");
}
}

2.3.4 增加日志类的Client

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import com.ldg.GoF23.proxy.demo02.UserService;
import com.ldg.GoF23.proxy.demo02.UserServiceImpl;

public class Client {
public static void main(String[] args) {
//真实角色
UserServiceImpl userService = new UserServiceImpl();

//代理角色:不存在
ProxyInvocationHandler pih = new ProxyInvocationHandler();

pih.setTarget(userService);//设置要代理的对象
UserService proxy = (UserService) pih.getProxy();//动态生成代理类

proxy.add();
proxy.query();

}
}

结果:

1
2
3
4
执行了add方法!
增加了一个用户!
执行了query方法!
查询了一个用户!

动态代理的好处

  • 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
  • 公共业务就交给代理角色!实现了业务的分工!
  • 公共业务发生扩展的时候,方便集中管理!
  • 一个动态代理类代理的是一个接口,一般就是对应的一类业务!
  • 一个动态代理类可以代理多个类,只要实现了同一个接口即可。

Spring注入方式(待总结)

0%