深入理解spring(动态代理源码2)(六)

总结与回顾(接上篇)
1、上一篇讲到我们自己实现动态代理。
1.1 自己模拟的动态代理

不需要手动创建类文件(因为一旦手动创建类文件,就会产生类爆炸),通过接口反射生成一个类文件,然后调用第三方的编译技术,动态编译这个产生的类文件成class文件,继而利用UrlclassLoader(因为这个动态产生的class不在工程当中所以需要使用UrlclassLoader)把这个动态编译的类加载到jvm当中,最后通过反射把这个类实例化。

那么JDK实现的动态代理,又是怎样实现的呢?

上一篇有一个问题,我们的逻辑都是打印语句。那我们的逻辑如何动态?怎么代理任何逻辑?

带着疑问我们来看一下 jdk的动态代理。如下图是jdk代理源码的核心方法,jdk为什么需要一个classloader?

在这里插入图片描述

jvm加载图,如下。jvm先回加载所有预编译好的class文件,但是动态产生的类怎么办呢?就需要我们传入classloader 然后再load一遍到jvm中。

在这里插入图片描述

1.2jdk动态代理是怎么回事?

如下代码片段,我们要调用JDK动态代理。只需要传入三个参数。类加载器,目标类实现的接口,与InvocationHandler对象。

 //第一个参数,传入类加载器。怎么证明一个类在项目中的唯一呢?很多人会说hashcode,
 //其实是根据类加载器来判断。因为一个类只会加载一次
        //第二个参数,传入被代理类(目标对象的类)的接口数组。因为类是可以多实现,所以是一个数据
        //第三个参数,传入InvocationHandler对象,但是InvocationHandler是一个接口。
        //接口中只有一个方法,invoke(代理对象,目标对象,参数) 目标对象.invoke()调用反射传入目标对象与方法实现动态代理
        UserDao dao = (UserDao) Proxy.newProxyInstance(UserDao.class.getClassLoader(),
         new Class[]{UserDao.class}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("假装增强,打印sql语句");
                return method.invoke(new UserDaoImpl(),args);
            }
        });
        dao.query();
        System.out.println("------------------华丽分分割线--------------------");
        System.out.println(dao.query("JDK动态代理。。。。"));
    }
-------------------------控制台输出---------------------------------------------
假装增强,打印sql语句
假装查询数据库
------------------华丽分分割线--------------------
假装增强,打印sql语句
JDK动态代理。。。。

1.3如上代码片段,只需要传入三个参数就可以对代理对象实现动态的业务与增强?那么jdk底层是怎么做的呢?

·我们先猜想,来改进一下上一篇的ProxyUtile工具类。

