结构型模式——代理模式(Proxy Pattern)

代理模式

代理模式(Proxy Pattern)属于结构型模式。使用一个类代表另一个类的功能。

访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介,为某个对象提供一个代理对象,并且由代理对象控制对原对象的访问。

代码

简介

目的:
  • 中介隔离作用。为其他对象提供一种代理以间接的控制对这个对象的访问。
  • 开闭原则,增加功能。通过修改代理类给被代理类添加功能。
主要解决:
  • 在直接访问对象时带来的问题。
    • 被访问的对象在远程的机器上。
    • 对象创建开销很大
    • 某些操作需要安全控制
    • 需要进程外的访问
何时使用:
  • 在访问一个类时做一些控制。
如何解决:
  • 增加中间代理层。
UML

代理模式.PNG

角色
  • 抽象主题(Subject):通过接口或抽象类声明真实主题和代理对象实现的业务方法。
  • 真实主题(Real Subject):实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
  • 代理(Proxy):提供了与真实主题相同的接口,其内部含有对真实主题的引用实例,它可以访问、控制或扩展真实主题的功能。
优点:
  1. 职责清晰。
  2. 高扩展性。
  3. 降低系统的耦合度。
  4. 客户端可以针对抽象主题角色进行编程,增加和更换代理类无须修改源代码,符合开闭原则,系统具有较好的灵活性和可扩展性。
缺点:
  1. 因为增加了代理,所以导致处理速度会因次降低
  2. 有些代理模式的实现非常复杂。
使用场景:

按职责来划分,通常有以下使用场景: 1、远程代理。 2、虚拟代理。 3、Copy-on-Write 代理。 4、保护(Protect or Access)代理。 5、Cache代理。 6、防火墙(Firewall)代理。 7、同步化(Synchronization)代理。 8、智能引用(Smart Reference)代理。

注意事项:

1、和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。 2、和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制。

分类

静态代理

在编写代码环节,被代理类以及如何代理已经被确定。在不编写代码的情况下无法更改被代理的对象。

代码
Java
//代理类,其中存一个抽象主题的实例,也就是被代理类,实现了抽象主题接口
public class Proxy implements Subject{
    //1.可以替换为懒加载,在使用时加载,也可以配合单例等其他模式使用
    //2.可以在构造时作为参数传入
    Subject realSubject = new RealSubject();
    public Proxy() {
    }
    @Override
    public void Request() {
        //TODO 业务前处理
        realSubject.Request();
        //TODO 业务后处理
    }
	//Client使用
    public static void main(String[] args) {
        Proxy proxy = new Proxy();
        proxy.Request();
    }
}

//抽象主题接口
interface Subject{
    void Request();
}

//被代理类,实现了抽象主题接口
class RealSubject implements Subject{
    @Override
    public void Request() {
        //TODO 真实业务
    }
}
优缺点
优点:
  • 可以做到在符合开闭原则的情况下对目标对象进行功能扩展。
缺点:
  • 需要为每一个服务创建代理类,工作量大,且不易管理。
  • 接口一旦发生改变,代理类也需要相应修改。
强制代理

真实角色必须通过代理才能访问,并且真实角色的代理的产生,也必须由真实角色决定,与普通代理正好相反;

被代理对象自己指定了一个代理对象(被代理类中获取代理当前类的代理类),如果未指定则会抛出异常,且指定代理对象必须使用其提供的方法。

代码略

动态代理

动态代理是在程序运行时通过反射机制动态创建的。可以在使用时,通过指定的的方式代理对应的类。

代码
Java_JDK动态代理
//自定义的处理handler
public class DynamicProxyHandler implements InvocationHandler {
    /**
     * 代理类中的真实对象
     */
    private Object target;
    public DynamicProxyHandler(Object target) {
        this.target = target;
    }

//    /**    
//     * 简化使用
//     * 将类传入,处理成为该类的对象(可用此封装,或者用Factory封装)
//     * @param target 目标类
//     * @return 目标类的对象
//     */
//    public Object newProxyInstance(Object target) {
//        return Proxy.newProxyInstance(
//                target.getClass().getClassLoader(),
//                target.getClass().getInterfaces(),
//                this);
//    }
    /**
     *
     * @param proxy 被代理对象
     * @param method 要调用的方法
     * @param args 方法调用时所需要的参数
     * @return 对应方法的结果
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //TODO 业务前处理
        Object ret = method.invoke(target, args);
        //TODO 业务后处理
        return ret;
    }
    
    //Client
    public static void main(String[] args) {
        //类加载器
        ClassLoader classLoader = Subject.class.getClassLoader();
        //类的接口*
        Class<?>[] interfaces = Subject.class.getInterfaces();
        //自定义的处理Handler实例
        DynamicProxyHandler dynamicProxyHandler = new DynamicProxyHandler(realSubject);
		//通过Proxy的newProxyInstance(..)方法获取实例
        Subject subject = (Subject) Proxy.newProxyInstance(classLoader, interfaces, dynamicProxyHandler);
		
        //执行
        subject.Request();
    }


}

补充

在Java中要想实现JDK动态代理,需要java.lang.reflect.InvocationHandler接口和 java.lang.reflect.Proxy类的支持。

package java.lang.reflect;
public interface InvocationHandler {
 /**
     * proxy 被代理对象
     * method 要调用的方法
     * args 方法调用时所需要的参数
     */
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable;
}
public class Proxy implements java.io.Serializable {
    ...
    /**
     * loader 类的加载器
     * interfaces 得到全部的接口
     * h 得到InvocationHandler接口的子类的实例
     */
        @CallerSensitive
        public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
        throws IllegalArgumentException{...}
    ...
}
CGLIB动态代理

