下面的代码是否破坏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. 为什么违背了可见性原则?


回答:

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

回到顶部