代理模式应用场景十分广泛,随便一个
框架
都会用到,因此学好代理模式对后续框架学习是最基本的要素!!今天我们就来讲讲代理模式
!
目录
首先,了解以下23种设计模式中
代理模式
的定位,它是
结构性模式
1、简介
Proxy pattern
1. 核心作用
-
通过代理,控制对对象的访问
可以详细的访问某个(某类)对象的实现,在调用这个方法前做前置处理,调用这个方法后做后置处理(AOP的微观实现)
-
代理模式是
SpringAOP
的核心实现机制!
2. 角色分析
抽象角色
- 定义代理角色和真实角色的
公共对外方法
,一般会使用接口或者抽象类来解决
真实角色
- 实现抽象角色,定义真实角色要实现的
业务
,供代理角色调用
代理角色
- 实现抽象角色,代理真实角色,通过真实角色的业务来实现抽象方法,一般会有附属的业务
客户
- 访问代理角色的人
2. 应用场景
安全代理:屏蔽对真实角色的直接访问。
远程代理:通过代理类处理远程方法调用(RMI)
延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象。
4. 分类
- 静态代理(静态定义代理类)
- 动态代理(动态生成代理类)
- JDK自带的动态代理
- javaassist字节码操作库实现
- CGLIB
- ASM(底层使用指令,可维护性较差)
2、静态代理
static proxy
1. 案例:房东租房
接下来以房东租房这件实例,来讲讲代理模式
房东
要租房
,如果房东
懒得管太多,这时候就需要一个中介
,来帮助房东
租房并打理一切事情,这时候租房者
就不需要直接和房东
打交道了,而是通过中介
间接和房东打交道,中介
就是中间者,代理了房东
,且可以在租房前后附加其他操作,比如:签合同,看房子等
这时候对象上述的四个角色就有四个对象:
抽象角色
:租房业务真实角色
:房东代理角色
:中介,可能还有带客户看房子等业务客户
:租房者
接下来,我们通过代码还原上述四个角色
-
抽象角色:表示
租房
这个业务,用接口实现//租房 public interface Rent { public void rent(); }
-
真实角色:代表
房东
,实现租房业务接口//房东 public class Host implements Rent{ public void rent() { System.out.println("房东要出租房子了"); } }
-
代理角色:
中介
,实现租房业务接口因为代理了
房东
,所以私有属性是房东对象,除了租房子业务外,可能还有看房、签合同、收中介费等业务public class Proxy implements Rent { private Host host; public Proxy() { } public Proxy(Host host) { this.host = host; } //代理租房子 public void rent() { seeHouse(); host.rent(); contract(); fare(); } //看房 public void seeHouse() { System.out.println("中介带你看房"); } //签合同 public void contract() { System.out.println("租赁合同"); } //收中介费 public void fare() { System.out.println("收中介费"); } }
-
客户:
租房者
,访问中介public class Client { public static void main(String[] args) { //房东租房子 Host host = new Host(); //代理,中介帮房东租房子,并且有一些附属操作 Proxy proxy = new Proxy(host); //不需要找房东,直接找中介租房即可 proxy.rent(); } }
2. 优劣分析
好处:
-
职责清晰
使
真实角色
更加的简单专一,不管具体的业务- 这里房东只用给中介费就行了,其他一切都交给中介来做,实现了业务分工
-
智能化
客户只需访问代理角色,减少了直接访问真实角色带来的问题
- 加入这里房东有很多房子,自己一个人可能管理不过来,这时候每个客户的体验可能会变差,会出现很多问题
-
高拓展性
业务发展拓展的时候,方便集中管理
缺点:
- 一个真实角色就会产生一个代理角色;代码量会翻倍,开发效率会变低
3. 加深理解
可能上述例子过于简单,不能直观的感受到代理模式的好处,我们再举个例子加深理解
首先创建一个业务实现接口
npublic interface UserService {
public void add();
public void delete();
public void update();
public void query();
}
然后再来一个业务实现类
public class UserServiceImpl implements UserService {
public void add() {
System.out.println("增加一个用户");
}
public void delete() {
System.out.println("删除一个用户");
}
public void update() {
System.out.println("修改了一个用户");
}
public void query() {
System.out.println("查询了一个用户");
}
}
再来个测试类
public class Client {
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();
userService.add();
userService.delete();
userService.update();
userService.query();
}
}
如果此时,我们需要增加一个日志业务,需要打印每个方法的执行
- 普通修改在
业务实现类
中的每个方法中都要修改
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("查询了一个用户");
}
}
业务很多的情况下,修改量十分大,这时候用代理模式
就能很好的解决我们的问题
我们新建一个业务代理类
,在业务实现类里只需增加一个方法就可以实现上述新增业务
public class UserServiceProxy implements UserService {
private UserServiceImpl userService;
public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}
public void add() {
log("使用了add方法");
userService.add();
}
public void delete() {
log("使用了delete方法");
userService.delete();
}
public void update() {
log("使用了update方法");
userService.update();
}
public void query() {
log("使用了query方法");
userService.query();
}
public void log(String msg) {
System.out.println("使用了" + msg + "方法");
}
}
然后修改测试类,进行测试
package demo2;
public class Client {
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();
UserServiceProxy proxy = new UserServiceProxy();
proxy.setUserService(userService);
proxy.add();
proxy.delete();
proxy.update();
proxy.query();
}
}
由此可见:
- 当业务需要拓展的时候,代理模式充分的体现了其
高拓展性
- 业务很多的时候,代理模式也方便管理
3、动态代理
dynamic proxy
1. 简介
- 动态代理和静态代理角色一样
- 动态代理的代理类是
动态生成
的,不是我们直接写好的 - 分类
- JDK自带的动态代理
- javaassist字节码操作库实现
- CGLIB
- ASM(底层使用指令,可维护性较差)
接下来我们讲述
JDK自带的动态代理
需要了解:
Proxy
(代理)、InvocationHandler
(调用处理程序)
2. Proxy 类
代理
- 都是静态方法,直接通过类名调用
- 作用:动态生成代理类和对象
所有方法:
创建动态类的方法:【重要】
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
每次通过Proxy生成的代理类对象都要指定对应的处理器对象,就是第三个参数
三个参数:
- 类加载器,来定义代理类
- 代理类实现的接口列表
- 处理器接口对象
3. InvocationHandler 接口
处理器接口
- 通过invoke方法实现对真实角色的访问(就是调用真实角色的方法)
唯一的一个方法:
三个参数:
- proxy:代理类
- method:正在调用的方法
- args:方法中的参数,默认即可
4. 代码示例
这里还是以上述租房子为实例,我们用动态代理的方式实现
Rent接口
(抽象角色) 和 Host类
(真实角色)不变
-
定义一个处理器接口实现类,继承
InvocationHandler
其中的invoke方法实现对真实角色方法的调用,因此该类中有真实角色私有属性,为传参使用package demo3; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ProxyInvocationHandler implements InvocationHandler { //定义真实角色 private Rent host; //真实角色set方法 public void setHost(Rent host) { this.host = host; } /** 生成代理类方法 1. 类加载器,为当前类即可 2. 代理类实现的接口 3. 处理器接口对象 **/ public Object getProxy() { return Proxy.newProxyInstance(this.getClass().getClassLoader(), host.getClass().getInterfaces(), this); } //处理代理实例,并返回结果 //方法在此调用 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //调用真实角色方法 Object result = method.invoke(host, args); //附加方法 seeHouse(); contract(); fare(); return result; } //看房 public void seeHouse() { System.out.println("中介带你看房"); } //签合同 public void contract() { System.out.println("租赁合同"); } //收中介费 public void fare() { System.out.println("收中介费"); } }
-
测试类
package demo3; public class Client { public static void main(String[] args) { //真实角色:房东 Host host = new Host(); //处理器接口对象 ProxyInvocationHandler handler = new ProxyInvocationHandler(); //设置要代理的真实角色 handler.setHost(host); //动态生成代理类 Rent proxy = (Rent) handler.getProxy(); //调用方法 proxy.rent(); } }
-
结果
好处
- 一个动态代理类代理的是一个接口,一般就是对应的一类业务
- 一个动态代理类可以代理多个类,只要是实现了同一个接口即可
其他好处同静态代理