Java代理初探

在日常的开发中,需求的变动是很正常的,而且也是经常发生的,比如在我们的开发中有如下一个接口

public interface UserService {
    User query(User user);
    void save(User user);
}

有如下的实现类,最开始的实现是这样的。

public class UserServiceImpl implements UserService {
    @Override
    public User query(User user) {
        System.out.println("查询用户成功:" + user.getName());
        return user;
    }
    @Override
    public void save(User user) {
        System.out.println("保存用户成功:" + user.getName());
    }
}

如果某一天,需求发生变化了,需要在每一个方法执行前都加上 验证权限 和在每一个方法 执行完成后 都添加上 操作日志 的业务逻辑,那你说很简单呀,直接像下面一样改动就好了。

public class UserServiceImpl implements UserService {
    @Override
    public User query(User user) {
        System.out.println("验证权限!");
        System.out.println("查询用户成功:" + user.getName());
        System.out.println("记录操作日志!");
        return user;
    }
    @Override
    public void save(User user) {
        System.out.println("验证权限!");
        System.out.println("保存用户成功:" + user.getName());
        System.out.println("记录操作日志!");
    }
}

但是这样的改动其实存在一些问题的:

1、我们需要对每一个接口的每一个方法都添加相应的逻辑,而且代码都大同小异的,这就会导致代码重复严重

2、验权和记录操作日志这种操作其实是通用的问题,不应该和具体的方法,具体的接口耦合在一起,可以剥离出来形成单独的框架

3、比如后续需要调整验证权限相关的逻辑,涉及到的改动很大

4、如果UserService是其他第三方开发的,我们无权修改代码,这个时候直接改原始代码就不行了

其实上面的问题可以使用代理模式很好的解决

接下来我们看下静态代理

其实静态代理比较简单,为每一个类生成对应的代理类,逻辑相对来说比较简单,直接上代码

public class UserServiceImplProxy implements UserService {
    private UserService userService;

    public UserServiceImplProxy(UserService userService) {
        this.userService = userService;
    }

    @Override
    public User query(User user) {
        System.out.println("验证权限!");

        //委托给原始的UserService执行
        User user1 = userService.query(user);
        
        System.out.println("记录操作日志!");
        return user1;
    }

    @Override
    public void save(User user) {
        System.out.println("验证权限!");
        userService.save(user);
        System.out.println("记录操作日志!");
    }
}

其实静态代理也是很繁琐的,我们需要为每一个类,每一个方法添加相应的操作。

接下来分析下JDK动态代理

1、首先我们来看下类Proxy

可以看到其实有两个构造函数,一个私有的构造函数,一个protected的构造函数

 /**
     * Prohibits instantiation.
     */
    private Proxy() {
    }

    /**
     * Constructs a new {@code Proxy} instance from a subclass
     * (typically, a dynamic proxy class) with the specified value
     * for its invocation handler.
     *
     * @param  h the invocation handler for this proxy instance
     *
     * @throws NullPointerException if the given invocation handler, {@code h},
     *         is {@code null}.
     */
    protected Proxy(InvocationHandler h) {
        Objects.requireNonNull(h);
        this.h = h;
    }

第二个构造函数接收一个InvocationHandler,我们再看看InvocationHandler是什么?

其实我们可以看到InvocationHandler是一个接口,就一个方法,invoke方法。

public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

下面我们基于动态代理实现上面的需求,首先定义一个InvocationHandler

public class MyInvocationHandler implements InvocationHandler {
        //代理对象
        private Object targerObject;

        public MyInvocationHandler(Object targetObject) {
            this.targerObject = targetObject;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //方法执行前逻辑
            System.out.println(method.getName() + ":鉴权开始!");
            
            //代理对象的方法
            Object result = method.invoke(targerObject, args);

            //执行后逻辑
            System.out.println(method.getName() + ":执行完成");
            System.out.println(method.getName() + ":已记录日志");
            return result;
        }
    }

接下来我们看下如何生成代理类,代码如下

public class UserServiceDynamicProxy {
    //被代理的类
    private Object targetObject;

    public UserServiceDynamicProxy(Object targetObject) {
        this.targetObject = targetObject;
    }

    //获得代理类
    public Object getProxyObject() {
        //获得实现的接口
        Class<?>[] interfaces = targetObject.getClass().getInterfaces();
        
        //获得InvocationHandler 
        InvocationHandler invocationHandler = new MyInvocationHandler(targetObject);
        return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), interfaces, invocationHandler);
    }
}

如何使用代理类

public class Test {
    public static void main(String[] args) {
        //设置saveGeneratedFiles值为true生成class字节码到文件,JDK8下,其他版本没试过
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

        User user = new User();
        user.setName("Ycb");
        //原始类
        UserService userService = new UserServiceImpl();

        //调用动态代理生成代理类
        UserService userServiceProxy = (UserService) new UserServiceDynamicProxy(userService).getProxyObject();

        //调用相关方法
        userServiceProxy.save(user);
    }
}

