设计模式——代理模式

Posted 爱学习的大鱼

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式——代理模式相关的知识,希望对你有一定的参考价值。

模式介绍

  • 代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
  • 当一个复杂对象的多份副本须存在时,代理模式可以结合享元模式以减少存储器用量。典型作法是创建一个复杂对象及多个代理者,每个代理者会引用到原本的复杂对象。而作用在代理者的运算会转送到原本对象。一旦所有的代理者都不存在时,复杂对象会被移除。
  • 代理模式有不同的形式,主要有三种静态代理、动态代理(JDK 代理、接口代理)和Cglib 代理(可以在内存动态的创建对象,而不需要实现接口,他是 属于动态代理的范畴)

UML类图

类图解析:

Subject:通过接口或抽象类声明真实角色实现的业务方法。

Proxy:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。

RealSubject:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。

静态代理

背景介绍:老师生病了,需要另一名老师帮其代课

ITeacherDao接口类

public interface ITeacherDao 

    /**
     * 老师上课
     */
    void teach();

TeacherDao实现类

public class TeacherDao implements ITeacherDao 
    @Override
    public void teach() 
        System.out.println("开始上课...");
    

TeacherDaoProxy代理类

public class TeacherDaoProxy implements ITeacherDao
    // 被代理对象
    private ITeacherDao iTeacherDao;

    public TeacherDaoProxy(ITeacherDao iTeacherDao) 
        this.iTeacherDao = iTeacherDao;
    

    public TeacherDaoProxy() 
        try 
            iTeacherDao =(ITeacherDao) this.getClass().getClassLoader().loadClass("com.xy.staticproxy.TeacherDao").newInstance();
         catch (Exception e) 
            System.err.println(e.getMessage());
        
    

    /**
     * 上课前
     */
    public void preTeach() 
        System.out.println("你们老师生病了, 我来代一节课...");
    

    @Override
    public void teach() 
        preTeach();
        iTeacherDao.teach();
        afterTeach();
    

    /**
     * 上课后
     */
    private void afterTeach() 
        System.out.println("下课了, 同学们有缘再见...");
    

Client测试类

public class Client 
    public static void main(String[] args) 
        // 生病的老师, 被代理对象
        ITeacherDao patientTeacher = new TeacherDao();

        // 新老师, 代理对象
        //ITeacherDao proxyTeacher = new TeacherDaoProxy(patientTeacher);
        TeacherDaoProxy proxyTeacher = new TeacherDaoProxy();

        // 老师代课
        proxyTeacher.teach();
    

实现效果:

静态代理优缺点

  • 优点:在不修改目标对象的功能前提下,能通过代理对象对目标功能扩展。
  • 缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类一旦接口增加方法,目标对象与代理对象都要维护

动态代理

动态代理模式的基本介绍

  • 代理对象,不需要实现接口,但是目标对象要实现接口,否则不能用动态代理
  • 代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象动态代理也叫做: JDK代理、接口代理。

ITeacherDao接口类

public interface ITeacherDao 

    /**
     * 老师上课
     */
    void teach();

TeacherDao实现类

public class TeacherDao implements ITeacherDao 
    @Override
    public void teach() 
        System.out.println("开始上课...");
    

ProxyFactory代理类

public class ProxyFactory
    // 目标对象
    private Object target;

    // 构造器,对target进行初始化
    public ProxyFactory(Object target) 
        this.target = target;
    

    /**
     * 方法执行前
     */
    public void preMethod() 
        System.out.println("方法执行前...");
    

    /**
     * 方法执行后
     */
    private void afterMethod() 
        System.out.println("方法执行后...");
    

    // 给目标对象生成代理对象
    public Object getProxyInstance() 

        /**
         * ClassLoader loader, 指定类加载器
         * Class<?>[] interfaces,目标对象(被代理对象),使用泛型方式确认类型
         * InvocationHandler h 事件处理执行目标对象的方法时,会触发时间处理器的方法
         * 会把当前执行的目标对象方法作为参数传入
         */
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                (proxy, method, args) -> 
                    preMethod();
                    // 反射机制调用目标对象方法
                    Object invoke = method.invoke(target, args);
                    afterMethod();
                    return invoke;
                );
    

