Java设计模式中代理模式是什么/JDK动态代理分为哪些,静态代理又怎么实现,又适合哪些场景

继续整理记录这段时间来的收获,详细代码可在我的Gitee仓库Java设计模式克隆下载学习使用!

5.结构型模式

5.1 概述

  • 根据如何将类或对象按某种布局组成更大的结构,分为类结构模式对象结构模式,前者采用继承机制来组织接口和类,后者采用组合或聚合来组合对象
  • 对象结构模式比类结构模式具有更大灵活性—因组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”

5.2 代理模式

5.2.1 概述

  • 背景:由于某些原因需要给某对象提供一个代理以控制对该对象的访问
  • 特点:访问对象不适合或者不直接引用目标对象,代理对象作为可以访问对象和目标对象的中介
  • 根据代理类生成时机不同分为静态代理和动态代理,静态代理在编译器生成,后者在Java运行时动态生成
  • 动态代理分为JDK代理和CGLIB代理

5.2.2 结构

  • 抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法
  • 真实主题(Real Subject)类:实现抽象主题类的具体业务,是代理对象所代表的真实对象
  • 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,可以访问,控制或扩展真实主题的功能

5.2.3 静态代理

火车站卖票案例
代码:

//抽象主题
public interface SellTickets {  
    void sellTickets();  
}
//真实主题
public class TrainStation implements SellTickets{  
    @Override  
    public void sellTickets() {  
        System.out.println("火车站卖票");  
    }  
}
//代理类
public class ProxyPoint implements SellTickets{  
    private TrainStation station = new TrainStation();  
    @Override  
    public void sellTickets() {  
        System.out.println("代理商收取服务费");  
        station.sellTickets();  
    }  
}

5.2.4 JDK动态代理

5.2.4.1 案例
  • 使用案例同上
  • 抽象主题和真实主题同上
  • 代码

    注意:
    ProxyFactory不是代理模式中的代理类
public class ProxyFactory{  
//    声明目标对象  
    private TrainStation station = new TrainStation();  
    public SellTickets getProxyObjects()  
    {  
        /*ClassLoader loader, 类加载器,用于加载代理类,可以通过目标对象获取类加载器  
         Class<?>[] interfaces,代理类实现的接口的字节码对象  
         InvocationHandler h 代理对象调用的调用处理程序  
        * */        SellTickets proxyInstance = (SellTickets) Proxy.newProxyInstance(  
                station.getClass().getClassLoader(),  
                station.getClass().getInterfaces(),  
                new InvocationHandler() {  
                    /*Object 代理对象,和proxyInstance是同一个对象,在invoke中基本不用  
                    Method 代理对象调用方法,对接口中方法进行封装的对象,这里是sellTickets  
                    Object[] 调用方法参数  
                    * */                    @Override  
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
                        System.out.println("代理商收取服务费(JDK动态代理)");  
//                        执行目标对象的方法  
                        Object invoke = method.invoke(station, args);  
                        return invoke;  
                    }  
                }  
        );  
        return proxyInstance;  
    }  
}
	//测试
	 public static void main(String[] args) {  
//        获取代理对象  
//         创建代理工厂对象  
        ProxyFactory proxyFactory = new ProxyFactory();  
//        使用代理工厂创建代理对象  
        SellTickets proxyObjects = proxyFactory.getProxyObjects();  
//        调用买票方法  
        proxyObjects.sellTickets();  
    }
