概述
初始化的5种情况:
- 遇到new、getstatic、putstatic 或invokestatic 这4 条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。这4 条指令的最常见的Java 代码场景是:
1.1 使用new 关键字实例化对象的时候
1.2 读取或设置一个类的静态字段(被final 修饰、已在编译期把结果放入常量池的静态字段除外)的时候
1.3 调用一个类的静态方法的时候。 - 使用java.lang.reflect 包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先初始化
- 当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化
- 当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类
- 当使用JDK 1.7 的动态语言支持时,如果一个java.lang.invoke.MethodHandle 实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic 的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先初始化。
加载
- 通过一个类的全限定名来获取定义此类的二进制字节流
- 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
- 在内存中生成一个代表这个类的java.lang.Class 对象,作为方法区这个类的各种数据的访问入口
验证
确保Class 文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全
- 文件格式验证
- 元数据验证
- 字节码验证
- 符号引用验证
准备
正式为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。
- 这时候进行内存分配的仅包括类变量(被static 修饰的变量),而不包括实例变量,实例变量将会在对象实例化时随着对象一起分配在Java
堆中 - 这里所说的初始值“通常情况”下是数据类型的零值
解析
将常量池内的符号引用替换为直接引用的过程
初始化
真正开始执行类中定义的Java 程序代码在准备阶段,变量已经赋过一次系统要求的初始值,而在初始化阶段,则根据代码初始化过程进行初始化
双亲委派模型
某个类加载器在加载类时,先将加载请求交由父类加载器加载,依次递归,如果父类加载器成功加载则成功返回,如果没有则自己加载。
事实上只存在三种类型的类加载器:
启动类加载器
这个类加载器使用C++语言实现,是虚拟机自身的一部分。
负责将存储在JAVA_HOME/lib或-Xbootclasspath指定的目录下的类库加载到虚拟机内存中。
扩展类加载器
由Java 语言实现,独立于虚拟机外部,并且全都继承自抽象类java.lang.ClassLoader。sun.misc.Launcher$ExtClassLoader 实现,它负责加载JAVA_HOME\lib\ext 目录中的,或者被java.ext.dirs 系统变量所指定的路径中的所有类库,开发者可以直接使用扩展类加载器。
应用程序类加载器
由sun.misc.Launcher $App-ClassLoader 实现。由于这个类加载器是ClassLoader 中的getSystemClassLoader()方法的返回值,所以一般也称它为系统类加载器。它负责加载用户类路径(ClassPath)上所指定的类库,开发者可以直接使用这个类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。
Tomcat类加载机制
打破了双亲委派模型的。
Webapp Loader重写了loadClass方法,破坏了类加载器模型。
Catalina 就是Tomcat服务器使用的Apache实现的servlet容器的名字,处理servlet,Tomcat的servlet容器在4.X版本中被Craig McClanahan重新设计为Catalina。
@Override
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (JreCompat.isGraalAvailable() ? this : getClassLoadingLock(name)) {
if (log.isDebugEnabled()) {
log.debug("loadClass(" + name + ", " + resolve + ")");
}
Class<?> clazz = null;
// Log access to stopped class loader
checkStateForClassLoading(name);
// (0) Check our previously loaded local class cache
clazz = findLoadedClass0(name);
if (clazz != null) {
if (log.isDebugEnabled()) {
log.debug(" Returning class from cache");
}
if (resolve) {
resolveClass(clazz);
}
return clazz;
}
// (0.1) Check our previously loaded class cache
clazz = JreCompat.isGraalAvailable() ? null : findLoadedClass(name);
if (clazz != null) {
if (log.isDebugEnabled()) {
log.debug(" Returning class from cache");
}
if (resolve) {
resolveClass(clazz);
}
return clazz;
}
String resourceName = binaryNameToPath(name, false);
ClassLoader javaseLoader = getJavaseClassLoader();
boolean tryLoadingFromJavaseLoader;
try {
URL url;
if (securityManager != null) {
PrivilegedAction<URL> dp = new PrivilegedJavaseGetResource(resourceName);
url = AccessController.doPrivileged(dp);
} else {
url = javaseLoader.getResource(resourceName);
}
tryLoadingFromJavaseLoader = (url != null);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
tryLoadingFromJavaseLoader = true;
}
if (tryLoadingFromJavaseLoader) {
try {
clazz = javaseLoader.loadClass(name);
if (clazz != null) {
if (resolve) {
resolveClass(clazz);
}
return clazz;
}
} catch (ClassNotFoundException e) {
// Ignore
}
}
// (0.5) Permission to access this class when using a SecurityManager
if (securityManager != null) {
int i = name.lastIndexOf('.');
if (i >= 0) {
try {
securityManager.checkPackageAccess(name.substring(0,i));
} catch (SecurityException se) {
String error = sm.getString("webappClassLoader.restrictedPackage", name);
log.info(error, se);
throw new ClassNotFoundException(error, se);
}
}
}
boolean delegateLoad = delegate || filter(name, true);
// (1) Delegate to our parent if requested
if (delegateLoad) {
if (log.isDebugEnabled()) {
log.debug(" Delegating to parent classloader1 " + parent);
}
try {
clazz = Class.forName(name, false, parent);
if (clazz != null) {
if (log.isDebugEnabled()) {
log.debug(" Loading class from parent");
}
if (resolve) {
resolveClass(clazz);
}
return clazz;
}
} catch (ClassNotFoundException e) {
// Ignore
}
}
// (2) Search local repositories
if (log.isDebugEnabled()) {
log.debug(" Searching local repositories");
}
try {
clazz = findClass(name);
if (clazz != null) {
if (log.isDebugEnabled()) {
log.debug(" Loading class from local repository");
}
if (resolve) {
resolveClass(clazz);
}
return clazz;
}
} catch (ClassNotFoundException e) {
// Ignore
}
// (3) Delegate to parent unconditionally
if (!delegateLoad) {
if (log.isDebugEnabled()) {
log.debug(" Delegating to parent classloader at end: " + parent);
}
try {
clazz = Class.forName(name, false, parent);
if (clazz != null) {
if (log.isDebugEnabled()) {
log.debug(" Loading class from parent");
}
if (resolve) {
resolveClass(clazz);
}
return clazz;
}
} catch (ClassNotFoundException e) {
// Ignore
}
}
}
throw new ClassNotFoundException(name);
}
Common ClassLoader是Catalina ClassLoader和Shared ClassLoader的父类,Shared ClassLoader又可能存在多个子加载器Webapp ClassLoader,一个Webapp ClassLoader对应一个Web应用。
代码层面上Catalina ClassLoader、Shared ClassLoader、Common ClassLoader 对应的实体类实际上都是URLClassLoader( 双亲委派模型),而Webapp ClassLoader 和JasperLoader 都是存在对应的类加
载器类的( 打破双亲委派模型)。
当Tomcat启动时会加载以下类加载器:
- Bootstrap :加载JVM 启动所需的类,以及标准扩展类(位于jre/lib/ext 下)
- System:加载tomcat 启动的类,如bootstrap.jar,通常在catalina.bat 或者catalina.sh 中指定。位于CATALINA_HOME/bin
- Common:加载tomcat 使用以及应用通用的一些类,位于CATALINA_HOME/lib 下,比如servlet-api.jar
- webapp:每个应用在部署后,都会创建一个唯一的类加载器。该类加载器会加载位于WEB-INF/lib 下的jar 文件中的class 和WEB-INF/classes下的class 文件
问题
Tomcat容器下的两个应用以及Tomcat的lib目录中都有UserServiceImpl类Tomcat怎么保证类的隔离性?