类生命周期和加载机制
java类的加载机制
java编译期:.java文件通过编译器编译成.class文件的过程,其中会校验格式和初步的问题(比如是否捕捉异常,list插入数据类型格式不对),也会优化一些运算(比如 String test=”a”+”b”; –> test=”ab”;)
编译期的过程:
- 解析与填充符号表过程:解析主要包括词法分析和语法分析两个过程;
- 过程;(loombok的原理就是在这个时候按照规则插入相应的代码,绝对不是反射,反射是运行时编译)
- 语义分析与字节码的生成过程;
- java运行期: 加载、连接、初始化、创建堆中对象
classLoader
http://blog.csdn.net/briblue/article/details/54973413#reply
- 加载器:
- BootstrapClassLoader 最顶层的加载类,jvm内置的加载器,主要加载核心类库,%JRE_HOME%\lib下的rt.jar、resources.jar、charsets.jar和class等。
- ExtentionClassLoader 扩展的类加载器,加载目录%JRE_HOME%\lib\ext目录下的jar包和class文件
- AppclassLoader也称为SystemAppClass 加载当前应用的classpath的所有类。也就是自己写的类基本上都是APP来加载的
- 自定义ClassLoader,可以突破class文件包路径的限制,需要重写findClass()缓存查找方法制定寻找路径即可
- contextClassLoader,是Thread的成员变量,默认是AppClassLoader。子线程默认和父线程相同的加载器
- 特点
- 不同包路径下的类由不同的类加载器加载
- jvm懒加载,根据需要去动态加载
- 父加载器不是父类
- 加载机制:
BootstrapClassLoader、ExtClassLoader、AppClassLoader实际是查阅相应的环境属性sun.boot.class.path、java.ext.dirs和java.class.path来加载资源文件的
- AppclassLoader的父加载器为ExtClassLoader,ExtClassLoader的父加载器为null,JVM发现为null时就找内置的加载器BootstrapClassLoader。
- Bootstrap ClassLoader是由C/C++编写的,它本身是虚拟机的一部分,所以它并不是一个JAVA类,也就是无法在java代码中获取它的引用,JVM启动时通过Bootstrap类加载器加载rt.jar等核心jar包中的class文件器。Bootstrap没有父加载器,但是它却可以作用一个ClassLoader的父加载器。比如ExtClassLoader
- 双亲委托:自下而上,先挨个找缓存,到了顶层缓存中还没有,就开始初始化,从各自对应负责的包路径下查找,有就创建,没有给就子加载器找。
- 避免重复创建对象,浪费内存
- 重要的方法
- loadClass(String 类全路径,Boolean resloved)
- findLoadedClass(name) 先加锁在自己的加载器缓存中找是否已经有实例化
- c = parent.loadClass(name, false); 找不到就递归在父加载器中加载
- c = findClass(name);父加载器也加载不了,就在自己的管辖范围内创建类
- 加载的结果
- 将class文件加载在内存中。
- 将静态数据结构(数据存在于class文件的结构)转化成方法区中运行时的数据结构(数据存在于JVM时的数据结构)。
- 在堆中生成一个代表这个类的java.lang.Class对象,作为数据访问的入口。
tomcat的类加载机制
http://blog.csdn.net/jiaomingliang/article/details/47416007
http://blog.csdn.net/czmacd/article/details/54017027
设计的结构:
- tomcat下每个应用程序都有一个独立的类加载器,WebAppClassLoader,因为不同应用程序的类不能乱加载,更不能公用啊。
- 但是有一些通用的类,比如JUnit、Log4j类,是不同应用程序公用的类,所以有sharedClassLoader出场,可以设置路径来实现这个类加载器,从而实现不同应用程序共同加载一些通用工具类
- 再上一层就是CommonClassLoader:下面由shardClassLoader和CatalinaClassLoader,一个是所有应用程序,一个是tomcat容器的扩展类加载器
- 再上一层就是AppClassLoader了,就是传统的模式往上走了
- tomcat类加载器
- CommonClassLoader(默认父加载器为AppClassLoader)。加载一些tomcat/lib下面的jar包,servlet-api.jar、jsp-api.jar等
- catalinaClassLoader(默认是空,有需要的话需要自己配置)
- sharedClassLoader(默认是空,有需要的话需要自己配置)
- WebAppClassLoader(tomcat下 WebRoot/应用程序/WEB-INF/lib 和 class包下的类)
- 加载顺序
- tomcat启动,先加载BootStrap类初始化:CommonClassLoader、catalinaLoader、sharedLoader,会根据catelina.property里面的配置分配各自管理的类范围,默认catalinaLoader、sharedLoader配置为null,也就是默认3个加载器是同一个CommonClassLoader。
- 每个应用程序都要各自独立的WebAppClassLoader,继承自sharedClassLoader
3.关键逻辑(源码 org.apache.catalina.loader.WebappClassLoader#loadClass)
tomcat的类加载机制是违反了双亲委托原则的,最后走投无路自己加载不了再去调用父加载器
- 先在本地缓存中查找是否已经加载过该类(clazz = findLoadedClass0(name))
- 再查询JVM缓存中查找是否已经加载过该类( clazz = findLoadedClass(name))
- 让系统类加载器(AppClassLoader)尝试加载该类,主要是为了防止一些基础类会被web中的类覆盖(tomcat bin下包)(clazz = system.loadClass(name))
- web应用的类加载器将自行加载(findClass),这里也违背了双亲 (clazz = findClass(name);)
- 最后还是加载不到的话,则委托父类加载器(Common ClassLoader)去加载。(clazz = Class.forName(name, false, parentLoader);)
反射和Spring IOC
- 反射是运行期编译,只有执行到这个方法,确定了参数:类名的时候才编译出对应的class文件,再加载到内存当中
Class tc = Class.forName(“com.java.dbtest.TestConnection”); - CGLIB也是运行期编译,Enhancer类继承代理类,拦截相应的方法,实现代理