代理模式:分为静态代理、动态代理
组成:
抽象角色:通过接口或抽象类声明真实角色实现的业务方法。
代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。
优点:
(1)、职责清晰
真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件完成事务,附带的结果就是编程简洁清晰。
(2)、代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了中介的作用和保护了目标对象的作用。
(3)、高扩展性
缺点: 1、由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。 2、实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
使用场景: 按职责来划分,通常有以下使用场景: 1、远程代理。 2、虚拟代理。 3、Copy-on-Write 代理。 4、保护(Protect or Access)代理。 5、Cache代理。 6、防火墙(Firewall)代理。 7、同步化(Synchronization)代理。 8、智能引用(Smart Reference)代理。
注意事项: 1、和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。 2、和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制。
静态代理
静态代理是由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
创建一个接口:抽象角色
public interface Role {// 抽象角色
// 例如:帮人讨债的公司,真实角色(讨债人)可以通过公司去帮自己讨债
// 而代理角色(打手)除要债外也可以做些多余的事,要债是必要目的
void getMoney();// 真实角色、代理角色共同
}
创建实现接口的实体类:真实角色和代理角色
public class RealRole implements Role {// 真实角色:
@Override
public void getMoney() {
System.out.println("把10000元钱要回来");
}
}
public class ProxyRole implements Role {// 代理角色
// 含有对真实角色的引用
RealRole realRole = new RealRole();
// 可以做其他的事情
public void before() {
System.out.println("知己知彼,百战百胜");
}
public void after() {
System.out.println("要点辛苦费");
}
@Override
public void getMoney() {
this.before();
realRole.getMoney();
this.after();
}
}
当被请求时:测试
public class Test {
public static void main(String[] args) {
Role prRole = new ProxyRole();
prRole.getMoney();
}
}
结果:
知己知彼,百战百胜
把10000元钱要回来
要点辛苦费
动态代理
动态代理是在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象。
与静态代理的区别:
静态代理通常只代理一个类,动态代理是代理一个接口下的多个实现类。
静态代理事先知道要代理的是什么,而动态代理不知道要代理什么东西,只有在运行时才知道。
动态代理的使用:
动态代理是实现 JDK 里的 InvocationHandler 接口的 invoke 方法,但注意的是代理的是接口,也就是你的业务类必须要实现接口,通过Proxy 里的 newProxyInstance 得到代理对象。
还有一种动态代理 CGLIB,代理的是类,不需要业务类继承接口,通过派生的子类来实现代理。通过在运行时,动态修改字节码达到修改类的目的。
AOP 编程就是基于动态代理实现的,比如著名的 Spring 框架、Hibernate 框架等等都是动态代理的使用例子。
创建抽象对象:只能是接口
可直接用上面的抽象对象与真实对象,我们只需重新创建代理角色,使代理角色可以代理任何真实角色。
implements InvocationHandler
InvocationHandler 是代理实例的调用处理程序 实现的接口。
Object invoke(Object proxy, Method method, Object[] args) 在代理实例上处理方法调用并返回结果。
参数:
proxy - 在其上调用方法的代理实例
method - 对应于在代理实例上调用的接口方法的 Method 实例。Method 对象的声明类将是在其中声明方法的接口,该接口可以是代理类赖以继承方法的代理接口的超接口。
args - 包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为 null。基本类型的参数被包装在适当基本包装器类(如 java.lang.Integer 或 java.lang.Boolean)的实例中。
类Proxy:提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
参数:
loader - 定义代理类的类加载器
interfaces - 代理类要实现的接口列表
h - 指派方法调用的调用处理程序
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class ProxyRole implements InvocationHandler {
// 用Object接受任何类型的真实角色
Object realObject;
public ProxyRole(Object object) {
realObject = object;
}
// 可以做些其他事
public void other() {
System.out.println("做些其他事...");
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
this.other();
Object object = method.invoke(realObject, args);// 调用真实对象的方法
this.other();
return object;
}
}
测试:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Test1 {
public static void main(String[] args) {
RealRole realRole = new RealRole();
// 初步代理角色
InvocationHandler h = new ProxyRole(realRole);
// 最终的代理角色
// loader - 定义代理类的类加载器
// interfaces - 代理类要实现的接口列表
// h - 指派方法调用的调用处理程序
Role role = (Role) Proxy.newProxyInstance(h.getClass().getClassLoader(), realRole.getClass().getInterfaces(),
h);
role.getMoney();
}
}
结果:
做些其他事...
把10000元钱要回来
做些其他事...