Java设计模式——代理模式
前言
什么是代理模式?
定义:为其他对象提供一种代理以控制对这个对象的访问
定义总是晦涩难懂的,举个栗子:买房子时的中介就是一个代理,中介的这种模式就是代理模式。
为什么要使用代理模式?
既然都有真实业务类的存在,为什么要"多此一举"的再找一个代理类来代理这个真实业务类来完成业务操作?(这就相当于是在问为什么不直接自己买房而需要找中介买房呢?)
代理模式可以通过扩展代理类,进行一些功能的附加与增强。
代理模式分为静态代理与动态代理
一、静态代理
两个子类共同实现同一个接口,其中一个子类负责真实业务实现,另一个子类完成辅助真实业务主题的操作(两个子类实现一个接口,一个类干事情,另一个类辅助它干事情)
举个栗子
以通过中介买房子为例:
接口:买房子
真实业务类:你(买房子)
代理类:中介(帮买房子)
静态代理的分析:静态代理指预先已经确定了代理与被代理者的关系了(买房子前你就已经和中介确定了"中介关系"),映射到编程中,就是指代理类与被代理类(真实业务类)在编译阶段就确定了依赖关系。
定义一个代表买房子的接口
interface HouseBuyer{
void buyHouse();
}
定义一个普通购房者类(真实业务类),实现HouseBuyer
接口
class Customer implements HouseBuyer{
@Override
public void buyHouse() {
System.out.println("在银行付钱买房");
}
}
定义一个中介类(代理类),除了实现HouseBuyer
接口中已定义的方法外
class Agent implements HouseBuyer{
// 代理类中持有被代理类的对象
HouseBuyer houseBuyer;
// 代理类通过构造方法将被代理类的对象传入
public Agent(HouseBuyer houseBuyer) {
this.houseBuyer = houseBuyer;
}
public void beforeBuyHouse(){
System.out.println("买房子前带着购房者坐车去游览小区再坐车去银行");
}
// 代理类实现接口的方法==调用被代理类实现该接口的方法(真实业务)
// +代理类中定义的其他用于辅助真实业务操作的方法(辅助方法)
@Override
public void buyHouse() {
beforeBuyHouse();
this.houseBuyer.buyHouse();
afterBuyHouse();
}
public void afterBuyHouse(){
System.out.println("买房子后将购房者送至想去的位置");
}
}
定义一个中介工厂,用户返回中介类的对象
class ProxyFactory{
// 工厂方法一般都是静态的
public static HouseBuyer getProxy(){
return new Agent(new Customer());
}
}
在客户端使用代理对象来进行相关操作
public static void main(String[] args) {
HouseBuyer houseBuyer = ProxyFactory.getProxy();
houseBuyer.buyHouse();
}
打印结果如下:
小小的总结一下静态代理:通过代理进行相关功能的增强,代理类中引用被代理类的对象(通过构造方法),并且代理类在实现接口方法时
不直接进行操作,而是调用被代理类实现接口的方法(真实业务)+在代理类中定义的其他用于辅助真实业务类操作的方法(辅助方法)
。还是中介的这个例子,购房者通过中介的"代理"购买房子,中介为购房者提供购房前,购房后的服务(车接车送),增强了购房者购买房子的功能。
二、动态代理
动态代理的引入:动态代理仍然是代理,情况与静态代理完全一样,只是代理与被代理人的关系是动态确定的(买房的当天才直接找了一个中介),映射到编程中,就是指代理类与被代理类(真实业务类)在运行阶段确定了依赖关系。
为什么要使用动态代理(动态代理并没有在静态代理的基础上增强任何代理方面的任何功能)?
动态代理具有灵活性、强扩展性的优势,若使用静态代理,若还有一个购房者需要购房,就得再创建一个新的中介(代理类),而若使用动态代理,则只需要创建一个中介(代理类),将很多的购房者都交给这个中介来处理。
动态的创建代理类进行代理(思路与静态代理基本一样)
定义2(N)个普通购房者类(真实业务类),实现HouseBuyer
接口
// 购房者B
class CustomerB implements HouseBuyer{
@Override
public void buyHouse() {
System.out.println("大家好,我是喜欢唱、跳、Rap、篮球的B,我在银行付钱买房");
}
}
// 购房者C
class CustomerC implements HouseBuyer{
@Override
public void buyHouse() {
System.out.println("大家好,我是喜欢唱、跳、Rap、篮球的C,我在银行付钱买房");
}
}
定义一个中介类,此时该中介类不需要实现买房子的接口,而是实现InvocationHandler接口(中的invoke()方法)
class DynProxyAgent implements InvocationHandler{
// 代理类中持有被代理类的对象
Object houseBuyer;
// 代理类通过构造方法将被代理类的对象传入
public DynProxyAgent(Object houseBuyer) {
this.houseBuyer = houseBuyer;
}
public void beforeBuyHouse(){
System.out.println("买房子前带着购房者坐车去游览小区再坐车去银行");
}
public void afterBuyHouse(){
System.out.println("买房子后将购房者送至想去的位置");
}
// 无论调用被代理者中的哪个方法,都需要先进入代理类的invoke(),代理新增的逻辑就写在invoke()中
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
this.beforeBuyHouse();
Object ret = method.invoke(this.houseBuyer,args);
this.afterBuyHouse();
return ret;
}
}
改造一下静态代理工厂,使用Proxy.newProxyInstance()由系统动态的创建一个代理类。
public static Object getProxy(Object target){
// 先获取动态代理类实例(实现了InvocationHandler)
InvocationHandler handler = new DynProxyAgent(target);
// 返回由Proxy.newProxyInstance()创建的动态代理类
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),handler);
}
在客户端使用代理对象来进行相关操作
public static void main(String[] args) {
HouseBuyer houseBuyerB = (HouseBuyer) DynProxyFactory.getProxy(new CustomerB());
houseBuyerB.buyHouse();
System.out.println("=============================================");
HouseBuyer houseBuyerC = (HouseBuyer) DynProxyFactory.getProxy(new CustomerC());
houseBuyerC.buyHouse();
}
打印结果如下(注意不同购房者B和C的区别):
小小的总结一下动态代理:动态代理的本质和静态代理是一样的,都是在代理类中对实际业务类进行了功能上的扩展,只不过,动态代理产生的代理类是在运行时由系统产生,代替了静态代理一个一个创建代理类的方法,动态代理类实现InvocationHandler接口中的invoke()方法。
InvocationHandler接口的作用:当代理对象(Proxy.newProxyInstance产生的对象)的原本方法被调用时,不会执行原本的方法,而是会执行InvocationHandler中的invoke()方法
,代理对象代理的逻辑全部写在这个invoke()方法中即可。
动态代理的流程图
使用动态代理工厂生成动态代理类proxy->调用proxy中的方法->调用newProxyInstance的参数h中的invoke()方法->根据invoke()方法中的逻辑调用其他方法->完成动态代理。
①动态的生成一个代理对象——通过Proxy.newProxyInstance()
loader:被代理的类的类加载器
interfaces:被代理的类所实现的接口(可以有多个)
h:调用当前代理对象的方法时,不直接调用当前代理对象的方法,而是调用h(实现了InvocationHandler接口的"代理类")中的invoke()方法
创建好代理类proxy后该方法会调用h中的invoke()方法
②实现了InvocationHandler的"代理类"
proxy:代理后的实例对象
method:代理对象被调用方法
args:被调用时的参数
invoke()方法除了会调用代理类中为增加功能而实现的其他方法(辅助方法),还会通过反射(Method.invoke())调用被代理类中实现接口的方法(真实业务)。
1、JDK代理
JDK的动态代理方法是依赖于接口的,首先使用接口来定义好操作的规范,然后通过Proxy类产生的代理对象调用被代理对象的操作,而这个操作又被分发给InvocationHandler
接口的invoke()
方法具体执行。
上面的例子即为JDK代理,使用Proxy类
的newProxyInstance()
方法生成的代理对象houseBuyerB去调用了houseBuyerB.buyHouse()
;操作,此时,系统将此方法分发给invoke()
方法,其中houseBuyerB是系统帮我们动态生成的,同样实现了接口HouseBuyer
2、cgLib代理
JDK实现的动态代理只能针对实现了接口的类,而不能对没实现接口的类进行动态代理,因此引入cgLib(Code Generation Library),底层使用java字节码操作框架ASM实现。
首先要使用cgLib,需要引入cgLib的库(jar包)。点击后面这个链接可以直接下载的哦。cglib-nodep-3.2.6.jar
举个栗子说明cgLib的执行流程,由于动态代理主要使用JDK代理,这里就不对cgLib代理的细节与详情展开讨论了。
定义一个真实业务类
class Message{
public void send(){
System.out.println("send a message");
}
}
定义中间代理类,实现MethodInterceptor接口(中的intercept()方法)——大体流程与JDK代理的实现InvocationHandler类似。
class ClassProxy implements MethodInterceptor{
// 接收真实业务类
private Object target;
public ClassProxy(Object target) {
this.target = target;
}
private void beforeMethod(){
System.out.println("before send");
}
private void afterMethod(){
System.out.println("after send");
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
beforeMethod();
Object ret = method.invoke(this.target,objects);
afterMethod();
return ret;
}
}
与JDK类似,CgLib的工厂类也需要创建Enhancer对象,并对该对象进行一系列操作。
class CgLibFactory{
public static Object getCgLib(Object obj){
// 负责代理对象的代理处理类
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(obj.getClass());
// 代理对象,以上就动态配置好了类之间的代理关系
enhancer.setCallback(new ClassProxy(obj));
return enhancer.create();
}
}
在客户端使用代理对象来进行相关操作
public static void main(String[] args) {
Message msg = new Message();
Message tmp = (Message) CgLibFactory.getCgLib(msg);
tmp.send();
}