• 静态代理与动态代理


    简介

    代理模式作用是:在不修改被代理对象功能的基础上,通过对代理类进行扩展,进行一些功能上的附加与增强。如Spring AOP就是用动态代理来实现的。下图可以表述代理模式的流程:

     

    静态代理

    我们在app上看电影的时候会看到很多广告,app厂家拿到电影的代理权限后,再通过穿插各种广告来增加自己的收益,下面通过代理模式来模拟一下

    创建代表电影播放的接口 

    1. public interface Movie {
    2. void play();
    3. }

    实现电影的播放接口

    1. public class MovieProxy implements Movie {
    2. private String movieName;
    3. public MovieProxy(String movieName) {
    4. this.movieName = movieName;
    5. }
    6. @Override
    7. public void play() {
    8. System.out.println("正在播放" + movieName);
    9. }
    10. }

    创建电影的代理类,表示上述例子中的app 

    1. public class Player implements Movie {
    2. private String movieName;
    3. public Player(String movieName) {
    4. this.movieName = movieName;
    5. }
    6. @Override
    7. public void play() {
    8. System.out.println("片头广告....");
    9. MovieProxy movieProxy = new MovieProxy(movieName);
    10. movieProxy.play();
    11. System.out.println("片尾广告....");
    12. }
    13. }

    测试类

    1. public class Test {
    2. public static void main(String[] args) {
    3. Player player = new Player("功夫熊猫");
    4. player.play();
    5. }
    6. }

    结果

    以上就是静态代理的实现,通过拓展代理类进行一些功能的拓展或增强 ,什么叫静态代理呢?就是增强的内容是事先定义好的,如果需要在运行时进行增强就需要用到动态代理。

    动态代理

    JDK动态代理

    JDK动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理,用JDK动态代理模拟看电影的例子

    代码演示

    创建接口,JDK代理中被代理的对象必须实现接口

    1. public interface Movie {
    2. void play();
    3. }

    接口实现类

    1. public class MyMovie implements Movie {
    2. @Override
    3. public void play() {
    4. System.out.println("正在播放电影");
    5. }
    6. }

    测试类

    1. public class Test {
    2. public static void main(String[] args) {
    3. // 保存动态代理生成的文件
    4. System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
    5. ClassLoader classLoader = MyMovie.class.getClassLoader();
    6. Class[] interfaces = MyMovie.class.getInterfaces();
    7. // proxy代理对象本身
    8. // method被执行的方法
    9. // args1方法参数
    10. Movie movie = (Movie) Proxy.newProxyInstance(classLoader, interfaces, (proxy, method, args1) -> {
    11. MyMovie myMovie = new MyMovie();
    12. System.out.println("片头广告");
    13. Object invoke = method.invoke(myMovie, args1);
    14. System.out.println("片尾广告");
    15. return invoke;
    16. });
    17. movie.play();
    18. }
    19. }

    源码分析

    newProxyInstance方法

    在此方法中可以分为三个步骤

    1)查找或生成指定的代理接口类,如果将sun.misc.ProxyGenerator.saveGeneratedFiles设置为true,则会在com.sun.proxy路径下保存生成的代理类

     2)获取构造方法

    3)利用反射实例化代理类

     

    CGLIB代理 

    cglib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。

    代码演示

     创建被代理类 

    1. public class Movie {
    2. public void play() {
    3. System.out.println("播放电影");
    4. }
    5. }

    创建回调方法

    1. public class MyMethodInterceptor implements MethodInterceptor {
    2. @Override
    3. public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    4. System.out.println("片头广告");
    5. Object o1 = methodProxy.invokeSuper(o, objects);
    6. System.out.println("片尾广告");
    7. return o1;
    8. }
    9. }

    测试类

    1. public class Test {
    2. public static void main(String[] args) {
    3. // 生成的代理类存储路径
    4. System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,".\\code");
    5. // 在创建Enhancer时会通过动态代理将EnhanceKey创建好,
    6. Enhancer enhancer = new Enhancer();
    7. // 设置父类
    8. enhancer.setSuperclass(Movie.class);
    9. // 设置回调方法
    10. enhancer.setCallback(new MyMethodInterceptor());
    11. // 创建代理类
    12. Movie movie = (Movie) enhancer.create();
    13. movie.play();
    14. }
    15. }

    总结

    JDK和CGLIB动态代理的区别:

    1. JDK代理使用的是反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
      CGLIB代理使用字节码处理框架asm,对代理对象类的class文件加载进来,通过修改字节码生成子类。
    2. JDK创建代理对象效率较高,执行效率较低;
      CGLIB创建代理对象效率较低,执行效率高。
    3. JDK动态代理机制是委托机制,只能对实现接口的类生成代理,通过反射动态实现接口类;
      CGLIB则使用的继承机制,针对类实现代理,被代理类和代理类是继承关系,所以代理类是可以赋值给被代理类的,因为是继承机制,不能代理final修饰的类。
  • 相关阅读:
    2023工博会强势回归!智微工业携八大系列重磅亮相
    请求服务器数据API
    elasticsearch+kibana部署
    android 在后台任务栏隐藏应用程序
    【微服务开篇-RestTemplate服务调用、Eureka注册中心、Nacos注册中心】
    速盾:cdn测试工具
    STM32——DHT11温湿度传感器
    数据结构之< AVLTree >
    Docker镜像详解(手拉手教你上传至阿里云,发布到私有库)
    基于fast-Unet的补强胶胶体在线识别分割技术
  • 原文地址:https://blog.csdn.net/qq_35597828/article/details/126287829