代理模式的学习

应用例子:

​ 一个老师请假了,需要一个代课老师。

代理模式

 让你能够提供对象的替代品或其占位符。 代理控制着对于原对象的访问, 并允许在将请求提交给对象前后进行一些处理。即通过代理对象访问具体目标对象。

分类:

 静态代理

 动态代理(JDK代理、接口代理)

  Cglib代理(可以在内存动态创建,而不需要实现接口)

 

代理模式结构

在这里插入图片描述

 1. 服务接口 (Service Interface) 声明了服务接口。 代理必须遵循该接口才能伪装成服务对象。

 2. 服务(Service) 类提供了一些实用的业务逻辑。

 3. 代理 (Proxy) 类包含一个指向服务对象的引用成员变量。 代理完成其任务 (例如延迟初始化、 记录日志、 访问控制和缓存等) 后会将请求传递给服务对象。 通常情况下, 代理会对其服务对象的整个生命周期进行管理。

 4. 客户端(Client) 能通过同一接口与服务或代理进行交互, 所以你可在一切需要服务对象的代码中使用代理。

 

代理模式适合应用场景

 1.延迟初始化 (虚拟代理)。如果你有一个偶尔使用的重量级服务对象,一直保持该对象运行会消耗系统资源时, 可使用代理模式.

 2.访问控制 (保护代理)。 如果你只希望特定客户端使用服务对象**,** 这里的对象可以是操作系统中非常重要的部分**,** 而客户端则是各种已启动的程序 (包括恶意程序), 此时可使用代理模式**。

 3. 本地执行远程服务 (远程代理)。 适用于服务对象位于远程服务器上的情形。

 4.记录日志请求 (日志记录代理)。 适用于当你需要保存对于服务对象的请求历史记录时。 代理可以在向服务传递请求前进行记录。

 5.智能引用。 可在没有客户端使用某个重量级对象时立即销毁该对象。

 

实现方式

 1. 如果没有现成的服务接口, 你就需要创建一个接口来实现代理和服务对象的可交换性。 从服务类中抽取接口并非总是可行的, 因为你需要对服务的所有客户端进行修改, 让它们使用接口。 备选计划是将代理作为服务类的子类, 这样代理就能继承服务的所有接口了。

 2. 创建代理类, 其中必须包含一个存储指向服务的引用的成员变量。 通常情况下, 代理负责创建服务并对其整个生命周期进行管理。 在一些特殊情况下, 客户端会通过构造函数将服务传递给代理。

 3. 根据需求实现代理方法。 在大部分情况下, 代理在完成一些任务后应将工作委派给服务对象。

 4. 可以考虑新建一个构建方法来判断客户端可获取的是代理还是实际服务。 你可以在代理类中创建一个简单的静态方法, 也可以创建一个完整的工厂方法。

 5. 可以考虑为服务对象实现延迟初始化。

 

代理模式优缺点

 优点:

  ✔️ 你可以在客户端毫无察觉的情况下控制服务对象。

  ✔️ 如果客户端对服务对象的生命周期没有特殊要求, 你可以对生命周期进行管理。

  ✔️即使服务对象还未准备好或不存在, 代理也可以正常工作。

  ✔️开闭原则。 你可以在不对服务或客户端做出修改的情况下创建新代理。

​  缺点:

  ❌代码可能会变得复杂, 因为需要新建许多类。

  ❌ 服务响应可能会延迟。

相关的代理模式实现

静态代理

  静态代理在使用时,需要定义接口或父类,被代理对象与代理对象一起实现相同的接口或者是继承相同的父类。

在这里插入图片描述

public interface Theacher {
    void teach();//上课
}

public class SchoolTeacher implements Theacher{
    @Override
    public void teach() {
        System.out.println("老师在上课...");
    }
}

Proxy

public class ProxyTeacher implements Theacher{

    private Theacher target; // 目标对象,通过接口来聚合

    public ProxyTeacher(Theacher target) {
        this.target = target;
    }

    @Override
    public void teach() {
        System.out.println("代理老师自我介绍"); //相关操作
        target.teach();
        System.out.println("代理老师结束"); //动作完成后续
    }
}

public class Client {
    public static void main(String[] args) {
        //创建 老师(目标对象)
        SchoolTeacher st = new SchoolTeacher();

        //创建代理对象,同时把被代理对象传递给代理对象
        ProxyTeacher pt = new ProxyTeacher(st);

        pt.teach();
    }
}

​ 优点:

​  在不改变目标对象的前提下,能通过代理对象对目标的功能进行拓展

​ 缺点:

​  因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类

​  一旦接口增加了方法,目标对象与代理对象都要维护

动态代理

​  代理对象依赖JDK的API,动态的在内存中构建代理对象。

​  代理对象不需要实现接口,但目标对象要实现接口。

更改后的项目

在这里插入图片描述

public interface Theacher {
    void teach();//上课
    void sayHello(String name);
}


public class SchoolTeacher implements Theacher{

    @Override
    public void teach() {
        System.out.println("老师在上课...");
    }

    @Override
    public void sayHello(String name) {
        System.out.println(name+" say Hello");
    }
}

Proxy

public class ProxyTeacher{

    private Object target; // 目标对象

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

    //给目标对象,生成一个代理对象
    public Object getProxyInstance(){
        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;
                    }
                });
    }

    /*
        补充说明:
            public static Object newProxyInstance(ClassLoader loader,
                  Class<?>[] interfaces, InvocationHandler h)

           ClassLoader loader : 目标对象使用的类加载器,获取加载器的方法
           Class<?>[] interfaces:  目标的对象使用的接口类型,使用泛型方法确定类型
           InvocationHandler h: 事情处理,执行目标对象的方法时,对触发事情处理器方法,会把当前执行的目标对象方法作为参数传入
     */
}
public class Client {
    public static void main(String[] args) {
        //创建 老师(目标对象)
        SchoolTeacher st = new SchoolTeacher();

        //创建代理对象,同时把被代理对象传递给代理对象
        Theacher pt = (Theacher) new ProxyTeacher(st).getProxyInstance();

        //class com.sun.proxy.$Proxy0 内存中动态生成了代理对象
        System.out.println(pt.getClass());

        pt.sayHello("Tom");
        pt.teach();
        
    }
}

Cglib代理

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

 Cglib是一个强大的高性能代码生成包,可以在运行期扩展java类与实现java接口,它被广泛应用于AOP框架中。

注:Cglib底层是使用字节码处理框架ASM来转换字节码并生成新的类

 实现要素:需要导入cglib的jar包

更改后的项目

在这里插入图片描述

public class Theacher {
    public String teach(){
        System.out.println("cglib代理");
        return "teaching...";
    }
}

Proxy

public class ProxyTeacher implements MethodInterceptor {

    private Object target; // 目标对象

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

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

    //重写intercept(),会调用目标对象的方法
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws InvocationTargetException, IllegalAccessException {
        System.out.println("Cglib代理模式开始...");
        Object result = method.invoke(target,objects);
        System.out.println("Cglib代理模式结束...");

        return result;
    }
}
public class Client {
    public static void main(String[] args) {
        //创建 老师(目标对象)
        Theacher st = new Theacher();

        //创建代理对象,同时把被代理对象传递给代理对象
        Theacher proxy = (Theacher) new ProxyTeacher(st).getProxyInstance();

        String str = proxy.teach();
        System.out.println(str);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值