输出结果,其实我们看到,经过上面的InvocationHandler的创建,代理类的生成,以及最终的调用,已经实现了相关的效果,而且我们没有和具体的接口,具体的方法绑定,更加灵活,那是怎么达到我们的需求的?


其实我们可以通过反编译生成的动态代理的字节码文件看一下究竟,使用的是JDK8,可以添加上  System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); 就可以拿到生成的代理类的字节码文件了,其他版本的JDK,估计不能这样玩,没试过。

我们可以看下生成的代理类,如下。

package com.sun.proxy;

public final class $Proxy0 extends Proxy implements UserService {

   private static Method m1;
   private static Method m2;
   private static Method m4;
   private static Method m3;
   private static Method m0;


   public $Proxy0(InvocationHandler var1) throws  {
      super(var1);
   }

   public final boolean equals(Object var1) throws  {
      try {
         return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
      } catch (RuntimeException | Error var3) {
         throw var3;
      } catch (Throwable var4) {
         throw new UndeclaredThrowableException(var4);
      }
   }

   public final String toString() throws  {
      try {
         return (String)super.h.invoke(this, m2, (Object[])null);
      } catch (RuntimeException | Error var2) {
         throw var2;
      } catch (Throwable var3) {
         throw new UndeclaredThrowableException(var3);
      }
   }

   public final User query(User var1) throws  {
      try {
         return (User)super.h.invoke(this, m4, new Object[]{var1});
      } catch (RuntimeException | Error var3) {
         throw var3;
      } catch (Throwable var4) {
         throw new UndeclaredThrowableException(var4);
      }
   }

   public final void save(User var1) throws  {
      try {
         super.h.invoke(this, m3, new Object[]{var1});
      } catch (RuntimeException | Error var3) {
         throw var3;
      } catch (Throwable var4) {
         throw new UndeclaredThrowableException(var4);
      }
   }

   public final int hashCode() throws  {
      try {
         return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
      } catch (RuntimeException | Error var2) {
         throw var2;
      } catch (Throwable var3) {
         throw new UndeclaredThrowableException(var3);
      }
   }

   static {
      try {
         m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
         m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
         m4 = Class.forName("com.Ycb.proxy.UserService").getMethod("query", new Class[]{Class.forName("com.Ycb.proxy.User")});
         m3 = Class.forName("com.Ycb.proxy.UserService").getMethod("save", new Class[]{Class.forName("com.Ycb.proxy.User")});
         m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      } catch (NoSuchMethodException var2) {
         throw new NoSuchMethodError(var2.getMessage());
      } catch (ClassNotFoundException var3) {
         throw new NoClassDefFoundError(var3.getMessage());
      }
   }
}

我们通过这个代理类可以看到,其实这个代理是实现了 我们 需要代理的类实现的接口,比如上面的UserService接口,而且继承了Proxy类。

1、最开始定义了几个静态的Method,static静态块中通过反射获取了类相关的方法。

2、接下来可以看到一个构造函数,调用了super(var1),其实就是Proxy中的protected接口,传递的InvocationHandler接口就是我们调用Proxy.newProxyInstance时设置的。

public $Proxy0(InvocationHandler var1) throws  {
      super(var1);
   }

接下来就是一些方法,我们具体看save方法,其实代码很简单,就是一句super.h.invoke(this,m3,new Object[]{var1});

public final void save(User var1) throws  {
      try {
         super.h.invoke(this, m3, new Object[]{var1});
      } catch (RuntimeException | Error var3) {
         throw var3;
      } catch (Throwable var4) {
         throw new UndeclaredThrowableException(var4);
      }
   }

super.h不就是我们自己定义的InvovationHandler吗?m3就是前面所讲的方法,参数就是我们传递的参数。其实到这里动态代理的过程已经很清楚了。

其实通过上面的调用,我们可以看到,第一个参数proxy,传进来的是this,其实也就是生成的代理类实例,method其实就是我们调用的method,第三个就是参数,

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //方法执行前逻辑
            System.out.println(method.getName() + ":鉴权开始!");
            
            //代理对象的方法
            Object result = method.invoke(targerObject, args);

            //执行后逻辑
            System.out.println(method.getName() + ":执行完成");
            System.out.println(method.getName() + ":已记录日志");
            return result;
        }

所以我们在invoke方法中执行如下语句时要特别小心,对象别弄错。

//代理对象的方法
Object result = method.invoke(targerObject, args);

我们也可以看到JDK动态代理的弊端,被代理的类需要实现接口,那不实现接口可以代理吗?后续单独开博文来讲解,CGLIB,后续再更新啦

个人学习记录有错误麻烦指出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值