java代理详解

1. 什么是代理模式

代理模式:为其他对象提供一种代理以控制对这个对象的访问。

2. 代理的作用

代理作用:

  1. 使目标对象和调用者解耦
  2. 可以对目标对象进行功能加强扩展

代理模式的作用场景:

  1. 当调用者不适合直接调用目标对象的时候,可以使用代理对象简介访问目标对象
  2. 当目标对象调用时需要做一些额外的扩展功能,但是直接修改代码不适合,可以使用代理对象对目标对象调用的方法进行加强。

代理模式分为静态代理和动态代理。

3. 静态代理

静态代理:在编译的时候就确定了代理类和目标类之间的关系,代理类在编译之前就存在。

静态代理的实现思路:

​ 新建一个接口,让目标类和代理类同时实现接口中的方法,在代理类中增加一个对目标类的引用,代理类在实现接口方法的过程中调用目标类的引用调用其接口实现,同时在其调用的前后进行一些功能的扩展。

静态代理的代码实例:

  1. 目标接口类Consumer

    public interface Consumer {
        void rent();
    }
    
  2. 目标类User

    public class User implements Consumer {
        public User() {
        }
    
        public void rent() {
            System.out.println("用户付款,签订租房合同");
        }
    }
    
  3. 代理类UserProxy

    public class UserProxy implements Consumer {
        private Consumer consumer;
    
        public UserProxy(Consumer consumer) {
            this.consumer = consumer;
        }
    
        public void rent() {
            System.out.println("房屋中介开始找房子");
            System.out.println("房屋中介打电话通知用户");
            this.consumer.rent();
            System.out.println("成功租到房子!!");
        }
    }
    
  4. 测试类StaticProxyTest

    public class StaticProxyTest {
        public StaticProxyTest() {
        }
    
        public static void main(String[] args) {
            UserProxy userProxy = new UserProxy(new User());
            userProxy.rent();
        }
    }
    

静态代理的总结:

  1. 特点:
    • 代理类在编译之前就已经确定
    • 代理类服务于一种类型的目标类
    • 代理类和目标类都要实现相同的接口
  2. 优点:比较简单实现,适用于功能固定的场景
  3. 缺点:代码不易于管理、很容易造成代码的冗余、不够灵活和代码维护

4. 动态代理

动态代理:在程序运行的时候创建代理类。