改造后的山寨版动态代理类核心如下。
    public static Object newInstance(Class[] targetIn, CoustomInvocationHandler h){
        Object proxy=null;
        Class targetInf = targetIn[0];
        Method methods[] =targetInf.getMethods();
        //定义换行
        String line="\n";
        //定义tab空格
        String tab ="\t";
        String infName = targetInf.getSimpleName();
        String content ="";
        String packageContent = "package com.google;"+line;
        String importContent = "import "+targetInf.getName()+";"+line
                +"import com.bing.li.util.CoustomInvocationHandler;"+line
                +"import java.lang.*;"+line
                +"import java.lang.reflect.Method;"+line;
        String clazzFirstLineContent = "public class $Proxy implements "+infName+"{"+line;
        String filedContent  =tab+"private CoustomInvocationHandler h;"+line;
        String constructorContent =tab+"public $Proxy (CoustomInvocationHandler h){" +line
                +tab+tab+"this.h =h;"
                +line+tab+"}"+line;
        String methodContent = "";
        for (Method method : methods) {
            String returnTypeName = method.getReturnType().getSimpleName();
            String methodName =method.getName();
            String argsContent = "";
            String paramsContent="";
            int flag =0;
            methodContent+=tab+"public "+returnTypeName+" "+methodName+"(";
                //获取方法的所有参数
                Class parameters[] = method.getParameterTypes();
                //参数字符串
                StringBuilder args = new StringBuilder();
                for (int i = 0; i < parameters.length; i++) {
                    //参数的类型,形参 String p0,Sting p1,
                    methodContent+=parameters[i].getSimpleName() + " p" +i;
                    args.append("p" +i);
                    if (i != parameters.length - 1) {
                        methodContent+=",";
                        args.append(",");
                    }
                }
                methodContent+="){"+line
                    +tab+tab+"try {"+line
                    +tab+tab+"Method method = Class.forName(\""+targetInf.getName()+"\").getDeclaredMethod(\""+methodName+"\");"+line;
                     if (!returnTypeName.equals("void")) {
                       //  methodContent+=tab + tab +"Class[] args = new Class[]{" + args + "};"+line;
                         methodContent+= tab + tab + "return (" + returnTypeName + ")h.invoke(method, new Object[]{"+ args +"});" + line;
                     }else {
                         methodContent+= tab + tab + " h.invoke(method,new Object[]{"+paramsContent+"});" + line;
                     }
                     methodContent+= tab+tab+"}catch (Exception e){"+line+tab+tab+"}"+line;
                    if (!returnTypeName.equals("void")) {
                        methodContent+= tab+tab+tab+"return "+ null+ ";"+line;
                    }
                     methodContent+= tab+"}"+line;
                  }
        content=packageContent+importContent+clazzFirstLineContent+filedContent+constructorContent+methodContent+"}";
        File file1 =new File("G:\\com\\google");
        File file =new File("G:\\com\\google\\$Proxy.java");
        try {
            //判断是否是一个目录
            if (!file1.isDirectory()){
                //创建目录
                file1.mkdirs();
            }
            if (!file.exists()) {
                //创建文件
                file.createNewFile();
            }
            //将字符串 写出到磁盘
            FileWriter fw = new FileWriter(file);
            fw.write(content);
            fw.flush();
            fw.close();

            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
            Iterable units = fileMgr.getJavaFileObjects(file);
            JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
            t.call();
            fileMgr.close();
            //创建需要加载.class的地址
            URL[] urls = new URL[]{new URL("file:G:\\\\")};
            URLClassLoader urlClassLoader = new URLClassLoader(urls);
            //加载class对象
            Class clazz = urlClassLoader.loadClass("com.google.$Proxy");
            Constructor constructor = clazz.getConstructor(CoustomInvocationHandler.class);
            proxy = constructor.newInstance(h);
            //clazz.newInstance();
            //Class.forName()
        }catch (Exception e){
            e.printStackTrace();

        }
        return proxy;
    }
调用工具类,控制台输出如下,我们也能动态实现业务逻辑部分了。
    public static void main(final String[] args) {
        UserDao proxy = (UserDao) ProxyUti2.newInstance(new Class[]{UserDao.class}, new CoustomInvocationHandler() {
            @Override
            public Object invoke(Method method, Object[] arg){
                System.out.println("山寨版JDK动态代理增强打印sql");
                try {
                    Object invoke = method.invoke(new UserDaoImpl(), arg);
                    return invoke;
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
                return null;
            }
        });
        proxy.query();
        ------------------------------控制台输出----------------------------
        山寨版JDK动态代理增强打印sql
		假装查询数据库
我们自己实现的动态代理缺点有哪些呢?

缺点:首先要生成文件
缺点:动态编译文件 class
编译会产生IO操作,软件性能的最终体现在IO操作。性能会降低。
缺点:需要一个URLclassloader

JDK 动态代理源码分析:

如下是JDK 动态代理newProxyInstance方法 如下,最重要的一行代码就是 Class<?> cl = getProxyClass0(loader, intfs); 通过这行代码 其实就得到了代理类,然后通过代理类的构造方法,就实例化了一个代理对象。

 @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);
		// 克隆一下我们传进来的 interface
        final Class<?>[] intfs = interfaces.clone();
        //安全验证
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         * 找到我们的代理类。
         */
         //这段方法执行完成,就得到我们的代理类
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        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;
                    }
                });
            }
            //通过构造方法 new出我们的要得代理对象
            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);
        }
    }
