为什么要学习代理模式?
因为代理模式是SpringAOP的底层,在Spring面试中,SpringAOP和SpringMVC是必问。
代理模式的好处:
- 可以不用改动原有业务代码(这是大忌,可能会导致原来可以运行的代码无法再运行了);
- 使得真实角色的操作更加纯粹,不用去关注一些公共的业务,公共业务放在代理里实现;
- 公共业务发生扩展的时候,方便集中管理。
什么是代理模式?
所谓的代理模式,就是在两个对象之间,加一层代理,来实现原有的功能,并且提供一些增值服务。
举个例子,租客找房东租房,中间加一层代理,那么租客直接找该代理完成租房的功能(该功能属于房东,现在 由代理帮忙完成)。此外,提供一些增值服务,例如,带看房,签合同,做保洁,收取费用等。
可以发现,原来是A接触B,现在A不接触B,通过接触代理,也可以完成原来的功能。此外,代理还能在不修改B的代码的条件下,提供额外的功能。
代理模式的分类
- 静态代理
- 动态代理
静态代理模式中的角色
- 接口 (例如租房这个接口)
- 真实角色 (例如房东)
- 代理角色 (列入中介)
客户端访问代理角色 (例如租客)
接口
public interface Rent {
public void rent();
}
真实角色
public class Host implements Rent{ public void rent() { System.out.println("房东要出租房子"); } }
代理角色 ```java package com.kuang.demo01;
public class Proxy implements Rent{
private Host host;
public Proxy() {
}
public Proxy(Host host) {
this.host = host;
}
public void rent() {
inspectHouse();
host.rent();
signContract();
chargeFee();
}
public void inspectHouse() {
System.out.println("中介带看房");
}
public void signContract() {
System.out.println("签署合同");
}
public void chargeFee() {
System.out.println("收中介费");
}
}
4. 访问代理角色
```java
package com.kuang.demo01;
public class Client {
public static void main(String[] args) {
Host host = new Host();
// 不直接通过房东租房子,而是通过中介来租
// 中间会添加一些附属操作。
Proxy proxy = new Proxy(host);
proxy.rent();
}
}
动态代理
- 动态代理和静态代理角色是一样的;
- 但是,动态代理的代理类是动态生成的,不是直接写好的。
- 动态代理分为两大类:基于接口的动态代理,基于类的动态代理。
- 基于接口 —- JDK动态代理
- 基于类 —- cglib
- java字节码实现:javassit
需要了解两个类:proxy和invocation
实现步骤:
- 实现InovacationHandler;
- 通过set方法设置要代理的对象;
- 重写invoke方法,当调用被代理对象里的方法时,会转而调用invoke方法;
- 通过getProxy方法来获取要得到的代理对象;
// 实现InvocationHandler
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);
}
// 重写invoke方法,当调用被代理对象的方法时,转而执行该invoke方法
@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 + "方法");
}
}
在使用的时候:
- 生成真实对象;
- 生成handler对象;
- 将真实对象设置到handler里去;
- 通过getProxy方法来生成并获取代理对象;
通过代理对象调用被代理对象的方法,实际是去调用invoke;
public class Client { public static void main(String[] args) { // 生成真实对象; Host host = new Host(); // 生成handler对象; ProxyInvocationHandler pih = new ProxyInvocationHandler(); // 将真实对象设置到handler里去; pih.setTarget(host); // 通过getProxy方法来生成并获取代理对象; Rent proxy = (Rent) pih.getProxy(); // 通过代理对象调用被代理对象的方法,实际是去调用invoke; proxy.rent(); } }
动态代理除了具有静态代理的优点以外,还能:
- 一个动态代理类可以代理多个类。