设计模式之代理模式

代理模式(Proxy Pattern):一个类代表另一个类的功能,给某对象提供一个代理以控制对该对 象的 访问。这时,访问对象不适合或者 不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。这种类型的设计模式属于结构型模式。

意图:为其他对象提供一种代理以控制对这个对象的访问。

主要解决:

  • 在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统
    中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对
    象时加上一个对此对象的访问层。

何时使用:想在访问一个类时做一些控制。

关键代码:实现与被代理类组合。

优点:

  1. 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用; 代理对象可
  2. 以扩展目标对象的功能;
  3. 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度;

缺点:

  1. 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
  2. 增加了系统的复杂度;

注意事项:

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

设计模式之代理模式_第1张图片
代理模式的主要角色如下。

  1. 抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。

  2. 真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对 象,是最终要引用的对象。

  3. 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访 问、控制或扩展真实主题的功能。

使用场景:

  1. 被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象。
  2. 代理模式主要有三种模式:静态代理、动态代理(JDK代理)和Cglib代理(可以在内存动态 的创建对象,而不需要实现接口)

实例:静态代理:

package com.model.study.proxy;

public interface ITeacherDao {

    void teach();
}
package com.model.study.proxy;

public class TeacherDao implements ITeacherDao {
    @Override
    public void teach() {
        System.out.println("老师在讲课");
    }
}
package com.model.study.proxy;

// 静态代理的代理对象
public class TeacherDaoProxy implements ITeacherDao {

    private ITeacherDao teacherDao;

    public TeacherDaoProxy(ITeacherDao teacherDao) {
        this.teacherDao = teacherDao;
    }

    @Override
    public void teach() {
        System.out.println("代理对象开始代理。。。");
        teacherDao.teach();
        System.out.println("提交。。。");
    }
}
package com.model.study.proxy;

public class Client {

    public static void main(String[] args) {

        ITeacherDao teacherDao = new TeacherDao();

        ITeacherDao proxy = new TeacherDaoProxy(teacherDao);

        proxy.teach();
    }
}
代理对象开始代理。。。
老师在讲课
提交。。。

看起来是调用proxy的方法,实际上执行的是teacherDao 的方法

静态代理的优缺点:

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

动态代理:

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

代理类所在包:java。lang.reflect.Proxy
JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整写法:
static Object newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h);

  • ClassLoader loader参数:目标对象使用的类加载器,获取加载器的方法固定
  • Class[] interfaces 参数:目标对象实现的接口类型,使用泛型方法确认类型。
  • InvocationHandler h 参数:执行目标对象的方法时,会触发事情处理器方法。

实例:

package com.model.study.proxy.dynamic;

public interface ITeacherDao {

    void teach();
}

package com.model.study.proxy.dynamic;

public class TeacherDao  implements ITeacherDao{


    @Override
    public void teach() {
        System.out.println("老是在授课中");
    }
}


package com.model.study.proxy.dynamic;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyFactory {

    private Object target;

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

    // 给目标对象生产一个代理对象
    public Object getProxyInstance() {
            //ClassLoader  loader参数:目标对象使用的类加载器,获取加载器的方法固定
            //Class[]  interfaces 参数:目标对象实现的接口类型,使用泛型方法确认类型。
            //InvocationHandler  h 参数:执行目标对象的方法时,会触发事情处理器方法。
      return   Proxy.newProxyInstance(target.getClass().getClassLoader(),
               target.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("jdk代理开始。。。");
                        Object result = method.invoke(target, args);
                        System.out.println("jdk代理提交");
                        return result;
                    }
                });
    }
}


package com.model.study.proxy.dynamic;

public class Client {

    public static void main(String[] args) {
        ITeacherDao teacherDao = new TeacherDao();

        ProxyFactory factory = new ProxyFactory(teacherDao);

        ITeacherDao proxyInstance = (ITeacherDao)factory.getProxyInstance();

        proxyInstance.teach();
    }
}
jdk代理开始。。。
老是在授课中
jdk代理提交

总结:动态代理其实也需要目标对象的引用,然后在通过Proxy.newProxyInstance获取动态的代理对象,这个代理对象会通过反射执行目标对象的方法。


Cglib代理:

  1. 静态代理和JDK代理模式都要求目标实现一个接口,但是有时候目标对象这是一个单独的对象,并没有实现任何的接口,这个时候可用目标子类来实现代理;
  2. Cglib也叫作子类代理,他是在内存中构建一个子类对象,从而实现对目标对象功能扩展,有些书也将Cglib归属于动态代理。
  3. Cglib可以在运行期扩展java类与实现java接口,它广泛的被许多AOP的框架使用,实现方法拦截。
  4. 如果选择代理模式:如果目标对象要实现接口,用JDK代理(动态代理),如果目标对象不需要实现接口,用Cglib代理。

步骤:

  1. 引入Cglib的jar文件(好像上传不了,新版的编辑器不会用)
  2. 在内存中动态构建子类,注意代理的类不能是final,否则会报错。
  3. 目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象(被代理对象)额外的业务方法。
被代理对象:
public class TeacherDao {

    public void teach(){
        System.out.println("老师讲课中,cglib,无需实现接口");
    }
}

代理工厂:
public class ProxyFactory implements MethodInterceptor {

   private Object target;

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

    // 返回目标对象的代理对象。
    public Object getProxyInstance(){
        // 创建工具类
        EnHancer enHancer = new EnHancer();
        // 设置父类
        enHancer.setSuperclass(target.getClass());
        // 设置回调方法
        enHancer.setCallBack(this);
        // 创建子类对象,即代理对象。
        return enHancer.create();
    }

    // 重写intercept方法,会调用目标对象的方法
    @Override
    public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable{
        System.out.println("cglib代理模式开始---");
        Object result = arg1.invoke(target, arg2);
        System.out.println("cglib代理提交---");
        return result;
        }
    }

Client
public class Client {
    public static void main(String[] args) {
        TeacherDao teacherDao = new TeacherDao();

        ProxyFactory proxyFactory = new ProxyFactory(teacherDao);

        TeacherDao proxyInstance = (TeacherDao)proxyFactory.getProxyInstance();

        proxyFactory.teach();
    }
}
cglib代理模式开始---
老师讲课中,cglib,无需实现接口
cglib代理提交---

理解起来难一点,但其实本质还是有一个代理工厂类,用于产生代理对象,代理对象需要持有被代理对象的引用,需要使用反射获取被代理对象(目标对象)的方法,然后执行,看起来是调用代理对象的方法,其实内部还是调用的是目标对象的方法。

本文章参考:
https://www.runoob.com/design-pattern/proxy-pattern.html
http://c.biancheng.net/view/1359.html
2019尚硅谷韩顺平图解java设计模式-23种设计模式(视频)
大话设计模式
HeadFirst 设计模式(中文版)
如有侵权,联系删除!

你可能感兴趣的:(设计模式)