ClassLoader

文章目录
  1. 1. 类加载器的区别
  2. 2. 双亲委派机制
  3. 3. 自定义类加载器
  4. 4. 资源文件的读取
  5. 5. 读取Properties文件
类加载器的区别

java中提供了三种类加载器,分别是启动类加载器,扩展类加载器,系统类加载器。三种类加载器通过双亲委派机制来实现类的加载。

类加载器 名称 加载路径 实现
启动类加载器 BootStrap ClassLoader 核心库:java.lang.*,系统属性:sun.boot.class.path所指路径 c++
扩展类加载器 Extension ClassLoader 扩展库:jre\lib\ext 目录下的包,系统属性:java.ext.dirs所指路径 纯java
系统类加载器 System ClassLoader 环境变量 classpath 或系统属性 java.class.path 指定目录中加载类 纯java

双亲委派机制

类的双亲委派机制是指子类加载器加载类时,首先会检查该类是否已经被加载。若是该类未被加载,会将加载任务委托给父类加载器,父类加载器又会委托给更上一层类加载器,直到启动类加载器。启动类加载器会检查其加载路径,有则加载,无则加载任务给扩展类加载器,扩展类有会检查路径并执行加载。


自定义类加载器

在测试之前,需要将Test.java编译成Test.class文件,将该文件放如到路径D:\jar\com\study\zl\classloader下,并将项目中的Test.javaTest.class文件删除,以免该类被系统类加载器加载到。

package com.study.zl.classloader;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
* @Author long
* @Date 2019/10/13 10:24
*/
public class App {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
SelfClassLoader selfClassLoader = new SelfClassLoader("D:\\jar");
Class<?> cls = selfClassLoader.loadClass("com.study.zl.classloader.Test");
if (cls != null) {
// 创建一个cls类型的对象
Object o = cls.newInstance();
// 获取该对象的say方法
Method say = cls.getMethod("say", null);
// 调用该类型的say方法
say.invoke(o, null);
// 查看 cls 的类加载器
System.out.println(cls.getClassLoader().toString());
}
}
// 输出结果
// hello,world
// com.study.zl.classloader.SelfClassLoader@7f31245a
}



package com.study.zl.classloader;
public class Test {

public void say(){
System.out.println("hello,world");
}

}

自定义一个类加载器,该类加载器继承自ClassLoader,重写 findClass 方法来获取 Class。该方法首先会调用getData方法来获取类的字节码,然后调用父类的defineClass方法将字节码转化未 Class并返回。

package com.study.zl.classloader;

import java.io.*;

/**
* @Author long
* @Date 2019/10/13 10:40
*/
public class SelfClassLoader extends ClassLoader {

private String classpath;

public SelfClassLoader(String classpath) {
this.classpath = classpath;
}

@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] data = getData(name);
if (data != null) {
// 调用 defineClass 方法将字节码转化为Class
return defineClass(name, data, 0 , data.length);
}
} catch (IOException e) {
e.printStackTrace();
}
return super.findClass(name);
}


/**
* 返回类的字节码
*
* @param className
* @return
* @throws IOException
*/
private byte[] getData(String className) throws IOException {
InputStream in = null;
ByteArrayOutputStream out = null;
String path = classpath + File.separatorChar + className.replace('.', File.separatorChar) + ".class";
try {
in = new FileInputStream(path);
out = new ByteArrayOutputStream();
byte[] buffer = new byte[2048];
int len = 0;
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
return out.toByteArray();
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
in.close();
out.close();
}
return null;
}
}

资源文件的读取

在 java 中可以使用 Class 或者是 ClassLoader 读取资源文件。比如现在项目的resources目录下面有一个 application.properties文件,可以使用项目中某个类的class.getResource方法或者某个类的类加载器class.getClassLoader().getResource方法。方法所传资源文件路径是向对于resources的目录。

package com.study.zl.classloader.p2;

import java.net.URL;

/**
* @Author long
* @Date 2019/10/13 14:26
*/
public class App {
public static void main(String[] args) {
Class<App> appClass = App.class;
// 通过 class 获取资源时,必须有 / ,否则会从该类所在包下面读取
URL resource1 = appClass.getResource("/application.properties");
// 通过 ClassLoader 读取资源,所不同的是 ClassLoader 还有一个 getResources 方法
URL resource2 = appClass.getClassLoader().getResource("application.properties");
System.out.println(resource1.toString());
System.out.println(resource2.toString());
}

// 输出结果
// file:/D:/code/struct-parent/interview-ready/target/classes/application.properties
// file:/D:/code/struct-parent/interview-ready/target/classes/application.properties
}

读取Properties文件

在spring核心包中提供了一个工具类PropertiesLoaderUtils,该工具类可以将ClassLoader读取到的资源转换未Properties文件。

//  pom.xml文件引入 spring framework 核心包依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>2.5.6</version>
</dependency>

// application.properties文件加入内容
#Tue Jun 18 23:47:55 GMT 2019
server.port=8080
server.name=spring-boot-application

package com.study.zl.classloader.p2;

import org.springframework.core.io.UrlResource;
import org.springframework.core.io.support.PropertiesLoaderUtils;

import java.io.IOException;
import java.net.URL;
import java.util.Enumeration;
import java.util.Properties;

/**
* @Author long
* @Date 2019/10/13 14:26
*/
public class App {
public static void main(String[] args) {
Class<App> appClass = App.class;
// 定义Properties对象
Properties properties = new Properties();
try {
// 读取资源集合
Enumeration<URL> urls = appClass.getClassLoader().getResources("application.properties");
while (urls.hasMoreElements()) {
// 使用工具类加载
properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource(urls.nextElement())));
}
} catch (IOException e) {
e.printStackTrace();
}
// 输出
System.out.println(properties);
}
// 输出结果
// {server.port=8080, server.name=spring-boot-application}
}