为什么要用代理模式?
-
中介隔离作用:
在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。 -
开闭原则,增加功能:
代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是同过调用委托类的相关方法,来提供特定的服务。真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类.
代理模式:
代理模式作为23种经典设计模式之一,其比较官方的定义为“为其他对象提供一种代理以控制对这个对象的访问”,简单点说就是,之前A类自己做一件事,在使用代理之后,A类不直接去做,而是由A类的代理类B来去做
1.静态代理
特点是代理对象是针对指定的目标做的, 所有代码都固定的, 由程序员提供. 代码实现非常简单, 不能复用.
静态代理代码如下:
/**
* 标准 - 房屋租赁接口
*/
public interface Rent {
/**
* 出租房屋的方法
*
* @param money
* @return
*/
Object rent(Object money);
}
/**
* 真实对象 - 房东
*/
public class Host implements Rent {
@Override
public Object rent(Object money) {
System.out.println("出租成功, 价格是: " + money);
return new Object();
}
}
/**
* 代理对象 - 中介人员
*/
public class Agent implements Rent {
private Rent host;
public Rent getHost() {
return host;
}
public void setHost(Rent host) {
this.host = host;
}
@Override
public Object rent(Object money) {
// 前置增强
System.out.println("带客户看房");
System.out.println("讲价");
System.out.println("签订合同");
// 调用业主的方法进行房屋的出租
Object key = host.rent(money);
// 后置增强
System.out.println("客户信息登记...");
System.out.println("客户维护...");
return key;
}
}
/**
* 客户 - 租客
*/
public class Customer {
public static void main(String[] args) {
// 找中介
Agent proxy = new Agent();
// 中介找业主
proxy.setHost(new Host());
// 租房
Object key = proxy.rent(2000);
}
}
2.动态代理
特点是代理对象不是固定的, 代码是由后台根据情况生成的. 代理功能更加通用. 代码实现复杂, 可以复用.
动态代理的实现方式有两种:
2.1 jdk动态代理
jdk中自带的动态代理实现方案. Proxy类和InvocationHandler接口.
- 生成的代理对象都是Proxy类的子类.
- 动态代理中额外增强的功能, 被称之为调用处理程序. 必须实现InvocationHandler接口.
- 必须提供接口.
- 生成的代理类和真实类是实现了相同接口的. 它俩不能互相转换, 否则会抛出ClassCastException.
代码如下:
/**
* 标准 - 接口
*/
public interface Rent {
/**
* 租房方法
*
* @param money
* @return
*/
Object rent(Object money);
}
/**
* 真实对象 - 业主
*/
public class Host implements Rent {
@Override
public Object rent(Object money) {
System.out.println("出租成功, 价格: " + money);
return new Object();
}
}
/**
* 中介公司 - 调用处理程序
*/
public class Agent implements InvocationHandler {
private Rent host;
public Rent getHost() {
return host;
}
public void setHost(Rent host) {
this.host = host;
}
/**
* 将来被自动调用
* 1. 最终需要调用业主的方法把房子租出去
* 2. 对这个功能要进行增强
* @param proxy 代理对象
* @param method 正在被调用的方法
* @param args 被调用方法的参数列表
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 前置增强
System.out.println("签合同...");
// 调用真实对象的方法
Object result = method.invoke(host, args);
// 后置增强
System.out.println("客户登记...");
return result;
}
/**
* 获取代理对象
*
* @return
*/
public Rent getProxy() {
return (Rent) Proxy.newProxyInstance(
Agent.class.getClassLoader(),
new Class<?>[]{Rent.class},
this);
}
}
public class Customer {
public static void main(String[] args) {
// 中介公司
Agent agent = new Agent();
// 中介公司找业主
agent.setHost(new Host());
// 从中介公司获取代理对象
Rent proxy = agent.getProxy();
System.out.println(proxy.getClass().getName());
System.out.println(proxy.getClass().getSuperclass().getName());
// 调用代理对象的租房方法
proxy.rent(3000);
}
}
2.2 cglib动态代理
- 需要额外导包, cglib相关的jar包: cglib.jar, asm.jar
- 不需要提供接口, 代理类是真实类的子类.
- 调用处理程序需要实现MethodInterceptor接口.
/**
* 真实对象 - 业主
*/
public class Host {
public Object rent(Object money) {
System.out.println("房屋出租, 价格: " + money);
return new Object();
}
}
/**
* 中介公司 - 调用处理程序
*/
public class Agent implements MethodInterceptor {
/**
* 方法拦截
* 1. 调用真实类中的方法
* 2. 需要对真实类中的方法进行增强
*
* @param obj 代理对象
* @param method 被调用的方法
* @param args 方法的参数列表
* @param methodProxy 被代理的方法
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
// 前置增强
System.out.println("[前置]");
// 调用真实类中的方法
Object result = methodProxy.invokeSuper(obj, args);
// 后置增强
System.out.println("[后置]");
return result;
}
public Host getProxy() {
// 创建增强子Enhancer
Enhancer enhancer = new Enhancer();
// 设置父类型
enhancer.setSuperclass(Host.class);
// 设置回调
enhancer.setCallback(this);
// 创建代理对象
return (Host) enhancer.create();
}
}
public class Customer {
public static void main(String[] args) {
// 找中介
Agent agent = new Agent();
// 获取代理对象
Host proxy = agent.getProxy();
System.out.println(proxy.getClass().getName());
System.out.println(proxy.getClass().getSuperclass().getName());
// 调用方法进行租房
proxy.rent(500);
}
}