zoukankan      html  css  js  c++  java
  • Java动态代理源码

    对于java代理和反射之间的理解
    首先需要明白概念:什么为反射,反射为一种机制,我们可以动态的获取类的所有参数
    反射可以用来做什么?现在最常用的设计模式之一的代理模式底层就是通过代理来实现的。
    代理:用一个类来代替另一个类执行操作。
    静态代理和动态代理的区别就是一个是因为有代理类,在编译期就已经确定了这个类为谁的代理类;
    动态代理则是可以随着程序员的选择手动选择代理某个类。
    首先先讲一下静态代理。为了符合面向接口编程,创建一个接口来抽象规定用户的某个行为

    public interface User {
    void eat();
    }


    现在有两个普通的User类实现这个接口。

    public class Rose implements User {
    @Override
    public void eat() {
    System.out.println("我是Rose,我在吃早餐");
    }
    }
    public class Tom implements User {
    @Override
    public void eat() {
    System.out.println("我是tom,我在吃苹果");
    }
    }


    如果想执行这两个用户的操作那肯定就是new Tom().eat之类的,如果现在有需求,要在执行eat方法前先洗手,静态代理的做法如下
    先创建一个类来代理tom

    public class TomProxy implements User {
    private Tom tom;
    
    public TomProxy(Tom tom) {
    this.tom = tom;
    }
    
    @Override
    public void eat() {
    //执行前置加强
    System.out.println("先洗手");
    //执行委托类的方法
    tom.eat();
    //进行后置加强
    System.out.println("吃饱了呀");
    }
    }

    创建代理类的时候传入需要代理类的参数,这样需要Tom吃饭的时候直接创建代理类对象来替他执行
    new TomProxy(new Tom()).eat;这样会先执行前置加强再执行代理类的方法,最后执行后置加强
    因为手动创建了一个类,其实编译期在编译期就已经知道了哪些类是代理类,这就是静态代理。

    动态代理:
    动态代理也叫jdk代理,我们将User接口不变,Rose和Tom类也不变,新建一个类LoadHandler,对外提供一个构造方法,参数为需要代理的类实例

    public class LoadHandler {
    
    public Object newProxyInstance(Object targetObject) {
    //代理的是接口,不是实现类
    /** @param loader :the class loader to define the proxy class
    * @param interfaces :the list of interfaces for the proxy class
    * to implement
    * @param h :the invocation handler to dispatch method invocations to */
    
    
    return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), new InvocationHandler() {
    @Override
    
    /**
    * @param proxy: the proxy instance that the method was invoked on
    *
    * @param method: the {@code Method} instance corresponding to
    * the interface method invoked on the proxy instance. The declaring
    * class of the {@code Method} object will be the interface that
    * the method was declared in, which may be a superinterface of the
    * proxy interface that the proxy class inherits the method through.
    *
    * @param args: an array of objects containing the values of the
    * arguments passed in the method invocation on the proxy instance,
    * or {@code null} if interface method takes no arguments.
    * Arguments of primitive types are wrapped in instances of the
    * appropriate primitive wrapper class, such as
    * {@code java.lang.Integer} or {@code java.lang.Boolean}.
    */
    
     
    
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    System.out.println("先洗手");
    //通过代理,调用动态代理对象要执行的方法
    //如果有返回值,这里Object为空
    Object o=method.invoke(targetObject, args);
    //这里写方法执行之后的操作
    System.out.println("吃饱了");
    //没有返回值,直接return
    return null;
    }
    });
    }
    }

    这个动态代理的用法我们肯定知道了,创建一个LoadHandler,传入需要代理的类的实例,返回对象直接调用方法就行了
    new LoadHandler().newProxyInstance(new TomProxy()).eat();即可
    现在我们来分析里面的参数,方法名字可以看出为创建一个代理实例并返回
    我们现在分析一下参数
    loader :the class loader to define the proxy class
    loader参数定义的类的类加载器,可以通过对象.getclass().getClassLoader()来获得

    interfaces :the list of interfaces for the proxy class to implement
    interfaces参数为代理类要实现的接口列表

    h:invocations to the specified invocation handler.
    h参数为调用处理程序,将方法调用分派到指定的调用处理程序。
    怎么理解?就是使用我们自己写的调用处理程序来代替方法执行,之前的方法会分派到我们写的调用处理程序里
    new InvocationHandler(); 这是一个接口,需要重写invoke方法,有三个参数
    @param proxy :the proxy instance that the method was invoked on
    proxy:调用方法的代理实例 在这里就是targetObject
    @param method:the {@code Method} instance corresponding to
    the interface method invoked on the proxy instance.
    与代理实例上调用的接口方法相对应的实例,在这里就是eat了。

    @param args :an array of objects containing the values of the
    * arguments passed in the method invocation on the proxy instance,
    * or {@code null} if interface method takes no arguments.
    * Arguments of primitive types are wrapped in instances of the
    * appropriate primitive wrapper class, such as
    * {@code java.lang.Integer} or {@code java.lang.Boolean}.
    一个对象数组,包含代理实例的方法调用中传递的参数值,如果接口方法不接受任何参数,就为null
    而且原始类型的参数要包装,比如Integer。什么意思?就是执行eat方法需要调用的参数,没参数就为Null

    java.lang.reflect.Method里对这个invoke的解释如下
    Invokes the underlying method represented by this {@code Method}
    * object, on the specified object with the specified parameters.
    * Individual parameters are automatically unwrapped to match
    * primitive formal parameters, and both primitive and reference
    * parameters are subject to method invocation conversions as
    * necessary.
    在具有指定参数的指定对象上调用此method对象表示的基础方法,各个参数将自动解包以匹配原始形式参数
    这里method其实就等于eat方法,method.invoke(对象,参数)可以理解为eat.invoke(对象,参数),这里为 什么对象执行eat方法,有什么参数。
    附上源码解读

    public static Object newProxyInstance(ClassLoader loader,
    Class<?>[] interfaces,
    InvocationHandler h)
    throws IllegalArgumentException
    {
    //判断h对象是否为空,可以理解为有没有指定的处理程序
    Objects.requireNonNull(h);
    //重点在这,动态代理是根据反射来创建的接口实例
    final Class<?>[] intfs = interfaces.clone();
    //获取程序的安全管理器
    final SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
    //检查创建代理类所需的权限
    checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
    }
    
    //生成指定的代理类,是根据接口来生成的哦,
    Class<?> cl = getProxyClass0(loader, intfs);
    
    //使用指定的调用处理程序调用代理类的构造函数
    try {
    if (sm != null) {
    //检查新的代理实例的许可
    checkNewProxyPermission(Reflection.getCallerClass(), cl);
    }
    // 反射!反射!获取指定的构造器
    final Constructor<?> cons = cl.getConstructor(constructorParams);
    final InvocationHandler ih = h;
    //不是公开的构造方法就破解
    if (!Modifier.isPublic(cl.getModifiers())) {
    AccessController.doPrivileged(new PrivilegedAction<Void>() {
    public Void run() {
    cons.setAccessible(true);
    return null;
    }
    });
    }
    //创建实例返回
    return cons.newInstance(new Object[]{h});
    //如果有异常抓取,并且抛出给控制台
    } catch (IllegalAccessException|InstantiationException e) {
    throw new InternalError(e.toString(), e);
    } catch (InvocationTargetException e) {
    Throwable t = e.getCause();
    if (t instanceof RuntimeException) {
    throw (RuntimeException) t;
    } else {
    throw new InternalError(t.toString(), t);
    }
    } catch (NoSuchMethodException e) {
    throw new InternalError(e.toString(), e);
    }
    }
    不和别人一样,不复制只真正理解
  • 相关阅读:
    数据库系统概论习题集 第八章 数据库并发控制
    数据库系统概论习题集 第七章 数据库恢复技术
    数据库系统概论习题集 第六章 数据库设计
    数据库系统概论习题集 第五章 关系数据理论
    数据库系统概论习题集 第四章 关系系统及其优化
    数据库系统概论习题集 第三章 SQL语言
    【转载】【笔记】vue-router之路由传递参数
    【Dos】复制指定文件夹下所有文件到另外指定文件夹下
    【转载】Vue.js 安装及其环境搭建
    【转载】解决:'webpack-dev-server' 不是内部或外部命令,也不是可运行的程序 或批处理文件。
  • 原文地址:https://www.cnblogs.com/Vinlen/p/12749867.html
Copyright © 2011-2022 走看看