设计模式---代理模式

在现实生活中,一个对象不能直接访问另一个对象,这时需要找中介来访问目标对象,此时的中介就是代理对象。例如:租房子时,我们无法与房东取得联系,只能通过某网站与中介进行交易,获取自己心仪的房间等等。在软件设计中,使用代理模式的例子也很多,例如:访问阿里的 maven 仓库,其就是海外 maven 仓库的代理。还有因为安全原因需要屏蔽客户端直接访问真是对象,如某单位的内部数据等。

为什么jdk动态代理必须基于接口 
原因如下: 
1、生成的代理类继承了Proxy,由于java是单继承,所以只能实现接口,通过接口实现 

2、从代理模式的设计来说,充分利用了java的多态特性,也符合基于接口编码的规范 

一、代理模式基本介绍
【1】代理模式:为一个对象提供一个替身,以控制对目标对象的访问。即通过代理对象访问目标对象。这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,及扩展目标对象的功能
【2】被代理的对象可以是远程对象,创建开销大的对象或需要安全控制的对象
【3】代理模式有不同的形式,主要有三种:静态代理、动态代理(又称JDK代理、接口代理)和 Cglib 代理(可以在内存动态的创建对象,目标对象也不需要实现接口,它也属于动态代理的范畴,但比较特殊)
【4】代理模式的主要优点

①、代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用。

②、代理对象可以扩展目标对象的功能

③、代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度

【5】代理模式的主要缺点

①、在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢

②、增加了系统的复杂度

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

静态代理 类图 如下:
 【1】抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
 【2】真实主题(Real Subject)类:实现了出现主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
 【3】代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。

   

静态代理 代码 实例:
【1】抽象主题类:代理类与被代理类都需要继承的接口

//购票接口
public interface Ticketing {
    //购票
    public String buy();
}

【2】真实主题类:目标类

//火车售票官方系统
public class RailwaySite implements Ticketing{
    @Override
    public String buy() {
        String ticket = " 调用官方系统购票,票价=120 ";
        System.out.println();
        return ticket;
    }
}


 【3】代理类:需要实现被代理类的接口,使用代理方法调用目标对象的方法,同时实现对目标方法的扩展。

//实现与目标系统一致的接口
public class ProxyTicketSystem implements Ticketing{
    //组合 被代理对象
    private Ticketing ticket;
    //构造器
    public ProxyTicketSystem(Ticketing ticket) {
        this.ticket = ticket;
    }
    
    @Override
    public String buy() {
        System.out.println("代理(智行火车票 系统启动");
        String ticketInfo = ticket.buy();
        ticketInfo+="第三方系统服务费 20 总计:140元";
        System.out.println("代理(智行火车票 系统结束");
        return ticketInfo;
    }
 
}

【4】客户端:需要创建被代理对象和代理对象,并进行组合调用。

public class Client {
    public static void main(String[] args) {
        //被代理类
        RailwaySite railwaySite = new RailwaySite();
        //获取代理类
        ProxyTicketSystem proxy = new ProxyTicketSystem(railwaySite);
        //调用代理方法
        String buy = proxy.buy();
        /**
         *   代理(智行火车票 系统启动
         *  代理(智行火车票 系统结束
         *   调用官方系统购票,票价=120 第三方系统服务费 20 总计:140元
         */
        System.out.println(buy);
    }
}

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

三、动态代理
动态代理基本介绍:

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

JDK 中生成代理对象的 API:代理类所在包:java.lang.reflect.Proxy JDK 实现代理只需要使用 newProxyInstance 方法,但是该方法需要接收三个参数,完整的写法是:

static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
动态代理类图如下:与动态代理 代码 实例相互参考实现。与静态最大的不同在于 代理类 的不同。代理类无需实现目标类的接口,同时组合的是 Object 通用对象,无需组合目标接口对象,适合所有类(实现了接口)的代理方式,非常通用。
  

动态代理代码实例:【1】抽象主题类:被代理类(目标类)都需要继承的接口

public interface ITicket {
    //购票
    public String buy();
}


