JDK动态代理两种方式

JDK动态代理两种方式

  • JDK动态代理(基于接口的动态代理)

    • 优点:

      • 解耦合(Decoupling):动态代理允许你将代理逻辑和实际业务逻辑分开,从而实现代码的解耦合。这可以使代码更容易维护和扩展。

      • 代码重用:通过使用动态代理,你可以在不修改原始类的情况下,为多个类添加相同的代理行为。这种代码重用可以减少冗余代码的编写。

      • 横切关注点(Cross-Cutting Concerns):动态代理可以用于实现横切关注点,如日志记录、事务管理、权限控制等。这样可以将这些关注点从业务逻辑中分离出来,提高了代码的可维护性。

      • 灵活性:动态代理在运行时生成代理类,这使得你可以在不同的情况下使用不同的代理逻辑。你可以在运行时决定要应用的代理行为,而无需修改原始类。

      • 减少重复代码:如果多个类需要相同的代理逻辑,动态代理可以避免在每个类中都编写相同的代码,从而减少了重复劳动。

    • 缺点:

      • 性能损失:动态代理通常会引入额外的运行时开销,包括代理类的生成和方法调用的拦截。虽然现代的动态代理机制尽可能优化了性能,但在某些高性能场景下,性能损失可能会有所影响。

      • 只能代理接口方法:Java的动态代理只能代理接口中的方法,无法代理类的非接口方法。这意味着如果目标类没有实现接口,或者你想代理非接口方法,就需要考虑其他方法,比如CGLIB代理。

      • 限制于public方法:动态代理只能代理公共(public)方法,无法代理私有(private)方法。这可能会限制你在一些特定情况下的代理能力。

      • 复杂性增加:在一些复杂的场景下,动态代理可能会增加代码的复杂性。代理逻辑、代理接口、实际业务逻辑等之间的交互可能会导致代码难以理解。

    • 案例:

      • 要求:被代理类至少实现一个接口

      1. 接口标准

        • /*
          技能类要实现的接口,即标准
          * */
          public interface ISkills {
              void shanxian(String user);
              void zhansha(String user);
          }

      2. 接口实现类

        • ​
          /*
          * 实现英雄技能类
          * */
          public class Skills implements ISkills {
              @Override
              public void shanxian(String user) {
                  System.out.println(user + " 使用闪现追击");
              }
          ​
              @Override
              public void zhansha(String user) {
                  System.out.println(user + " 斩杀了英雄");
              }
          }

      3. 主方法

        • public class Users {
              public static void main(String[] args) {
                  # 实例化接口类
                  final Skills skills = new Skills();
          ​
                  /*
                  * 涉及的类:Proxy
                   * 创建代理对象的方法:newProxyInstance()
                   * 该方法的参数:
                   *      ClassLoader:;类加载器。和被代理对象使用相同的类加载器
                   *      Class[]:字节码数组。被代理类实现的接口,要求代理对象和被代理对象具有相同的行为
                   *      InvocationHandler:用于我们提供增强代码的接口。一般会写一个该接口的实现类。
                   *                        实现类可以是匿名内部类。它的含义就是如何代理。这里的代码只能是谁用谁提供
                   */
          ​
                  ISkills  proxySkills = (ISkills) Proxy.newProxyInstance(skills.getClass().getClassLoader(), skills.getClass().getInterfaces(),
                          new InvocationHandler() {
                              @Override
                              public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                                 String name = null;
                                  String user = (String) args[0];
                                  if ("shanxian".equals(method.getName( ) ) ){
                                      name = (String) method.invoke(skills, user);
                                  }
                                  if ("zhansha".equals(method.getName( ) ) ){
                                      name = (String) method.invoke(skills, user);
                                  }
                                  return name;
                              }
                          });
                  proxySkills.shanxian("鲁班");
                  proxySkills.zhansha("亚索");
          ​
              }
  • 简单CGLIB动态代理

      • CGLib是一个强大、高性能的字节码生成库, 使用CGLib实现动态代理, 完全不受代理类必须实现接口的限制。CGLib底层采用的是ASM字节码生成框架。使用字节码技术生成代理类,比java的反射效率还要高。CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类

      • CGLib包结构:

        • net.sf.cglib.core 底层字节码处理类。

        • net.sf.cglib.transform 该包中的类用于class文件运行时转换或编译时转换。

        • net.sf.cglib.proxy 该包中的类用于创建代理和方法拦截。

        • net.sf.cglib.reflect 该包中的类用于快速反射,并提供了C#风格的委托。

        • net.sf.cglib.util 集合排序工具类。

        • net.sf.cglib.beans JavaBean工具类。

      • CGLib动态代理相关的基础类:

        • net.sf.cglib.proxy.Enhancer 主要的增强类。

        • net.sf.cglib.proxy.MethodInterceptor 主要的方法拦截类,它是Callback接口的子接口,需要用户实现。

        • net.sf.cglib.proxy.MethodProxy JDK的java.lang.reflect.Method类的代理类,可以方便的实现对源对象方法的调用。

      • 案例:

        1. 被代理的类

          // 创建一个普通类做为代理类
          public class Person {
              //  代理类中由普通方法
              public void eat() {
                  System.out.println("我要开始吃饭咯...");
              }
          ​
              public void play() {
                  System.out.println("我要出去玩耍了,,,");
              }
          }

        2. 拦截器

          public class MyInterceptor implements MethodInterceptor {
              @Override
              public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                  System.out.println("吃饭前我会先洗手"); // 此处可以做一些操作
                  // Object result=proxy.invokeSuper(o,args); 表示调用原始类的被拦截到的方法。这个方法的前后添加需要的过程。
                  // 在这个方法中,我们可以在调用原方法之前或之后注入自己的代码
                  Object result = methodProxy.invokeSuper(object, args);
                  System.out.println("吃完饭我会先休息会儿" );  // 方法调用之后也可以进行一些操作
                  return result;
          ​
              }
          }
          ​

        3. 主方法

                 /*
                  * void setSuperclass(java.lang.Class superclass) 设置产生的代理对象的父类。
                  * void setCallback(Callback callback) 设置CallBack接口的实例。
                  * void setCallbacks(Callback[] callbacks) 设置多个CallBack接口的实例。
                  * void setCallbackFilter(CallbackFilter filter) 设置方法回调过滤器。
                  * Object create() 使用默认无参数的构造函数创建目标对象。
                  * Object create(Class[], Object[]) 使用有参数的构造函数创建目标对象。
                  * 参数Class[] 定义了参数的类型,第二个Object[]是参数的值。
                  *
                  * */
                  Enhancer enhancer = new Enhancer();
                  enhancer.setSuperclass(Person.class);
                  enhancer.setCallback(new MyInterceptor());
                  Person person = (Person) enhancer.create();
                  person.eat();
          ​
          ​

  • 复杂的CGLib动态代理

      • 此时我想要在吃饭和玩耍前后都要进行一些操作,我们先看完整代码

    • 完整代码:

      • package org.example;
        ​
        import net.sf.cglib.proxy.*;
        ​
        import java.lang.reflect.Method;
        ​
        public class test1 {
            public static void main(String[] args) {
        ​
                // 定义一个回调接口的数组
                Callback[] callbacks = new Callback[] {
                        new MyApiInterceptor(), new MyApiInterceptorForPlay()
                };
        ​
                Enhancer enhancer = new Enhancer();
                enhancer.setSuperclass(Person.class); // 设置要代理的父类
                enhancer.setCallbacks(callbacks); // 设置回调的拦截器数组
        ​
                enhancer.setCallbackFilter(new CallbackFilterImpl()); // 设置回调选择器
                Person person = (Person) enhancer.create(); // 创建代理对象
        ​
                person.eat();
                System.out.println("--------------------");
                person.play();
            }
        }
        ​
        class MyApiInterceptor implements MethodInterceptor {
        ​
            @Override
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                System.out.println("吃饭前我会先洗手"); // 此处可以做一些操作
                Object result = proxy.invokeSuper(obj, args);
                System.out.println("吃完饭我会先休息会儿" );  // 方法调用之后也可以进行一些操作
                return result;
            }
        }
        class MyApiInterceptorForPlay implements MethodInterceptor {
        ​
            @Override
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                System.out.println("出去玩我会先带好玩具"); // 此处可以做一些操作
                Object result = proxy.invokeSuper(obj, args);
                System.out.println("玩一个小时我就回家了" );  // 方法调用之后也可以进行一些操作
                return result;
            }
        }
        ​
        class CallbackFilterImpl implements CallbackFilter {
            @Override
            public int accept(Method method) {
                if (method.getName().equals("play"))
                    return 1;
                else
                    return 0;
            }
        }
        ​
        ​
        // 创建一个普通类做为代理类
        class Person {
            //  代理类中由普通方法
            public void eat() {
                System.out.println("我要开始吃饭咯...");
            }
        ​
            public void play() {
                System.out.println("我要出去玩耍了,,,");
            }
        }
      • CallbackFilter可以实现不同的方法使用不同的回调方法。CallbackFilter中的accept方法, 根据不同的method返回不同的值i, 这个值是在callbacks中的顺序, 就是调用了callbacks[i]

      • 增加定义有一个回调接口的数组

        // 定义一个回调接口的数组
        Callback[] callbacks = new Callback[] {
        new MyApiInterceptor(), new MyApiInterceptorForPlay()
        };
      • 实现多个拦截

        ​
        class MyApiInterceptorForPlay implements MethodInterceptor {
         
            @Override
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                System.out.println("出去玩我会先带好玩具"); // 此处可以做一些操作
                Object result = proxy.invokeSuper(obj, args);
                System.out.println("玩一个小时我就回家了" );  // 方法调用之后也可以进行一些操作
                return result;
            }
        }
        ​

        参考文献:动态代理之 cglib 实现_温柔狠角色的博客-CSDN博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值