git
前言
老問題了,到網上也一搜一堆答案,比如,随便來一篇
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5CN1MzMiBDZhhjY2UzN2AzNjdzYwYGMhJTZ5EDZkdjZk9CX0JXZ252bj91Ztl2Lc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
乍一看好像沒什麼問題,但是在掌握自定義類加載器之後,知道如何打破雙親委派之後再回頭來看這段話發現有兩個問題:
1:憑什麼你認為我現在是ApplicationClassLoader? 畢竟很多架構都會自定義類加載器的
2: 憑什麼你認為我一定要走雙親委派?
懷疑當然要有個證據,那麼我們就寫一段程式:
1:打破雙親委派
2:寫一個java.lang.String類
代碼見git
《2020最新Java基礎精講視訊教程和學習路線!》
一些重要的地方截取出來看看package java.lang;
public class String {
private Integer a;
public Integer getA() {
return a;
}
public void setA(Integer a) {
this.a = a;
}
}package org.wayne;
import org.wayne.util.ClassLoaderUtil;
import java.lang.reflect.Method;
public class RegisterDriverUtil {
public static void register(String name){
Class pluginClass = ClassLoaderUtil.getPluginClass(RegisterDriverUtil.class);
try {
Method method = pluginClass.getDeclaredMethod("register", String.class);
method.setAccessible(true);
method.invoke(pluginClass.newInstance(),name);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}package org.wayne;
public class RegisterDriverUtil {
public void register(String name) throws ClassNotFoundException {
Class.forName(name);
}
}public static void test8(){
EnvironmentUtil.setEnv(EnvEnum.A);
RegisterDriverUtil.register("java.lang.String");
EnvironmentUtil.clearEnv();
}
打包,在wsl下運作,結果如下
推測:
1:代碼裡是用自定義類加載器直接加載的java.lang.String類,并沒有走雙親委派
2:由報錯來看,是java.lang.ClassLoader阻止了加載
原因
無論何種自定義類加載器,最後都是調用的defineClass方法加載byte[],注釋如下
protected final Class> defineClass(String name, byte[] b, int off, int len,
ProtectionDomain protectionDomain)
throws ClassFormatError
{
protectionDomain = preDefineClass(name, protectionDomain);
String source = defineClassSourceLocation(protectionDomain);
Class> c = defineClass1(name, b, off, len, protectionDomain, source);
postDefineClass(c, protectionDomain);
return c;
}
private ProtectionDomain preDefineClass(String name,
ProtectionDomain pd)
{
if (!checkName(name))
throw new NoClassDefFoundError("IllegalName: " + name);
// Note: Checking logic in java.lang.invoke.MemberName.checkForTypeAlias
// relies on the fact that spoofing is impossible if a class has a name
// of the form "java.*"
if ((name != null) && name.startsWith("java.")) {
throw new SecurityException
("Prohibited package name: " +
name.substring(0, name.lastIndexOf('.')));
}
if (pd == null) {
pd = defaultDomain;
}
if (name != null) checkCerts(name, pd.getCodeSource());
return pd;
}
結論
1:無論何種自定義類加載器,最終都會調用ClassLoader.defineClass
2:ClassLoader.defineClass中會檢查類名,類名以java.開頭的,不予加載