Tomcat的类加载设计
类载入:1 new隐式载入,2 显示载入loadclass()
我们都知道:Java默认的类加载机制是委派模式,委派的过程如下。
1)从缓存中加载
2) 如果没有,则从父加载器中加载
3) 如果父加载器没有,从当前加载器加载
4) 没有,抛异常
servlet容器不应该完全信任它正在运行的servlet类。 servlet应该只允许访问web-inf/class和web-inf/lib中的类。
而tomcat自己定义加载器后,可以加入限定规则。
下面是ClassLoader的loadClass()方法源码逻辑
protected Class<?> loadClass(String name, boolean resolve)
{
synchronized (getClassLoadingLock(name)) {
//1 查看是否加载过
Class<?> c = findLoadedClass(name);//
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
//2 父加载器加载
c = parent.loadClass(name, false);
} else { //3 系统加载器加载
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
}
if (c == null) {
long t1 = System.nanoTime();
//4自定义的加载器 加载
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
复制代码
上图是tomcat的类加载器继承图,顶层设计在loader接口。loader接口的setContainer
方法将载入器和servlet容器关联。如果context容器的类被修改了,载入器也可以支持对类的自动重载。载入器会调用context接口的reloader方法来载入。WebappLoader中还包含一个WebappClassLoader,该类是默认的类加载器。当webappLoader随着生命周期启动时,会调用内部createClassLoader()方法来创建一个默认的类加载器。
private WebappClassLoader createClassLoader() throws Exception {
Class<?> clazz = Class.forName(loaderClass);
WebappClassLoader classLoader = null;
if (parentClassLoader == null) {
parentClassLoader = container.getParentClassLoader();
}
Class<?>[] argTypes = { ClassLoader.class };
Object[] args = { parentClassLoader };
Constructor<?> constr = clazz.getConstructor(argTypes);
classLoader = (WebappClassLoader) constr.newInstance(args);
return classLoader;
}
复制代码
设置仓库(即设置在哪加载类)
上面拿到classLoader后,会调用classLoader.addRepository(repositories[i])
内部调用WebappClassLoader# addRepository(String repository)
设置仓库。
public void addRepository(String repository) {
if (repository.startsWith("/WEB-INF/lib")
|| repository.startsWith("/WEB-INF/classes"))
return;
try {
URL url = new URL(repository);
super.addURL(url);
hasExternalRepositories = true;
repositoryURLs = null;
} catch (MalformedURLException e) {
}
}
复制代码
这是整个载入过程(省略部分不重要代码)
protected void startInternal() throws LifecycleException {
URLStreamHandlerFactory streamHandlerFactory =
DirContextURLStreamHandlerFactory.getInstance();
if (first) {
first = false;
try {
URL.setURLStreamHandlerFactory(streamHandlerFactory);
} catch (Exception e) {
} catch (Throwable t) {
}
}
// Construct a class loader based on our current repositories list
try {
classLoader = createClassLoader();
classLoader.setResources(container.getResources());
classLoader.setDelegate(this.delegate);
classLoader.setSearchExternalFirst(searchExternalFirst);
if (container instanceof StandardContext) {
classLoader.setAntiJARLocking(
((StandardContext) container).getAntiJARLocking());
classLoader.setClearReferencesStatic(
((StandardContext) container).getClearReferencesStatic());
classLoader.setClearReferencesStopThreads(
((StandardContext) container).getClearReferencesStopThreads());
classLoader.setClearReferencesStopTimerThreads(
((StandardContext) container).getClearReferencesStopTimerThreads());
classLoader.setClearReferencesHttpClientKeepAliveThread(
((StandardContext) container).getClearReferencesHttpClientKeepAliveThread());
}
for (int i = 0; i < repositories.length; i++) {
classLoader.addRepository(repositories[i]);
}
// Configure our repositories
setRepositories();//--设置仓库
setClassPath();//--设置类路径
setPermissions();//--借用安全管理器,设置类载入器对路径的访问权限
((Lifecycle) classLoader).start();
// Binding the Webapp class loader to the directory context
DirContextURLStreamHandler.bind(classLoader,
this.container.getResources());
StandardContext ctx=(StandardContext)container;
String contextName = ctx.getName();
if (!contextName.startsWith("/")) {
contextName = "/" + contextName;
}
ObjectName cloname = new ObjectName
(MBeanUtils.getDomain(ctx) + ":type=WebappClassLoader,context="
+ contextName + ",host=" + ctx.getParent().getName());
Registry.getRegistry(null, null)
.registerComponent(classLoader, cloname, null);
} catch (Throwable t) {
setState(LifecycleState.STARTING);
}
复制代码
tomcat中正在干活的类加载器是WebappClassLoader类。该类的设计考虑到了优化和安全两个方面。例如:会缓存以前已经加载过的类,同时,还会缓存加载失败的类的名字。WebappClassLoader会在仓库列表和指定的JAR文件搜索。
WebappClassLoader不允许载入指定的某些类,名字存在triggers和packageTriggers
那么,它是如何完成类的缓存和载入任务的呢?
public Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
if (debug >= 2)
log("loadClass(" + name + ", " + resolve + ")");
Class clazz = null;
// Don't load classes if class loader is stopped
if (!started) {
log("Lifecycle error : CL stopped");
throw new ClassNotFoundException(name);
}
// (0) Check our previously loaded local class cache
clazz = findLoadedClass0(name);//先在本地找,看是否加载过.(根据类名找类)
if (clazz != null) {
if (debug >= 3)
log(" Returning class from cache");
if (resolve)
resolveClass(clazz);
return (clazz);
}
// (0.1) Check our previously loaded class cache
clazz = findLoadedClass(name);//看上一级 即classloader是否加载过
if (clazz != null) {
if (debug >= 3)
log(" Returning class from cache");
if (resolve)
resolveClass(clazz);
return (clazz);
}
// (0.2) Try loading the class with the system class loader, to prevent
// the webapp from overriding J2SE classes
try {//--使用系统的类加载器进行加载
clazz = system.loadClass(name);
if (clazz != null) {
if (resolve)
resolveClass(clazz);
return (clazz);
}
} catch (ClassNotFoundException e) {
// Ignore
}
// (0.5) Permission to access this class when using a SecurityManager
if (securityManager != null) {
int i = name.lastIndexOf('.');
if (i >= 0) {
try {//--检查该类是否允许被加载
securityManager.checkPackageAccess(name.substring(0,i));
} catch (SecurityException se) {
String error = "Security Violation, attempt to use " + "Restricted Class: " + name;
System.out.println(error);
se.printStackTrace();
log(error);
throw new ClassNotFoundException(error);
}
}
}
boolean delegateLoad = delegate || filter(name);
// (1) Delegate to our parent if requested
if (delegateLoad) {
if (debug >= 3)
log(" Delegating to parent classloader");
ClassLoader loader = parent;
if (loader == null)
loader = system;
try {
clazz = loader.loadClass(name);
if (clazz != null) {
if (debug >= 3)
log(" Loading class from parent");
if (resolve)
resolveClass(clazz);
return (clazz);
}
} catch (ClassNotFoundException e) {
;
}
}
// (2) Search local repositories
if (debug >= 3)
log(" Searching local repositories");
try {
clazz = findClass(name);
if (clazz != null) {
if (debug >= 3)
log(" Loading class from local repository");
if (resolve)
resolveClass(clazz);
return (clazz);
}
} catch (ClassNotFoundException e) {
;
}
// (3) Delegate to parent unconditionally
if (!delegateLoad) {
if (debug >= 3)
log(" Delegating to parent classloader");
ClassLoader loader = parent;
if (loader == null)
loader = system;
try {
clazz = loader.loadClass(name);
if (clazz != null) {
if (debug >= 3)
log(" Loading class from parent");
if (resolve)
resolveClass(clazz);
return (clazz);
}
} catch (ClassNotFoundException e) {
;
}
}
// This class was not found
throw new ClassNotFoundException(name);
}
复制代码