一、代理模式
为什么要学习代理模式?
- 因为这就是SpringAOP的底层!【SpringAOP 和SpringMVC】
分类:
- 静态代理
- 动态代理
1、静态代理
角色分析:
- 抽象角色:一般会使用接口或者抽象类来解决—租房
- 真实角色:被代理的角色—房东
- 代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作。—中介
- 客户:访问代理对象的人—租客
1.1 房东直租
1.1.1 Rent接口
抽象角色:租房接口
1 | //租房 |
1.1.2 Landlord类
1 | //房东 |
1.1.3 Client类
1 | //租客 |
结果:
1 房东要出租房子!!!分析:
- 实际情况,我们一般无法直接找到房东,需要通过中介,才能找到房东,因此上面需要修改。
1.2 通过中介租房
1.2.1 Rent接口
1 | //租房 |
1.2.2 Landlord类
1 | //房东 |
1.2.3 Proxy类
新增代理类
下列代码优先使用了组合,而非继承
1 private Landlord landlord;因为代理中介,也需要租房,所以实现了Rent接口
最后,代理也添加了一些附属操作,比如看房、签合同、收中介费
1 | //中介,代理 |
1.2.4 Client类
1 | //租客 |
结果:
1 房东要出租房子!!!
代码步骤
- 接口
- 真实角色
- 代理角色
- 客户端访问代理角色
2、加深理解
以常见的用户的增删改查为例
2.1 UserService接口
1 | public interface UserService { |
2.2 UserServiceImpl类
实现UserService接口
1 | //真实对象 |
2.3 Client类
客户端调用类
1 | public class Client { |
结果:
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 {
public void add() {
System.out.println("使用了add方法");
System.out.println("增加了一个用户!");
}
public void delete() {
System.out.println("使用了delete方法");
System.out.println("删除了一个用户!");
}
public void update() {
System.out.println("使用了update方法");
System.out.println("修改了一个用户!");
}
public void query() {
System.out.println("使用了query方法");
System.out.println("查询了一个用户!");
}
}结果:
1
2 使用了add方法
增加了一个用户!若有多个实现类,则需要在每一个实现类中都要添加log日志,特别的繁琐,且违背了开闭原则,对修改关闭
因此,可以通过添加代理类来实现该功能,且遵循开闭原则。
使用代理类
2.4.1 UserService接口
1 | public interface UserService { |
2.4.2 UserServiceImpl类
1 | //真实对象 |
2.4.3 UserServiceProxy类
1 | public class UserServiceProxy implements UserService { |
2.4.4 Client类
1 | public class Client { |
结果:
1
2 【Debug】使用了add方法!
增加了一个用户!分析:
通过添加代理类的方式,使用组合,通过spring的bean注入,将想要添加的日志信息在代理类中进行编写,原先的Impl实现类不需要做任何的修改,符合开闭原则。
- 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务。
- 公共业务就交给了代理角色!实现了业务的分工
- 公共业务发生扩展的时候,方便集中管理!
缺点:
- 一个真实角色就会产生一个代理角色;代码量会翻倍,开发效率会变低~
代理图解
2、动态代理
- 动态代理和静态代理角色一样
- 动态代理的代理类是动态生成的,不是我们直接写好的
- 动态代理分为两大类:1)基于接口的动态代理;2)基于类的动态代理
- 基于接口—JDK动态代理
- 基于类:cglib
- java字节码实现:基于Javassist,应用于JBoss应用服务器,
需要了解两个类:Proxy:代理、InvocationHandler:调用处理程序
2.1 动态代理所需类的介绍
2.1.1 InvocationHandler
结合JDK1.8文档来查看
2.1.2 Proxy类
2.2 基本动态代理实例讲解
2.2.1 Rent接口
1 | //租房 |
2.2.2 Landlord类
1 | //房东 |
2.2.3 ProxyInvocationHandler类
分析:
(1)创建一个类,实现InvocationHandler接口
(2)从JDK1.8的文档中,可以看出,InvocationHandler接口只有一个方法
该方法的主要作用是处理代理实例,并返回结果。
1
2
3
4
5 //处理代理实例,并返回结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
} (3)使用Proxy类,来返回指定接口的代理类的实例
参照上面的简单例子,可以写出下面的代码。
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 //处理代理实例,并返回结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//动态代理的本质,就是试用反射机制实现!
Object result = method.invoke(rent,args);
return result;
}
1 | import java.lang.reflect.InvocationHandler; |
2.2.4 Client类
1 | public class Client { |
结果:
1 房东要出租房子!!!
2.2.5 修改ProxyInvocationHandler类
1 | import java.lang.reflect.InvocationHandler; |
结果(客户端不变):
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);
}
//处理代理实例,并返回结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//动态代理的本质,就是试用反射机制实现!
Object result = method.invoke(target,args);
return result;
}
}
2.3.2 Client类
1 | import com.ldg.GoF23.proxy.demo02.UserService; |
结果:
1 增加了一个用户!
2.3.3 增加日志—ProxyInvocationHandler类
1 | import java.lang.reflect.InvocationHandler; |
2.3.4 增加日志类的Client
1 | import com.ldg.GoF23.proxy.demo02.UserService; |
结果:
1
2
3
4 执行了add方法!
增加了一个用户!
执行了query方法!
查询了一个用户!
动态代理的好处
- 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
- 公共业务就交给代理角色!实现了业务的分工!
- 公共业务发生扩展的时候,方便集中管理!
- 一个动态代理类代理的是一个接口,一般就是对应的一类业务!
- 一个动态代理类可以代理多个类,只要实现了同一个接口即可。