getProxyClass0(ClassLoader loader,Class<?>… interfaces) 方法。其实就一行代码proxyClassCache.get(loader, interfaces);通过名字看出,缓存中拿到代理对象。那我们 继续往下看,看get(loader, interfaces)方法它又做了什么呢?
    /**
     * Generate a proxy class.  Must call the checkProxyAccess method
     * to perform permission checks before calling this.
     */
    private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        return proxyClassCache.get(loader, interfaces);
    }
get(loader, interfaces)方法如下。
 public V get(K key, P parameter) {
        Objects.requireNonNull(parameter);
        expungeStaleEntries();
		//JDK去 cache中拿缓存,如果缓存中存在。就不在生成,直接拿。
        Object cacheKey = CacheKey.valueOf(key, refQueue);
        // lazily install the 2nd level valuesMap for the particular cacheKey
        ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
        if (valuesMap == null) {
            ConcurrentMap<Object, Supplier<V>> oldValuesMap
                = map.putIfAbsent(cacheKey,
                                  valuesMap = new ConcurrentHashMap<>());
            if (oldValuesMap != null) {
                valuesMap = oldValuesMap;
            }
        }

        // create subKey and retrieve the possible Supplier<V> stored by that
        // subKey from valuesMap
        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
        Supplier<V> supplier = valuesMap.get(subKey);
        Factory factory = null;

        while (true) {
            if (supplier != null) {
                // supplier might be a Factory or a CacheValue<V> instance
                //通过这句代码得到了我们的value
                V value = supplier.get();
                if (value != null) {
                //value就是我们要的这个代理类。
                    return value;
                }
            }
            // else no supplier in cache
            // or a supplier that returned null (could be a cleared CacheValue
            // or a Factory that wasn't successful in installing the CacheValue)

            // lazily construct a Factory
            if (factory == null) {
                factory = new Factory(key, parameter, subKey, valuesMap);
            }

            if (supplier == null) {
                supplier = valuesMap.putIfAbsent(subKey, factory);
                if (supplier == null) {
                    // successfully installed Factory
                    supplier = factory;
                }
                // else retry with winning supplier
            } else {
                if (valuesMap.replace(subKey, supplier, factory)) {
                    // successfully replaced
                    // cleared CacheEntry / unsuccessful Factory
                    // with our Factory
                    supplier = factory;
                } else {
                    // retry with current supplier
                    supplier = valuesMap.get(subKey);
                }
            }
        }
    }
以上源码部分可以看出通过V value = supplier.get();这一行代码得到了我们要的类。接着,我们继续分析这一行代码。到底做了什么?通过断点的方式,得到如下代码。
  @Override
        public synchronized V get() { // serialize access
            // re-check
            //从jdkmap众去获取。。
            Supplier<V> supplier = valuesMap.get(subKey);
            if (supplier != this) {
                // something changed while we were waiting:
                // might be that we were replaced by a CacheValue
                // or were removed because of failure ->
                // return null to signal WeakCache.get() to retry
                // the loop
                return null;
            }
            // else still us (supplier == this)

            // create new value
            V value = null;
            try {
            //获取到value 说白了,这个方法最后返回 回去的value取值就是这一行代码。
                value = Objects.requireNonNull(valueFactory.apply(key, parameter));
            } finally {
                if (value == null) { // remove us on failure
                    valuesMap.remove(subKey, this);
                }
            }
            // the only path to reach here is with non-null value
            assert value != null;

            // wrap value with CacheValue (WeakReference)
            CacheValue<V> cacheValue = new CacheValue<>(value);

            // try replacing us with CacheValue (this should always succeed)
            if (valuesMap.replace(subKey, this, cacheValue)) {
                // put also in reverseMap
                reverseMap.put(cacheValue, Boolean.TRUE);
            } else {
                throw new AssertionError("Should not reach here");
            }

            // successfully replaced us with new CacheValue -> return the value
            // wrapped by it
            //获取到我们要的类文件
            return value;
        }
    }