【2】真实主题类:目标类

public class RailTicketImpl implements ITicket{
 
    @Override
    public String buy() {
        String ticket = " 调用官方系统购票,票价=120 ";
        System.out.println();
        return ticket;
    }
}


【3】代理类:也是动态代理与静态的区别之处,动态代理主要通过 JDK 的 Proxy.newProxyInstance 方法返回代理对象,且调用 method 的 invoke 内置方法,并将其结果返回。代理类实现了与目标类的解耦,适合为 实现任意接口的所有类 做代理

 //代理类  能够实现所有类(必须实现接口)的代理
public class ProxyTicket {
    //注入目标类的接口
    private Object target;
    //构造器
    public ProxyTicket(Object target) {
        super();
        this.target = target;
    }
    public Object getInstance() {
        // ClassLoader loader =指定当前目标对象使用的类加载器,获取加载器的方法固定
        // Class<?>[] interfaces = 目标类实现的接口 使用泛型方法确认类型
        //InvocationHandler h = 事件处理,执行目标对象的方法,会触发事件处理器方法,会把当前执行的目标对象方法作为参数传入
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), new InvocationHandler() {
                    //Object  proxy  传入代理对象
                    //method 代理的方法  
                    // args 代理参数 
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("代理对象 方法入口");
                        //调用代理方法时 传入目标对象和参数
                        Object invoke = method.invoke(target, args);
                        return invoke;
                    }
                });
    }
}


【4】客户端类:目标对象和代理对象的返回值都必须使用接口接收,否则会出现转换异常。这也是为什么目标类必须实现接口的原因。

public class Client {
    public static void main(String[] args) {
        //定义目标类(被代理类)
        ITicket railTicketImpl = new RailTicketImpl();
        //创建代理类
        ProxyTicket proxyTicket = new ProxyTicket(railTicketImpl);
        //获取代理对象  需要强转对象类型
        ITicket ticket = (ITicket)proxyTicket.getInstance();
        //调用目标方法,使用 debugger 调试时会发现其调用了 invoke 方法
        ticket.buy();
    }
}


【JDK代理为什么不能直接对非接口的类进行代理】 :由于 Java的单继承,动态生成的代理类已经继承了 Proxy类的,就不能再继承其他的类,所以只能靠实现被代理类的接口的形式,故 JDK的动态代理必须有接口。

四、Cglib 代理
1)、静态代理和动态代理都要求目标对象是实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可使用目标对象子类来实现代理——这就是 Cglib 代理。
2)、Cglib 代理也叫子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展,有些书也将 Cglib 代理归属于动态代理。
3)、Cglib 是一个非常强大的高性能的代码生成包,它可以在运行期扩展 java 类与 实现 java 接口。它广泛的被许多 AOP 框架使用,例如:Spring AOP,实现方法的拦截。
4)、在AOP编程中如何选择代理:目标对象需要实现接口,用 JDK 代理。目标对象不需要实现接口,用 Cglib 代理。
5)、Cglib 包在底层是通过使用字节码处理框架 ASM 来转换字节码并生成新的类。

【1】Cglib 依赖的 jar 包:


 【2】在内存中动态构建子类,需要注意代理的类不能为 final ,否则会出现:java.lang.IllegalArgumentException 错误。
【3】目标方法不能使用 final/static 修饰,否则不会被拦截,即不会执行。
【动态代理 类图 如下】:


【动态代理代码实例如下】:
【1】被代理类:无需实现接口,很平常的一个类

public class RailTicketImpl{
    public String buy() {
        String ticket = " 调用官方系统购票,票价=120 ";
        System.out.println(ticket);
        return ticket;
    }
}


【2】代理类:需要实现 jar 包中的 MethodInterceptor 接口,重写 intercept 方法,此方法用于拦截代理对象的方法调用。同时需要创建一个代理对象返回方法:getProxyInstall() 可自行定义,其内部通过工具类 enhancer.create() 创建并返回代理对象,代理对象中需要传入父类即目标类等等参数。

