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); } }
说白了就是想得到一个代理,就需要卖家的字节码,卖房的接口,和那个处理类handler,jdk动态代理的不好处也是显而易见的,必须实现接口,只能调用接口对应的方法,实例的其他方法无法访问,从这点也启示了我们面向接口编程和多态的用处。
暂时先敲这么多吧,剩下的下篇接着说