通过如上代码,得到value = Objects.requireNonNull(valueFactory.apply(key, parameter));value取值的这段代码就是这一句。接口我们继续分析,这一句代码到底做了些什么。这是一个类部类,外部类的名称叫ProxyClassFactory 的apply方法。
    @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            //得到我们传进来的接口然后循环
            for (Class<?> intf : interfaces) {
                /*
                 * Verify that the class loader resolves the name of this
                 * interface to the same Class object.
                 */
                Class<?> interfaceClass = null;
                try {
                -- 这里就非常重要。这里我们传进来的已经是一个class,然后它用反射再load一次?为什么要再load一次呢?
                -- 这里不是重复了一次嘛?我们传进来本身就是一个class
                -- 其实JDK这里做了一个相对高级的判断,这里重复的load一遍。是为了判断这两个类是不是相等。
                -- 上一篇说到,两个类相等有一个前提条件,同一个类加载器。所以它用传进来的loader
                -- 再生了了一遍整个类。
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }
                --这里对两个类 做判断、如果不相等。则抛异常。
                if (interfaceClass != intf) {
                    throw new IllegalArgumentException(
                        intf + " is not visible from class loader");
                }
                /*
                 * Verify that the Class object actually represents an
                 * interface.
                 */
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
                /*
                 * Verify that this interface is not a duplicate.
                 */
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
            }
			--如果相等,则就往下面走。 这个就是代理类的包名。
            String proxyPkg = null;     // package to define proxy class in
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

            /*
             * Record the package of a non-public proxy interface so that the
             * proxy class will be defined in the same package.  Verify that
             * all non-public proxy interfaces are in the same package.
             */
             --循环所有的interface 
            for (Class<?> intf : interfaces) {
                int flags = intf.getModifiers();
                -- 判断接口是不是 public 如果不是公共的 则不能实现它。
                if (!Modifier.isPublic(flags)) {
                    accessFlags = Modifier.FINAL;
                    String name = intf.getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                            "non-public interfaces from different packages");
                    }
                }
            }

            if (proxyPkg == null) {
                // if no non-public proxy interfaces, use com.sun.proxy package
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }

            /*
             * Choose a name for the proxy class to generate.
             */
            long num = nextUniqueNumber.getAndIncrement();
            -- 跑完以上代码,就得到了 包名+类名+一个类标识。
            -- 包名是JDK  内部这一段大嘛写死的public static final String PROXY_PACKAGE = "com.sun.proxy";
            -- proxyClassNamePrefix  是固定的成员静态变量$Proxy
            -- 然后再生成一个随机数。防止多线程重复的情况。
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            /*
             * Generate the specified proxy class.
             */
            -- 这一段很重要 ,这一句代码直接生成一个代理类的class byte数组。
            -- 那么byte怎么变成这个方法所需要的class对象呢?
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
            	-- 通过这一段代码就返回了。我们要的class类。
            	--  这里调用的native方法,JDK底层的方法。就不在深入研究了。
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                /*
                 * A ClassFormatError here means that (barring bugs in the
                 * proxy class generation code) there was some other
                 * invalid aspect of the arguments supplied to the proxy
                 * class creation (such as virtual machine limitations
                 * exceeded).
                 */
                throw new IllegalArgumentException(e.toString());
            }
        }
    }

ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags) 讲一下这个方法,这个其实是JDK 的一个静态方法。。
调用这个方法,生成的Class文件如下,其实就跟我们自己上面写的山寨版的动态代理文件差不多。只不过我们先生成.java文件,在调用URLclassload 生成.class字节码。然后读取到内存中,产生对象。这里一步到位了。这里就不做多讲解。有兴趣的可以自己去品。
    byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy18", new Class[]{UserDao.class});
        FileOutputStream fileOutputStream = new FileOutputStream("G:\\$Proxy18.class");
        fileOutputStream.write(bytes);
        fileOutputStream.flush();

在这里插入图片描述

项目源码地址
总结:

JDK动态代理底层:
通过接口反射,得到字节码(就是那个byte[])然后通过native defineClass0方法把字节码转成calss
JDK动态代码基于接口实现反射,
Cglib 基于继承
cglib 是基于asm框架实现。ASM框架是一个操作字节码的一个框架。
JDK 与cglib 性能呢?
性能基本上可以忽略,看应用场景。没有接口的情况下就用cglib

预告:下一篇,手写IOC ,AOP 最近比较忙更新比较慢。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值