该机制来避免JDK 动态代理只能代理实现了接口的类这个问题。

CGLIB介绍

CGLIBopen in new window(Code Generation Library)是一个基于ASMopen in new window的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB 通过继承方式实现代理。

  • 前提步骤

maven引入CGLIB

<dependency>
  <groupId>cglib</groupId>
  <artifactId>cglib</artifactId>
  <version>3.3.0</version>
</dependency>
public class DynamicProxyInterceptorCGLIB implements MethodInterceptor {

    /**
     * 自定义方法拦截器 MethodInterceptor
     * 类似于JDK动态代理中的InvocationHandler.invoke()
     * obj : 动态生成的代理对象
     * method : 被拦截的方法(需要增强的方法)
     * args : 方法入参
     * proxy : 用于调用原始方法
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        //TODO 业务前处理
        System.out.println("pre");
        Object ret = methodProxy.invokeSuper(o, objects);
        //TODO 业务后处理
        return ret;
    }

    public static void main(String[] args) {
        // 创建动态代理增强类
        Enhancer enhancer = new Enhancer();
        // 设置类加载器
        enhancer.setClassLoader(Subject.class.getClassLoader());
        // 设置被代理类(具体类,而非接口)
        enhancer.setSuperclass(RealSubject.class);
        // 设置方法拦截器
        enhancer.setCallback(new DynamicProxyInterceptorCGLIB());
        // 创建代理类
        Subject subject = (Subject) enhancer.create();
        
        subject.Request();
    }
}

补充

在Java中要想实现 CGLIB 动态代理,需要net.sf.cglib.proxy.MethodInterceptor接口和 net.sf.cglib.proxy.Enhancer类的支持。

package net.sf.cglib.proxy;

import java.lang.reflect.Method;

public interface MethodInterceptor extends Callback {
    /**
     * @param o           代理对象(增强的对象)
     * @param method      被拦截的方法(需要增强的方法)
     * @param args        方法入参
     * @param methodProxy 用于调用原始方法
     */
    Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable;
}
package net.sf.cglib.proxy;
import ...;

public class Enhancer extends AbstractClassGenerator {
    ...
    /**
     * @param classLoader 设置类加载器
     */
    public void setClassLoader(ClassLoader classLoader) {...}
	...
    /**
     * @param superclass 设置被代理类(此处应该为实际被代理类)
     */
    public void setSuperclass(Class superclass) {...}
    ...
    /**
     * @param callback 设置方法拦截器,即自定义的实现MethodInterceptor接口的类
     */
    public void setCallback(Callback callback) {...}
    ...
    /**
     * 创建代理类
     */
    public Object create() {...}
    ...
}
缺点
  • JDK 动态代理只能代理实现了接口的类,CGLIB 可以代理未实现任何接口的类
  • CGLIB 动态代理是通过生成一个被代理类的子类来拦截被代理类的方法调用,因此不能代理声明为 final 类型的类和方法
  • 效率较低。使用了反射机制,导致了效率会下降。但是就二者的效率来比较,大部分情况都是JDK 动态代理更优秀,随着 JDK 版本的升级,这个优势更加明显。
静态动态代理对比
  1. 灵活性
    • 静态代理需要根据目标类创建对应的代理类,造成了代码的大量冗余,而且无论修改或者新增都需要对原始代码进行一定的改动;
    • 动态代理更加灵活,不需要通过实现统一接口,可以直接代理实现类,无需要针对每个目标类都创建一个代理类。
  2. JVM 层面
    • 静态代理在编译时就将接口、实现类、代理类这些都变成了一个个实际的 class 文件。
    • 动态代理是在运行时动态生成类字节码,并动态加载到 JVM 中的。
  3. 效率
    • 静态代理正因为在编译时产生class字节码文件,可以直接使用,因此效率高。
    • 动态代理在运行时动态生成类字节码,并加载到JVM中,因此效率低。

应用实例:

  1. Spring AOP
    大量冗余,而且无论修改或者新增都需要对原始代码进行一定的改动;
  • 动态代理更加灵活,不需要通过实现统一接口,可以直接代理实现类,无需要针对每个目标类都创建一个代理类。
  1. JVM 层面
    • 静态代理在编译时就将接口、实现类、代理类这些都变成了一个个实际的 class 文件。
    • 动态代理是在运行时动态生成类字节码,并动态加载到 JVM 中的。
  2. 效率
    • 静态代理正因为在编译时产生class字节码文件,可以直接使用,因此效率高。
    • 动态代理在运行时动态生成类字节码,并加载到JVM中,因此效率低。

应用实例:

  1. Spring AOP
    1. 如果要代理的目标对象实现至少一个接口,则将使用 JDK 动态代理。目标类型实现的所有接口都将被代理。如果目标对象未实现任何接口,则将创建 CGLIB 代理。详情分析参考
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值