何为类加载器?
网上说:加载类的工具。我觉得作为编程人员,把他定义成能够加载类的一段代码更加能够去除概念的晦涩。
何为类加载器加载了类?
一个类被类加载器加载了的意思就是通过类加载器把类的二进制加载到内存中.也就是说在类加载器加载某个类之前。这个类已经是.class文件了,编译器做了这件事情。
什么样的类可以被加载?
1):本地编译好的class中直接加载
2):网络加载:java.net.URLClassLoader可以加载url指定的类
3):从jar、zip等等压缩文件加载类,自动解析jar文件找到class文件去加载util类
4):从java源代码文件动态编译成为class文件
类加载过程中都发生了什么?
编译器将java代码编译成了.class文件这只是万里长城走完了第一步。虚拟机是如何掌握这个.class文件的操纵权的呢,这个时候就需要参谋长类加载器去做这件事情。虚拟机的类加载机制如下:
1、装载(Load)
查找并加载类的二进制数据;
2、链接(Link)
2.1、校验(Verify):确保被加载类的正确性;
2.2、准备(Prepare):为类的静态变量分配内存,并将其初始化为默认值;
2.3、解析(Resolve):把类中的符号引用转换为直接引用;
3、初始化(Initialize):为类的静态变量赋予正确的初始值;
从这个类加载过程来看,“参谋长”要做的事情倒是真多,也看出来“参谋长”的能力有多强。接下来我们就来仔细看看我们的“参谋长”。
首先我们看看“参谋长”的长相和“参谋长”的脾气。
jvm的类加载器的结构如图所示:
(1) Bootstrap ClassLoader : 将 存放于<JAVA_HOME>\lib目录中的,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的(仅按照文 件名识别,如 rt.jar 名字不符合的类库即使放在lib目录中也不会被加载)类库加载到虚拟机内存中。启动类加载器无法被Java程序直接引用
(2) Extension ClassLoader : 将<JAVA_HOME>\lib\ext目录下的,或者被java.ext.dirs系统变量所指定的路径中的所有类库加载。开发者可以直接使用扩展类加载器。
(3) Application ClassLoader : 负责加载用户类路径(ClassPath)上所指定的类库,开发者可直接使用。
(4)自定义类加载器:
属于应用程序根据自身需要自定义的ClassLoader,如tomcat、jboss都会根据j2ee规范自行实现ClassLoader。
“参谋长”对待需要加载的类遵守双亲委派模型的原则。 双亲委派模型的工作过程:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的 类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需 的类)时,子加载器才会自己去加载。
以上都是JVM类加载器的知识点,分水岭了哈,来说说servlet服务器tomcat的类加载器。
再说tomcat(7)服务器的类加载器。tomcat服务器毕竟是要符合自己的业务需要所以在原则不变的基础上同jvm加载器是有一定的差异。作为优秀的java web服务器他需要解决自己的问题:
① 同一个Web服务器里,各个Web项目之间各自使用的Java类库要互相隔离。
② 同一个Web服务器里,各个Web项目之间可以提供共享的Java类库。
③ 服务器为了不受Web项目的影响,应该使服务器的类库与应用程序的类库互相独立。
④ 对于支持JSP的Web服务器,应该支持热插拔(hotswap)功能。
以上的东西太多太复杂,本文就介绍简单的加载器。(代码针对tomcat4)。
tomcat类加载器如图所示:
简单的tomcat类加载器需要满足以下需求:
· 要制定类加载器的某些特定规则
· 缓存以前加载的类 ·
事先加载类以预备使用
public interface Loader {
//返回容器使用的java类加载器
public ClassLoader getClassLoader();
//返回关联加载器的容器
public Container getContainer();
//设置和类加载器相关联的容器
public void setContainer(Container container);
//返回和管理器相关联的DefaultContext
public DefaultContext getDefaultContext();
//设置和管理器相关的DefaultContext /**
public void setDefaultContext(DefaultContext defaultContext);
//返回配置类加载器的代理模式flag值
public boolean getDelegate();
//设置类加载器的代理模式flag
public void setDelegate(boolean delegate);
//返回类加载器的版本号、描述信息
public String getInfo();
//是否可以自动重载
public boolean getReloadable();
//设置自动重载标示
public void setReloadable(boolean reloadable);
// --------------------------------------------------------- 公共方法
//添加该容器的属性变化监听器
public void addPropertyChangeListener(PropertyChangeListener listener);
//添加类加载器的加载位置
public void addRepository(String repository);
//返回类加载器的库加载路径组合
public String[] findRepositories();
//类加载器是否被修改标示
public boolean modified();
//删除该容器的属性变化监听器
public void removePropertyChangeListener(PropertyChangeListener listener);
}
---------------------
public interface Reloader {
//添加新的类加载路径
public void addRepository(String repository);
//返回所有的类库加载路径
public String[] findRepositories();
//类加载器是否被修改了
public boolean modified();
}
---------------------------------
//该类加载器用于从指向 JAR 文件和目录的 URL 的搜索路径加载类和资源。这里假定任何以 '/' 结束的 URL //都是指向目录的。如果不是以该字符结束,则认为该 URL 指向一个将根据需要打开的 JAR 文件。
//创建 URLClassLoader 实例的 AccessControlContext 线程将在后续加载类和资源时使用。
//为加载的类默认授予只能访问 URLClassLoader 创建时指定的 URL 的权限。
public class URLClassLoader extends SecureClassLoader implements Closeable {
//加载类和资源的url路径
private final URLClassPath ucp;
//加载类和资源的上下文
private final AccessControlContext acc;
//为给定的 URL 构造新 URLClassLoader。首先在指定的父类加载器中搜索 //URL,然后按照为类和资源指定的顺序搜索 URL。这里假定任何以 '/' 结束的 URL //都是指向目录的。如果不是以该字符结束,则认为该 URL 指向一个将根据需要下载和打开的 JAR //文件。 如果有安全管理器,该方法首先调用安全管理器的 checkCreateClassLoader //方法以确保允许创建类加载器。
public URLClassLoader(URL[] urls, ClassLoader parent) {
super(parent);
// this is to make the stack depth consistent with 1.1
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkCreateClassLoader();
}
ucp = new URLClassPath(urls);
this.acc = AccessController.getContext();
}
URLClassLoader(URL[] urls, ClassLoader parent,
AccessControlContext acc) {
super(parent);
// this is to make the stack depth consistent with 1.1
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkCreateClassLoader();
}
ucp = new URLClassPath(urls);
this.acc = acc;
}
/**
使用默认的委托父 ClassLoader 为指定的 URL 构造一个新 URLClassLoader。首先在父类加载器中搜索 URL,然后按照为类和资源指定的顺序搜索 URL。这里假定任何以 '/' 结束的 URL 都是指向目录的。如果不是以该字符结束,则认为该 URL 指向一个将根据需要下载和打开的 JAR 文件。
如果有安全管理器,该方法首先调用安全管理器的 checkCreateClassLoader 方法以确保允许创建类加载器。
*/
public URLClassLoader(URL[] urls) {
super();
// this is to make the stack depth consistent with 1.1
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkCreateClassLoader();
}
ucp = new URLClassPath(urls);
this.acc = AccessController.getContext();
}
//回给定 codesource 对象的权限。该方法的实现首先调用 super.getPermissions,然后基于 //codesource 的 URL 添加权限。
//如果此 URL 的协议为 "jar",那么授予的权限将基于 Jar 文件 URL 所请求的权限。
//如果协议为 "file",并且路径指定了某个文件,则要授予对该文件的读权限。如果协议为 //"file",并且路径是一个目录,则要授予该目录中的所有文件及其(递归)子目录中包含的所有文件读权限。
protected PermissionCollection getPermissions(CodeSource codesource)
{
PermissionCollection perms = super.getPermissions(codesource);
URL url = codesource.getLocation();
Permission p;
URLConnection urlConnection;
try {
urlConnection = url.openConnection();
p = urlConnection.getPermission();
} catch (java.io.IOException ioe) {
p = null;
urlConnection = null;
}
if (p instanceof FilePermission) {
// if the permission has a separator char on the end,
// it means the codebase is a directory, and we need
// to add an additional permission to read recursively
String path = p.getName();
if (path.endsWith(File.separator)) {
path += "-";
p = new FilePermission(path, SecurityConstants.FILE_READ_ACTION);
}
} else if ((p == null) && (url.getProtocol().equals("file"))) {
String path = url.getFile().replace('/', File.separatorChar);
path = ParseUtil.decode(path);
if (path.endsWith(File.separator))
path += "-";
p = new FilePermission(path, SecurityConstants.FILE_READ_ACTION);
} else {
/**
* Not loading from a 'file:' URL so we want to give the class
* permission to connect to and accept from the remote host
* after we've made sure the host is the correct one and is valid.
*/
URL locUrl = url;
if (urlConnection instanceof JarURLConnection) {
locUrl = ((JarURLConnection)urlConnection).getJarFileURL();
}
String host = locUrl.getHost();
if (host != null && (host.length() > 0))
p = new SocketPermission(host,
SecurityConstants.SOCKET_CONNECT_ACCEPT_ACTION);
}
// make sure the person that created this class loader
// would have this permission
if (p != null) {
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
final Permission fp = p;
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() throws SecurityException {
sm.checkPermission(fp);
return null;
}
}, acc);
}
perms.add(p);
}
return perms;
}
//为指定的 URL 和父类加载器创建新 URLClassLoader 实例。如果安装了安全管理器,该方法返回的 //URLClassLoader 的 loadClass 方法将在加载该类之前调用 SecurityManager.checkPackageAccess //方法。
public static URLClassLoader newInstance(final URL[] urls,
final ClassLoader parent) {
// Save the caller's context
final AccessControlContext acc = AccessController.getContext();
// Need a privileged block to create the class loader
URLClassLoader ucl = AccessController.doPrivileged(
new PrivilegedAction<URLClassLoader>() {
public URLClassLoader run() {
return new FactoryURLClassLoader(urls, parent, acc);
}
});
return ucl;
}
//加载器工厂类
final class FactoryURLClassLoader extends URLClassLoader {
static {
ClassLoader.registerAsParallelCapable();
}
FactoryURLClassLoader(URL[] urls, ClassLoader parent,
AccessControlContext acc) {
super(urls, parent, acc);
}
FactoryURLClassLoader(URL[] urls, AccessControlContext acc) {
super(urls, acc);
}
public final Class loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// First check if we have permission to access the package. This
// should go away once we've added support for exported packages.
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
int i = name.lastIndexOf('.');
if (i != -1) {
sm.checkPackageAccess(name.substring(0, i));
}
}
return super.loadClass(name, resolve);
}
其他略
}