下面的代码是否破坏Java中类加载器的可见性原则?
tomcat加载spring-web模块的时候,需要发现spring-web的提供的实现类,就用到了SPI机制,会使用ServiceLoader#load方法拿到所有实现ServletContainerInitializer接口的类.
我看到一个文章说,这会破坏Java的类加载过程的可见性原则.
public static <S> ServiceLoader<S> load(Class<S> service) { ClassLoader cl = Thread.currentThread().getContextClassLoader();
// java.util.ServiceLoader.LazyIterator#nextService
// c = Class.forName(cn, false, loader);
return ServiceLoader.load(service, cl);
}
上面的代码我看实际上使用了加载器sun.misc.Launcher.AppClassLoader来加载实现了load方法拿到所有实现ServletContainerInitializer接口的类.
sun.misc.Launcher#Launcher
public Launcher() { ExtClassLoader var1;
try {
var1 = Launcher.ExtClassLoader.getExtClassLoader();
} catch (IOException var10) {
throw new InternalError("Could not create extension class loader", var10);
}
try {
this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
} catch (IOException var9) {
throw new InternalError("Could not create application class loader", var9);
}
Thread.currentThread().setContextClassLoader(this.loader);
String var2 = System.getProperty("java.security.manager");
if (var2 != null) {
SecurityManager var3 = null;
if (!"".equals(var2) && !"default".equals(var2)) {
try {
var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();
} catch (IllegalAccessException var5) {
} catch (InstantiationException var6) {
} catch (ClassNotFoundException var7) {
} catch (ClassCastException var8) {
}
} else {
var3 = new SecurityManager();
}
if (var3 == null) {
throw new InternalError("Could not create SecurityManager: " + var2);
}
System.setSecurityManager(var3);
}
}
和下面的代码效果是一样的吧?
return ServiceLoader.load(service, null);
- 为什么违背了可见性原则?
回答:
1、首先要理解为什么会说SPI破坏了双亲委派?其实就是诸如rt.jar里定义的接口是由BootstrapClassLoader类加载器完成加载的,但是这些接口的实现类BootstrapClassLoader类加载器找不到,因为它们是由第三方的jar包实现的,是在classPath下的,应该是由AppClassLoader类加载器来加载的,对于BootstrapClassLoader来说它还能委派给哪个父加载器呢?可是又不能向下委派,所以就只能指定一个线程上下文类加载器 (Thread Context ClassLoader)。
这个类加载器可以通过java.lang.Thread类的setContext-ClassLoader()方法进行设置,如果创建线程时还未设置,它将会从父线程中继承一个,如果在应用程序的全局范围内都没有设置过的话,那这个类加载器默认就是应用程序类加载器(AppClassLoader),所以最后SPI可以正常运行,但是我们说它打破了双亲委派机制
2、sun.misc.Launcher.AppClassLoader的顶级抽象类就是java.lang.ClassLoader,所以你说
“这里没有执行java.lang.ClassLoader#loadClass(java.lang.String, boolean)双亲委派代码”其实最后还是执行的了,是SPI交由AppClassLoader已经找到了第三方的实现类把它加载进JVM了,所以不需要继续向上委托父加载器了
3、SPI就是违反双亲委派比较经典的例子呀,具体一点说——常常会被拿来作SPI的例子的JDBC的例子,JAVA只给了操纵数据库的接口,具体要怎么实现是各家厂商自己的事;事实上你也可以自己写一个类加载器继承java.lang.ClassLoader,重写它的loadClass方法,就算是打破双亲委派机制了
回答:
public class WebappClassLoader extends ClassLoader { public WebappClassLoader(ClassLoader parent) {
super(parent);
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
Class<?> result = findLoadedClass(name);
if (result == null) {
try {
// 先尝试自己加载
result = findClass(name);
} catch (ClassNotFoundException e) {
// 如果自己加载失败,再委派给父类加载器
result = super.loadClass(name);
}
}
return result;
}
}
以上是 下面的代码是否破坏Java中类加载器的可见性原则? 的全部内容, 来源链接: utcz.com/p/945273.html