Test用于测试动态代理效果

public interface Test 
    void test();

Client测试类

public class Client 
    public static void main(String[] args) 
        // 被代理对象
        TeacherDao target = new TeacherDao();

        // 创建代理对象
        ITeacherDao proxyITeacher = (ITeacherDao)new ProxyFactory(target).getProxyInstance();
        proxyITeacher.teach();

        System.out.println("-------------");

        Test test = (Test)new ProxyFactory((Test) () ->
                System.out.println("this is a test")
        ).getProxyInstance();
        test.test();
    

测试结果:

cglib代理

Cglib代理模式的基本介绍

  • 静态代理和JDK代理模式都要求目标对象是实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可使用目标对象子类来实现代理,即Cglib代理

  • Cglib代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展,有些书也将Cglib代理归属到动态代理。

  • Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP,实现方法拦截

  • 在AOP编程中如何选择代理模式:

    • 目标对象需要实现接口,用JDK代理
    • 目标对象不需要实现接口,用Cglib代理
  • Cglib包的底层是通过使用字节码处理框架ASM来转换字节码并生成新的类

导入cglib依赖jar包


`ProxyFactoryd代理工厂

public class ProxyFactory implements MethodInterceptor 
    //  维护一个目标对象
    private Object target;

    public ProxyFactory(Object target) 
        this.target = target;
    

    /**
     * 返回一个target代理对象
     * @return
     */
    public Object getProxyInstance() 
        // 1、创建一个工具类
        Enhancer enhancer = new Enhancer();
        // 2、设置父类
        enhancer.setSuperclass(target.getClass());
        // 3、设置回调函数
        enhancer.setCallback(this);
        // 4、返回子类,即代理对象
        return enhancer.create();
    

    /**
     * 方法执行前
     */
    public void preMethod() 
        System.out.println("方法执行前...");
    

    /**
     * 方法执行后
     */
    private void afterMethod() 
        System.out.println("方法执行后...");
    

    // 重写拦截方法,调用目标方法
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable 
        preMethod();
        // 使用反射
        //Object value = method.invoke(target, objects);

        // 使用fastClass
        Object value = methodProxy.invokeSuper(o, objects);
        afterMethod();
        return value;
    

TeacherDao和Test(被代理类)

public class TeacherDao 
    public void teach() 
        System.out.println("老师授课中...");
    


class Test
    public void test() 
        System.out.println("this is a teat...");
    

Client测试类

public class Client 
    public static void main(String[] args) 
        // 被代理类
        TeacherDao target = new TeacherDao();

        // 创建对象代理
        TeacherDao proxyTeacher =(TeacherDao) new ProxyFactory(target).getProxyInstance();

        // 执行方法
        proxyTeacher.teach();

        System.out.println("----------");

        // 测试类
        ((Test)new ProxyFactory(new Test()).getProxyInstance()).test();
    

测试结果:

几种常见的代理模式介绍——几种变体

  • 防火墙代理:内网通过代理穿透防火墙,实现对公网的访问。
  • 缓存代理:比如:当请求图片文件等资源时,先到缓存代理取,如果取到资源则ok,如果取不到资源,再到公网或者数据库取,然后缓存。
  • 远程代理:远程对象的本地代表,通过它可以把远程对象当本地对象来调用。远程代理通过网络和真正的远程对象沟通信息。
  • 同步代理:主要使用在多线程编程中,完成多线程间同步工作

以上是关于设计模式——代理模式的主要内容,如果未能解决你的问题,请参考以下文章

《Head First 设计模式》之代理模式

读大学师生关系有感

Javascript模块模式的变体

Android 横向和纵​​向模式变体

20165203 我期望的师生关系

如何在纵向模式下支持 iPad 的 180 度变体?