动态代理分为两种:

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

  • cglib动态代理(基于类的代理)

    1. jdk动态代理

    jdk动态代理是基于接口的动态代理,jdk动态代理的实现主要是依赖于两个类:InvocationHandler和Proxy,其中InvocationHandler类的解释:

    /**
     * {@code InvocationHandler} is the interface implemented by
     * the <i>invocation handler</i> of a proxy instance.
     *该接口是被代理实例实现的一个调用处理程序。
     * <p>Each proxy instance has an associated invocation handler.
     * When a method is invoked on a proxy instance, the method
     * invocation is encoded and dispatched to the {@code invoke}
     * method of its invocation handler.
     每个代理实例都有一个于其关联的调用处理,当代理实例的方法调用的时候,该方法的调用会被编码并且分配到调用处理器。
     */
    public interface InvocationHandler {
    
        /**
         * Processes a method invocation on a proxy instance and returns
         * the result.  This method will be invoked on an invocation handler
         * when a method is invoked on a proxy instance that it is
         * associated with.该方法会在一个代理实例上调用并且返回结果。当invocationHandler所关联的代理实例调用方法的时候这	   *	个方法会在调用处理器上被调用
         * proxy:代理实例对象
           method:调用的目标对象的方法对象
           args:目标方法的传参对象
         */
        public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable;
    }
    

    proxy类的解释:

    /**
    * {@code Proxy} provides static methods for creating dynamic proxy
     * classes and instances, and it is also the superclass of all
     * dynamic proxy classes created by those methods.
     提供创建动态代理类和实例的静态方法,同时该类是所有通过这些静态方法创建的动态代理类的超类
    */
    public class Proxy implements java.io.Serializable {
        /**
        loader:类加载器,定义由哪个类加载器加载对应的代理类
        interfaces:接口数组,代理对象将要实现的接口,我们提供这些接口,代理类会实现这些接口中的方法,我们可以通过代理类调用这些方法
        h:调用处理类,表明当前的代理对象关联的调用处理器是哪一个,最后在代理类调用其接口实现方法的时候,会调用该调用处理器的方法完成接口的扩展。
        */
         @CallerSensitive
        public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
            throws IllegalArgumentException
        {}
    }
    

    jdk代理的实现思路:

    ​ 新建一个接口,目标类实现该接口,创建一个InvocationHandler的实现类,完成对该接口方法的扩展,使用时候用Proxy的静态方法newProxyInstance创建一个代理实例,调用接口方法。

    jdk动态代理的代码实例:

    1. 目标接口类Consumer

      public interface Consumer {
          public void rent();
      }
      
    2. 目标类User

      public class User implements Consumer {
      
          @Override
          public void rent() {
              System.out.println("用户付款,签订租房合同");
          }
      }
      
    3. InvocationHandler的实现类UserProxy

      public class UserProxy implements InvocationHandler {
          private Object target;
      
          public UserProxy(Object target) {
              this.target = target;
          }
      
          @Override
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
              System.out.println("房屋中介开始找房子");
              System.out.println("房屋中介打电话通知用户");
              Object invoke = method.invoke(target, args);
              System.out.println("成功租到房子!!");
              return invoke;
          }
      }
      
    4. 测试类JdkProxyTest

      public class JdkProxyTest {
          public static void main(String[] args) {
              UserProxy userProxy = new UserProxy(new User());
              Consumer consumer = (Consumer) Proxy.newProxyInstance(userProxy.getClass().getClassLoader(), new Class[]{Consumer.class}, userProxy);
              consumer.rent();
          }
      }
      
    5. cglib动态代理

      ​ cglib动态代理私基于类的动态代理。cglib是通过动态生成一个子类去覆盖目标类中的非final方法,并且设置号callback,则原有的每个方法调用就会转变成调用用户定义的拦截方法intercept。

      cglib中的相关类MethodInterceptor和Enhancer:

      /**
      var1:代理对象
      var2:代理方法
      var3:传参
      var4:代理父类方法
      */
      调用目标方法的任意非final方法都会调用该方法
      public interface MethodInterceptor extends Callback {
          Object intercept(Object var1, Method var2, Object[] var3, MethodProxy var4) throws Throwable;
      }
      

      Enhancer类的解释:

      /**The dynamically
       * generated subclasses override the non-final methods of the superclass and
       * have hooks which callback to user-defined interceptor
       * implementations.
       动态生成子类覆盖父类的非final方法,通过钩子回调到用户自定义的拦截实现
       */
       public class Enhancer extends AbstractClassGenerator {
       // 创建一个新类
       	public Object create() {}
       }
      

      cglib代理的实现思路:

      ​ 新建一个MethodInterceptor的实现类,调用Enhancer传入目标类类型和MethodInterceptor的实现类创建代理类。

      cglib代理的代码实现:

      1. 目标类User

        public class User {
        
            public void rent1() {
                System.out.println("用户付款,签订租房合同1");
            }
        }
        
      2. MethodInterceptor的实现类CglibUserInterceptor

        public class CglibUserInterceptor implements MethodInterceptor {
        
            public Object getProxy(Class t) {
                Enhancer enhancer = new Enhancer();
                // 目标类的class
                enhancer.setSuperclass(t);
                // 设置代理类的回调
                enhancer.setCallback(this);
                return enhancer.create();
            }
        
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("房屋中介开始找房子");
                System.out.println("房屋中介打电话通知用户");
                Object invoke = methodProxy.invokeSuper(o, objects);
                System.out.println("成功租到房子!!");
                return invoke;
            }
        }
        
      3. 测试类CglibTest

        public class CglibTest {
            public static void main(String[] args) {
                CglibUserInterceptor cglibUserInterceptor = new CglibUserInterceptor();
                User proxy = (User)cglibUserInterceptor.getProxy(User.class);
                proxy.rent1();
        
            }
        }
        

        jdk代理和cglib代理的区别:

        1. jdk使用于实现接口的类,cglib通过继承来实现,该类应该为非final类型,方法是非final和private的
        2. jdk和cglib都是在运行时生成代理类的字节码,但是jdk是直接写字节码,cglib是使用asm生成字节码,更加复杂,生成代理对象jdk效率比cglib要快
        3. jdk调用代理方法是通过反射,但是cglib是通过FastClass机制直接调用方法,cglib执行效率较高

5. 静态代理和动态代理的区别

静态代理在编译时就已经实现,编译完成后代理类是一个实际的class文件。
动态代理是在运行时动态生成的,即编译完成后没有实际的class文件,而是在运行时动态生成类字节码,并且加载到JVM中。

6. 总结

java代理推荐使用动态代理,jdk动态代理基于反射,只能对实现接口的目标类生成代理,cglib动态代理基于继承,对于非final修饰的类生成代理。如果目标对象创建比较频繁,推荐使用jdk代理,jdk创建代理比较快,如果目标对象创建比较少,但是调用比较多,推荐使用cglib代理,cglib代理的代理方法执行效率高。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值