Java的三种代理模式实现

2023-12-13 04:29:16

代理模式的定义:

Provide a surrogate or placeholder for another object to control access to it.(为其他对象提供一种代理以控制对这个对象的访问。)

简单说,就是设置一个中间代理来控制访问原目标对象,达到增强原对象的功能和简化访问方式的目的。

图片

代理模式通用类图

Java实现代理模式分为两类三种,两类是静态代理动态代理,动态代理又可以分为JDK动态代理CGLIB动态代理

图片

Java实现代理模式

静态代理

静态代理比较简单,代理类需要实现和目标接口类一样的接口。

图片

Solver静态代理类图

  • 接口类:ISolver

    public?interface?ISolver?{
    ????void?solve();
    }
    
  • 目标类:Solver

    public?class?Solver?implements?ISolver?{
    ????@Override
    ????public?void?solve()?{
    ????????System.out.println("疯狂掉头发解决问题……");
    ????}
    }
    
  • 代理类:SolverProxy,代理类也要实现接口,并且还要维护一个目标对象。

    public?class?SolverProxy?implements?ISolver?{
    ????//目标对象
    ????private?ISolver?target;
    
    ????public?SolverProxy(ISolver?target)?{
    ????????this.target?=?target;
    ????}
    
    ????@Override
    ????public?void?solve()?{
    ????????System.out.println("请问有什么能帮到您?");
    ????????target.solve();
    ????????System.out.println("问题已经解决啦!");
    ????}
    }
    
  • 客户端;Client

    public?class?Client?{
    ????public?static?void?main(String[]?args)?{
    ????????//目标对象:程序员
    ????????ISolver?developer?=?new?Solver();
    ????????//代理:客服小姐姐
    ????????SolverProxy?csProxy?=?new?SolverProxy(developer);
    ????????//目标方法:解决问题
    ????????csProxy.solve();
    ????}
    }
    
  • 运行结果

    请问有什么能帮到您?
    疯狂掉头发解决问题……
    问题已经解决啦!
    

我们看到,通过静态代理,可以在不修改目标对象的前提下扩展目标对象的功能。

但是,它也有一些问题:

  • 冗余:由于代理对象要实现与目标对象一致的接口,会产生过多的代理类。

  • 维护性不佳:一旦接口增加方法,目标对象与代理对象都要进行修改。

JDK动态代理

JDK动态代理利用了JDK反射机制,动态地在内存中构建代理对象,从而实现对目标对象的代理功能。

它主要用到了两个反射类的API:

  • java.lang.reflect Proxy| static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h):返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序

  • java.lang.reflect InvocationHandler|Object invoke(Object proxy, Method method, Object[] args) :在代理实例上调用目标方法,并返回结果。

我们来看看使用JDK动态代理之后的客服代理场景。

图片

JDK动态代理类图

  • 接口类:ISolver

    public?interface?ISolver?{
    ????void?solve();
    }
    
  • 目标类:Solver,目标类需要实现接口类。

    public?class?Solver?implements?ISolver?{
    ????@Override
    ????public?void?solve()?{
    ????????System.out.println("疯狂掉头发解决问题……");
    ????}
    }
    
  • 动态代理工厂:ProxyFactory,这里的动态代理工厂,不需要实现接口,直接采用反射的方式生成一个目标对象的代理对象实例。

    ps:这里用了一个匿名内部类的方法,还有一种方法,动态代理类实现InvocationHandler接口,大体上类似,就不再给出例子了。

    public?class?ProxyFactory?{
    
    ????//?维护一个目标对象
    ????private?Object?target;
    
    ????public?ProxyFactory(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("请问有什么可以帮到您?");
    
    ????????????????????????//?调用目标对象方法
    ????????????????????????Object?returnValue?=?method.invoke(target,?args);
    
    ????????????????????????System.out.println("问题已经解决啦!");
    ????????????????????????return?null;
    ????????????????????}
    ????????????????});
    ????}
    }
    
    
  • 客户端:Client

    客户端生成一个代理对象实例,通过代理对象调用目标对象方法的时候,就会进入invoke()方法,最后是通过反射的方式调用目标对象的方法。

    public?class?Client?{
    ????public?static?void?main(String[]?args)?{
    ????????//目标对象:程序员
    ????????ISolver?developer?=?new?Solver();
    ????????//代理:客服小姐姐
    ????????ISolver?csProxy?=?(ISolver)?new?ProxyFactory(developer).getProxyInstance();
    ????????//目标方法:解决问题
    ????????csProxy.solve();
    ????}
    }
    
  • 运行结果:

    请问有什么可以帮到您?
    疯狂掉头发解决问题……
    问题已经解决啦!
    