5.2.4.2 案例中的代理类
  • 使用arthas-boot.jar来查看具体的代理类等信息
  • 解压后使用CMD打开
    • 在测试类加代码使程序一直运行,如图![[Pasted image 20221231154138.png]]
    • 命令java -jar arthas-boot.jar,如图![[Pasted image 20221231154030.png]]
    • 继续输入2,如图![[Pasted image 20221231154059.png]]
    • 命令com.sun.proxy.$Proxy0,如图![[Pasted image 20221231154338.png]]
    • 代理类$Proxy0重点代码
    public final class $Proxy0 extends Proxy implements SellTickets {  
    private static Method m3;  
    public $Proxy0(InvocationHandler invocationHandler) {  
        super(invocationHandler);  
    }  
    static {  
        m3 = Class.forName("com.demo.patternDesign.ProxyModel.DynamicJDKProxy.SellTickets").getMethod("sellTickets", new Class[0]);  
    } 
    

// 代理类调用真实对象方法
public final void sellTickets() {
this.h.invoke(this, m3, null);
}
}
```

  • 执行流程
    • 测试类通过代理对象调用sell方法
    • 根据多态,执行代理类$Proxy0的sell方法
    • 代理类$Proxy0的sell方法又调用InvocationHandle接口中的自实现类对象的invoke方法(测试类加收取服务费那方法)
    • invoke方法通过反射执行真实对象类TrainStation中的sell方法

5.2.5 CGLIB动态代理

和之前一样的案例

<dependency>
	<groupId>cglib</groupId>
	<artifactId>cglib</artifactId>
	<version>3.3.0</version>
</dependency>
  • CGLIB包介绍:高性能代码生成包,可为没有实现接口类提供代理
  • 代码
// 代理工厂
public class ProxyFactory implements MethodInterceptor {  
//    创建真实对象  
    private TrainStation trainStation = new TrainStation();  
    public TrainStation getProxyObject(){  
//        创建Enhance对象,类似于JDK代理中的Proxy类  
        Enhancer enhancer = new Enhancer();  
//        设置父类的字节码对象  
        enhancer.setSuperclass(TrainStation.class);  
        设置回调函数  
        enhancer.setCallback(this);  
//        创建代理对象  
        TrainStation proxyObject = (TrainStation) enhancer.create();  
        return proxyObject;  
    }  
    @Override  
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {  
        System.out.println("代理商收取服务费(cglib代理)");  
        Object invoke = method.invoke(trainStation,objects);  
        return invoke;  
    }  
}
// 测试代码
    public static void main(String[] args) {  
//        创建代理工厂对象  
        ProxyFactory proxyFactory = new ProxyFactory();  
//        获取代理对象  
        TrainStation proxyObject = proxyFactory.getProxyObject();  
        proxyObject.sellTickets();
    }

5.2.6 3 种方式对比

5.2.6.1 JDK和CGLIB
  • CGLIB代理
    • 底层采用ASM字节码生成框架,使用字节码技术生成代理类
    • 不能对声明为final的类或方法进行代理,因CGLIB原理是动态生成被代理的子类
    • JDK1.6之前比JAVA使用反射效率要高
  • 两者效率问题
    • JDK1.6,1.7,1.8后在调用次数较少情况下JDK代理效率高于CGLIB代理效率
    • 仅当进行大量调用且在JDK1.6,1.7中CGLIB代理效率高
  • 选择:有接口使用JDK动态代理,没有接口使用CGLIB代理
5.2.6.2 动态和静态
  • 动态优点:声明的所有方法都被转移到调用处理器一个集中方法处理
  • 静态缺点:若接口增加方法,静态代理除了所有实现类需要实现此方法外,所有处理类也需要实现此方法,增加代码冗余与复杂性

5.2.7 优缺点

5.2.7.1 优点
  • 在客户端与目标对象之间起一个中介作用和保护目标对象作用
  • 可以扩展目标对象的功能
  • 能将客户端与目标对象分离,一定程度上降低系统耦合度
5.2.7.2 缺点

增加系统复杂度

5.2.8 使用场景

5.2.8.1 远程(Remote)代理

本地服务通过网络请求远程服务:可将网络通信部分隐藏,只保留本地服务一个接口,使其通过此接口访问远程服务提供功能,而不必过多关心通信细节。

5.2.8.2 防火墙(Firewall)代理

将浏览器配置成代理模式,防火墙将请求转发给互联网,当互联网响应时,代理服务器将信息再转发给浏览器

5.2.8.3 保护(Protect or Access)代理

控制一个对象访问,若需要,可以给不同对象以不同访问权限

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值