tomcat源码解析--Loader
tomcat 如何加载 class 文件? 如何加载 lib 目录下的jar 文件?在 tomcat 中存在一个 Loader接口:
其中的主要功能包括
container: Loader 是与一个context关联的,负责在context下的所有servlet解析的时候使用
可以设置reloadable参数,repository是指需要加载的资源的路径
其子类 WebappLoader 负责了以上方法的实现,并且实现了其他一些接口(implements Lifecycle, Loader, PropertyChangeListener, MBeanRegistration )
具体的解析任务将会交给 webappclassLoader类
在tomcat的classLoader运作机制中,主要将资源分为3层
顶层是 webapp的classes 资源和lib 目录下的 jar 资源
第二层是 tomcat 自己lib目录下的资源
底层是 java jdk下的lib资源
当容器根据一个url寻找资源时,默认是从顶层开始。
WebappLoader的 主要 任务(start方法):
1. 创建classLoader
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默认使用 WebappClassLoader
2. 配置 classLoader:
classLoader.setResources(container.getResources());
classLoader.setDelegate(this.delegate);
classLoader.setSearchExternalFirst(searchExternalFirst);
if (container instanceof StandardContext) {
classLoader.setAntiJARLocking(
((StandardContext) container).getAntiJARLocking());
classLoader.setClearReferencesStopThreads(
((StandardContext) container).getClearReferencesStopThreads());
classLoader.setClearReferencesStopTimerThreads(
((StandardContext) container).getClearReferencesStopTimerThreads());
classLoader.setClearReferencesThreadLocals(
((StandardContext) container).getClearReferencesThreadLocals());
classLoader.setClearReferencesHttpClientKeepAliveThread(
((StandardContext) container).getClearReferencesHttpClientKeepAliveThread());
}
关于container.getResources() 对象 DirContext 将在之后分析 antiJARLocking 属性, 在classLoader. getResource方法中可见
clearReferencesStopThreads 见 classLoader.clearReferences -> clearReferencesThreads方法(关闭web app start的任何线程)
clearReferencesStopTimerThreads 见 classLoader.clearReferences -> clearReferencesThreads方法 用于 判断是否调用 clearReferencesStopTimerThread 方法, 默认 false
clearReferencesThreadLocals 见 clearReferences 方法
clearReferencesHttpClientKeepAliveThread 见 clearReferences 方法
3. 加载资源
for (int i = 0; i < repositories.length; i++) {
classLoader.addRepository(repositories);
}
4. 加载class文件和lib路径:
setRepositories 方法
4.1 设置默认工作路径:
if (!(container instanceof Context))
return;
ServletContext servletContext =
((Context) container).getServletContext();
if (servletContext == null)
return;
loaderRepositories=new ArrayList();
// Loading the work directory
File workDir =
(File) servletContext.getAttribute(Globals.WORK_DIR_ATTR);
if (workDir == null) {
log.info("No work dir for " + servletContext);
}
if( log.isDebugEnabled())
log.debug(sm.getString("webappLoader.deploy", workDir.getAbsolutePath()));
classLoader.setWorkDir(workDir);
4.2 得到classes 文件夹路径 资源
String classesPath = "/WEB-INF/classes";
DirContext classes = null;
try {
Object object = resources.lookup(classesPath);
if (object instanceof DirContext) {
classes = (DirContext) object;
}
} catch(NamingException e) {
// Silent catch: it's valid that no /WEB-INF/classes collection
// exists
}
if (classes != null) {
File classRepository = null;
String absoluteClassesPath =
servletContext.getRealPath(classesPath);
if (absoluteClassesPath != null) {
classRepository = new File(absoluteClassesPath);
} else {
classRepository = new File(workDir, classesPath);
classRepository.mkdirs();
if (!copyDir(classes, classRepository)) {
throw new IOException(
sm.getString("webappLoader.copyFailure"));
}
}
if(log.isDebugEnabled())
log.debug(sm.getString("webappLoader.classDeploy", classesPath,
classRepository.getAbsolutePath()));
// Adding the repository to the class loader
classLoader.addRepository(classesPath + "/", classRepository);
loaderRepositories.add(classesPath + "/" );
}
并且 把 相关的 classPath classRepository 设置到 classLoader中
4.3 lib 中的jar资源导入
String libPath = "/WEB-INF/lib";
classLoader.setJarPath(libPath);
DirContext libDir = null;
// Looking up directory /WEB-INF/lib in the context
try {
Object object = resources.lookup(libPath);
if (object instanceof DirContext)
libDir = (DirContext) object;
} catch(NamingException e) {
// Silent catch: it's valid that no /WEB-INF/lib collection
// exists
}
if (libDir != null) {
boolean copyJars = false;
String absoluteLibPath = servletContext.getRealPath(libPath);
File destDir = null;
if (absoluteLibPath != null) {
destDir = new File(absoluteLibPath);
} else {
copyJars = true;
destDir = new File(workDir, libPath);
destDir.mkdirs();
}
// Looking up directory /WEB-INF/lib in the context
NamingEnumeration<NameClassPair> enumeration = null;
try {
enumeration = libDir.list("");
} catch (NamingException e) {
IOException ioe = new IOException(sm.getString(
"webappLoader.namingFailure", libPath));
ioe.initCause(e);
throw ioe;
}
while (enumeration.hasMoreElements()) {
NameClassPair ncPair = enumeration.nextElement();
String filename = libPath + "/" + ncPair.getName();
if (!filename.endsWith(".jar"))
continue;
// Copy JAR in the work directory, always (the JAR file
// would get locked otherwise, which would make it
// impossible to update it or remove it at runtime)
File destFile = new File(destDir, ncPair.getName());
if( log.isDebugEnabled())
log.debug(sm.getString("webappLoader.jarDeploy", filename,
destFile.getAbsolutePath()));
// Bug 45403 - Explicitly call lookup() on the name to check
// that the resource is readable. We cannot use resources
// returned by listBindings(), because that lists all of them,
// but does not perform the necessary checks on each.
Object obj = null;
try {
obj = libDir.lookup(ncPair.getName());
} catch (NamingException e) {
IOException ioe = new IOException(sm.getString(
"webappLoader.namingFailure", filename));
ioe.initCause(e);
throw ioe;
}
if (!(obj instanceof Resource))
continue;
Resource jarResource = (Resource) obj;
if (copyJars) {
if (!copy(jarResource.streamContent(),
new FileOutputStream(destFile))) {
throw new IOException(
sm.getString("webappLoader.copyFailure"));
}
}
try {
JarFile jarFile = new JarFile(destFile);
classLoader.addJar(filename, jarFile, destFile);
} catch (Exception ex) {
// Catch the exception if there is an empty jar file
// Should ignore and continue loading other jar files
// in the dir
}
loaderRepositories.add( filename );
5. 设置 classpath, 设置webapp的环境变量
// Validate our current state information
if (!(container instanceof Context))
return;
ServletContext servletContext =
((Context) container).getServletContext();
if (servletContext == null)
return;
if (container instanceof StandardContext) {
String baseClasspath =
((StandardContext) container).getCompilerClasspath();
if (baseClasspath != null) {
servletContext.setAttribute(Globals.CLASS_PATH_ATTR,
baseClasspath);
return;
}
}
StringBuffer classpath = new StringBuffer();
// Assemble the class path information from our class loader chain
ClassLoader loader = getClassLoader();
int layers = 0;
int n = 0;
while (loader != null) {
if (!(loader instanceof URLClassLoader)) {
String cp=getClasspath( loader );
if( cp==null ) {
log.info( "Unknown loader " + loader + " " + loader.getClass());
break;
} else {
if (n > 0)
classpath.append(File.pathSeparator);
classpath.append(cp);
n++;
}
break;
//continue;
}
URL repositories[] =
((URLClassLoader) loader).getURLs();
for (int i = 0; i < repositories.length; i++) {
String repository = repositories.toString();
if (repository.startsWith("file://"))
repository = repository.substring(7);
else if (repository.startsWith("file:"))
repository = repository.substring(5);
else if (repository.startsWith("jndi:"))
repository =
servletContext.getRealPath(repository.substring(5));
else
continue;
if (repository == null)
continue;
if (n > 0)
classpath.append(File.pathSeparator);
classpath.append(repository);
n++;
}
loader = loader.getParent();
layers++;
}
this.classpath=classpath.toString();
// Store the assembled class path as a servlet context attribute
servletContext.setAttribute(Globals.CLASS_PATH_ATTR,
classpath.toString());
通过循环将各个loader中的资源路径加载到classpath 变量中
6. setPermissions 设置资源 权限
判断是否 存在 SecurityManager
if (!Globals.IS_SECURITY_ENABLED)
return;
if (!(container instanceof Context))
return;
只允许根目录的读写权限,和classes 、lib目录的读写删权限,其他根目录下的资源不能碰
URL rootURL = servletContext.getResource("/");
classLoader.addPermission(rootURL);
String contextRoot = servletContext.getRealPath("/");
if (contextRoot != null) {
try {
contextRoot = (new File(contextRoot)).getCanonicalPath();
classLoader.addPermission(contextRoot);
} catch (IOException e) {
// Ignore
}
}
URL classesURL = servletContext.getResource("/WEB-INF/classes/");
classLoader.addPermission(classesURL);
URL libURL = servletContext.getResource("/WEB-INF/lib/");
classLoader.addPermission(libURL);
7. 启动 classloader, 绑定 JMX
if (classLoader instanceof Lifecycle)
((Lifecycle) classLoader).start();
// Binding the Webapp class loader to the directory context
DirContextURLStreamHandler.bind
((ClassLoader) classLoader, this.container.getResources());
StandardContext ctx=(StandardContext)container;
Engine eng=(Engine)ctx.getParent().getParent();
String path = ctx.getPath();
if (path.equals("")) {
path = "/";
}
ObjectName cloname = new ObjectName
(ctx.getEngineName() + ":type=WebappClassLoader,path="
+ path + ",host=" + ctx.getParent().getName());
Registry.getRegistry(null, null)
.registerComponent(classLoader, cloname, null);
页:
[1]