我们简单总结一下静态代理和动态代理的主要区别:

图片

静态代理动态代理最主要区别

  • 静态代理在编译时就已经实现,编译完成后代理类是一个实际的class文件

  • 动态代理是在运行时动态生成的,即编译完成后没有实际的class文件,而是在运行时动态生成类字节码,并加载到JVM中

我们也观察到,JDK动态代理,目标对象必须得实现接口,也就是说它是面向接口的,假如我们不想要接口怎么办呢?

Cglib动态代理

CGLIB(Code Generation Library)是一个基于ASM的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成,它是通过继承来实现的。

我们来看看使用Cglib之后,我们的客服代理是什么样的:

图片

Cglib动态代理类图

  • 引入依赖:Cglib是第三方类库,需要引入依赖

    ????????<dependency>
    ????????????<groupId>cglib</groupId>
    ????????????<artifactId>cglib</artifactId>
    ????????????<version>3.2.5</version>
    ????????</dependency>
    
  • 目标类:Solver,这里目标类不用再实现接口。

    public?class?Solver?{
    
    ????public?void?solve()?{
    ????????System.out.println("疯狂掉头发解决问题……");
    ????}
    }
    
  • 动态代理工厂:

    public?class?ProxyFactory?implements?MethodInterceptor?{
    
    ???//维护一个目标对象
    ????private?Object?target;
    
    ????public?ProxyFactory(Object?target)?{
    ????????this.target?=?target;
    ????}
    
    ????//为目标对象生成代理对象
    ????public?Object?getProxyInstance()?{
    ????????//工具类
    ????????Enhancer?en?=?new?Enhancer();
    ????????//设置父类
    ????????en.setSuperclass(target.getClass());
    ????????//设置回调函数
    ????????en.setCallback(this);
    ????????//创建子类对象代理
    ????????return?en.create();
    ????}
    
    ????@Override
    ????public?Object?intercept(Object?obj,?Method?method,?Object[]?args,?MethodProxy?proxy)?throws?Throwable?{
    ????????System.out.println("请问有什么可以帮到您?");
    ????????//?执行目标对象的方法
    ????????Object?returnValue?=?method.invoke(target,?args);
    ????????System.out.println("问题已经解决啦!");
    ????????return?null;
    ????}
    
    }
    
  • 客户端:Client

    public?class?Client?{
    ????public?static?void?main(String[]?args)?{
    ????????//目标对象:程序员
    ????????Solver?developer?=?new?Solver();
    ????????//代理:客服小姐姐
    ????????Solver?csProxy?=?(Solver)?new?ProxyFactory(developer).getProxyInstance();
    ????????//目标方法:解决问题
    ????????csProxy.solve();
    ????}
    }
    
  • 运行结果

    请问有什么可以帮到您?
    疯狂掉头发解决问题……
    问题已经解决啦!
    

我们可以看到Cglib动态代理和JDK动态代理最大的区别就是:

  • 使用JDK动态代理的对象必须实现一个或多个接口

  • 使用Cglib动态代理的对象则无需实现接口,达到代理类无侵入。

我们还需要注意:

  • CGLib不能对声明为final的方法进行代理,因为是通过继承父类的方式实现,如果父类是final的,那么就无法继承父类。

文章来源:https://blog.csdn.net/qq_39940634/article/details/134846544
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。