您的位置:首页 > 编程语言 > Java开发

《从零开始写Javaweb框架》知识点--类的加载

2017-11-01 23:29 260 查看
Java的开发,都是从类的加载开始,加载类后,才能实例化具体的对象,进而才能使用类。类的加载就要有相应的类加载器,回顾之前看的《深入理解Java虚拟机》,类加载器ClassLoader有四种,第一种启动类加载器,主要是将<JAVA_HOME>/lib下的,或者被-Xbootclasspath参数指定的路径中的,并且是虚拟机识别的(按照文件名识别,名字不符合的,即使在lib下也不会被加载),将这些类加载到虚拟机中。第二种是扩展类加载器,主要是将<JAVA_HOME>/lib/ext下的,或者被java.ext.dirs所指定路径中的类,加载至虚拟机中。第三种Application
ClassLoader,负责将用户ClassPath下的类加载至虚拟机中,如果程序中没有定义自己的类加载器,一般情况下这个就是程序中默认的类加载器。第四种为用户自定义类加载器,前三种为虚拟机自带的,一般情况下足够使用。

从最顶层至最底层,依次是:启动类加载---扩展类加载---应用程序类加载---自定义类加载器。类加载器的层次关系,为双亲委派模型,工作过程为:如果一个类加载器收到了类加载的请求,他首先不会加载这个类,而是将这个请求委派至其父类加载器去加载,每一层次都是这样,所以所有的类加载请求,都会传到启动类加载器,只有当父类加载器不能加载这个类时,子类才回尝试去加载。

了解了类加载器,就可以进行类加载,比如:加载某一个类

public static Class<?> loadClass(String className, boolean isInitialized) {
Class<?> cls;
try {
cls = Class.forName(className, isInitialized, getClassLoader());
} catch (ClassNotFoundException e) {
LOGGER.error("load class failure", e);
throw new RuntimeException(e);
}
return cls;
}由于虚拟机规范并没有指定类的加载形式,因此类的加载可以从压缩包获取,比如jar包、war包;也可以从网络中获取比如Applet等。可从指定包名下获取所有类:
/**
* 获取指定包名下的所有类
*/
public static Set<Class<?>> getClassSet(String packageName) {
Set<Class<?>> classSet = new HashSet<Class<?>>();
try {
Enumeration<URL> urls = getClassLoader().getResources(packageName.replace(".", "/"));
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
if (url != null) {
String protocol = url.getProtocol();
if (protocol.equals("file")) {
String packagePath = url.getPath().replaceAll("%20", " ");
addClass(classSet, packagePath, packageName);
} else if (protocol.equals("jar")) {
JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
if (jarURLConnection != null) {
JarFile jarFile = jarURLConnection.getJarFile();
if (jarFile != null) {
Enumeration<JarEntry> jarEntries = jarFile.entries();
while (jarEntries.hasMoreElements()) {
JarEntry jarEntry = jarEntries.nextElement();
String jarEntryName = jarEntry.getName();
if (jarEntryName.endsWith(".class")) {
String className = jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).replaceAll("/", ".");
doAddClass(classSet, className);
}
}
}
}
}
}
}
} catch (Exception e) {
LOGGER.error("get class set failure", e);
throw new RuntimeException(e);
}
return classSet;
}

private static void addClass(Set<Class<?>> classSet, String packagePath, String packageName) {
File[] files = new File(packagePath).listFiles(new FileFilter() {
public boolean accept(File file) {
return (file.isFile() && file.getName().endsWith(".class")) || file.isDirectory();
}
});
for (File file : files) {
String fileName = file.getName();
if (file.isFile()) {
String className = fileName.substring(0, fileName.lastIndexOf("."));
if (StringUtil.isNotEmpty(packageName)) {
className = packageName + "." + className;
}
doAddClass(classSet, className);
} else {
String subPackagePath = fileName;
if (StringUtil.isNotEmpty(packagePath)) {
subPackagePath = packagePath + "/" + subPackagePath;
}
String subPackageName = fileName;
if (StringUtil.isNotEmpty(packageName)) {
subPackageName = packageName + "." + subPackageName;
}
addClass(classSet, subPackagePath, subPackageName);
}
}
}

private static void doAddClass(Set<Class<?>> classSet, String className) {
Class<?> cls = loadClass(className, false);
classSet.add(cls);
}其中Enumeration接口中定义了一些方法,通过这些方法可以枚举(一次获得一个)对象集合中的元素。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