换种眼光看Spring之bean是怎么诞生的(一)

   Java的世界里处处存在了对象,有时候换一种眼光往往会给自己带来与之前大不一样的理解。

   一个对象的出现离不开字节码,拿classforname来讲,classforname("...") 就像问你 狗 ,在你脑子里立马就出现了狗的样子,这就是狗的类,拉布拉多就出现拉布拉多狗的类,就类似于一个加载的过程,想要得到私有的方法,私有的类,比如拉布拉多和其他种类狗的区别,私有的用setAccessible设置为true,这样就可以调用,这其中加载的过程一眼就看到了父类也会加载进去,这就是类的初始化。

    而这里还要说的是classloader是从BootStrapClassLoader开始,到ExtClassLoader再到AppClassLoader,再到自己的ClassLoader。很多人看到这些有的会很明白,同样的也有很多大头的,其实拿我们自身来说,我们要生存,首先我们自己得先活着,这就要求我们有血有肉,各个器官各种完好,BootStrapClassLoader不就是类似于干这事的么,它主要用于加载一些Java自带的核心类,是不能被替换掉的,由jvm内核实现,只有加载了这些最核心的内容,才会有后面classloader的存在机会。

      在有了基本的器官之后,作为人,我们要生存还需要一些本能,呼吸,眨眼,条件反射(先天的),这个就类似于ExtClassLoader,其是加载在jre/lib/ext下的jar包,当然,我们也可以把自己的jar放到里面去,通过这个来加载,毕竟我们的好多先天的条件反射也是慢慢进化来的,是不是很形象

     作为人,我们可以学习到很多技能,认识很多事物,每个人的所见都不一样,每个人都是一个独立的虚拟机,这里就谈到了AppClassLoader,它加载的是classpath下面的内容,默认情况都由其加载。

    当然,凡事总有特殊,用户自定义的classloader要加载的内容不在上面的classpath范围内,怎么加载完全自己去定义,就像你从未见过某一种东西,你怎么去知道那是什么,要不就不打算认识说不知道,要不就给其编个名字安上!

上面说了一些class字节码的加载顺序,然后就是对其进行解析校验,最后是初始化,既然说到了这里,那就提下,初始化的时候会调用Class对象自身的构造函数,这里static块其实是个坑,当多个线程同时访问这个类时,必须等static块执行完,要不会发生阻塞,所以不适合编写大量的业务尤其是i/o逻辑业务

    在classloader源码里发现了如下代码:

protected final Class<?> defineClass(String name, byte[] b, int off, int len)
        throws ClassFormatError
    {
        return defineClass(name, b, off, len, null);
    }

通过传入byte[]数组就可以动态的加载Class类,这就牵扯到了字节码加强。

     终于和上一篇接着了,只要接触过Spring都知道,AOP,动态代理,追根究底都是字节码增强,所以也免不了俗,先说动态代理,看过很多这方面的文章,说说自己的理解,首先,jdk的动态代理,有点类似于专卖店,我拿房地产中介来说可能更好理解一些,一个共同的接口,卖房,房主实现了卖方的接口,房产中介也实现了卖方的接口,然后要有一套卖家卖房的处理流程也就是处理类handler,这个处理类的内部的invoke方法,如下图所示:

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

包含了代理对象,代理方法,方法参数对象,方法内部由被代理的对象来实现,其实就是包含了中介,卖房扯的过程,方法里面由卖家来实现

 java.lang.reflect.Proxy下的newProxyInstance()方法如下所示

 @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        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;
                    }
                });
            }
            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);
        }
    }
View Code

说白了就是想得到一个代理,就需要卖家的字节码,卖房的接口,和那个处理类handler,jdk动态代理的不好处也是显而易见的,必须实现接口,只能调用接口对应的方法,实例的其他方法无法访问,从这点也启示了我们面向接口编程和多态的用处。

   暂时先敲这么多吧,剩下的下篇接着说

 

posted @ 2016-04-01 09:42  知秋z  阅读(676)  评论(1编辑  收藏  举报