public class ProxyFactory implements MethodInterceptor{
    //组合目标对象
    private Object target;
    //构造器
    public ProxyFactory(Object target) {
        this.target = target;
    }
    //写一个返回代理对象的方法:target 的代理类
    public Object getProxyInstall() {
        //1.创建一个工具类
        Enhancer enhancer = new Enhancer();
        //2.将目标类设置为 父类 , 因为我们创建的是子类
        enhancer.setSuperclass(target.getClass());
        //3. 设置回调函数
        enhancer.setCallback(this);
        //4. 创建子类对象,即代理对象
        return enhancer.create();
    }
    
    //重写  intercept 方法,会调用目标对象的方法
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy arg3) throws Throwable {
        System.out.println("Cglib代理模式 ~~ 开始");
        Object returnVal = method.invoke(target, args);
        System.out.println("Cglib代理模式 ~~ 提交");
        return returnVal;
    }
}


【3】客户端类:创建目标类和代理类,并调用目标方法,跟踪代码会发现调用了 intercept 方法,也就是方法会被 intercept  拦截。此时代理对象的返回值,就可以为目标类。

public class Client {
    public static void main(String[] args) {
        //目标类
        RailTicketImpl ticket = new RailTicketImpl();
        //代理工厂类
        ProxyFactory proxyFactory = new ProxyFactory(ticket);
        //获取代理类 需要强转类型
        RailTicketImpl proxyInstall = (RailTicketImpl)proxyFactory.getProxyInstall();
        //调用目标方法
        proxyInstall.buy();
        /**
         * 输入如下:
         * Cglib代理模式 ~~ 开始
         *  调用官方系统购票,票价=120 
         * Cglib代理模式 ~~ 提交
         */
    }
}    


五、代理模式的变体(了解)
【1】防火墙(Firewall)代理:内网通过代理穿透防火墙,实现对公网的访问。
【2】缓存(Cache)代理:例如,当请求图片或文件等资源时,先到缓存代理取,如果取到资源则返回,如果取不到资源,再到公网或者数据库中取,然后缓存。
【3】远程(Remote)代理:可以把远程对象在本地 cope 一份来调用。远程代理通过网络和真正的远程对象同步。
【4】同步(Synchronization)代理:主要使用在多线程编程中,完成多线程间同步工作。
【5】智能引用(Smart Reference)代理 等等

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java设计模式是一组经过实践验证的面向对象设计原则和模式,可以帮助开发人员解决常见的软件设计问题。下面是常见的23种设计模式: 1. 创建型模式(Creational Patterns): - 工厂方法模式(Factory Method Pattern) - 抽象工厂模式(Abstract Factory Pattern) - 例模式(Singleton Pattern) - 原型模式(Prototype Pattern) - 建造者模式(Builder Pattern) 2. 结构型模式(Structural Patterns): - 适配器模式(Adapter Pattern) - 桥接模式(Bridge Pattern) - 组合模式(Composite Pattern) - 装饰器模式(Decorator Pattern) - 外观模式(Facade Pattern) - 享元模式(Flyweight Pattern) - 代理模式Proxy Pattern) 3. 行为型模式(Behavioral Patterns): - 责任链模式(Chain of Responsibility Pattern) - 命令模式(Command Pattern) - 解释器模式(Interpreter Pattern) - 迭代器模式(Iterator Pattern) - 中介者模式(Mediator Pattern) - 备忘录模式(Memento Pattern) - 观察者模式(Observer Pattern) - 状态模式(State Pattern) - 策略模式(Strategy Pattern) - 模板方法模式(Template Method Pattern) - 访问者模式(Visitor Pattern) 4. 并发型模式(Concurrency Patterns): - 保护性暂停模式(Guarded Suspension Pattern) - 生产者-消费者模式(Producer-Consumer Pattern) - 读写锁模式(Read-Write Lock Pattern) - 信号量模式(Semaphore Pattern) - 线程池模式(Thread Pool Pattern) 这些设计模式可以根据问题的特点和需求来选择使用,它们提供了一些可复用的解决方案,有助于开发高质量、可维护且易于扩展的软件系统。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值