标签:
0.2.1)原因1:因为servlet容器不应该完全信任它正在运行的servlet类;0.2.2)原因2:如果使用系统类的载入器载入某个servlet类所使用的全部类,那么servlet就能够访问所有的类,包括当前运行的java 虚拟机中环境变量CLASSPATH指明的路径下的所有的类和库,这是非常危险的;因为 servlet应该只允许载入 WEB-INF/classes目录及其子目录下的类,和部署到 WEB-INF/lib 目录下的类(类库);0.2.3)原因3:为了提供自动重载的功能,即当 WEB-INF/classes 目录或 WEB-INF/lib目录下的类发生变化时,web 应用程序会重新载入这些类。在tomcat的载入器的实现中, 类载入器使用一个额外的线程来不断检查servlet 类和其它类的文件的时间戳。

step1)首先调用 系统类载入器,但并不会立即载入这个类;step2)相反,他会将载入类的任务交给其父类载入器——扩展类载入器;step3)而扩展类载入器也会将载入任务交给其父类载入器——引导类载入器;
case1)如果引导类载入器找不到需要载入的类,那么扩展类载入器会尝试 载入该类;case2)如果扩展类载入器也找不到该类,就轮到系统类载入器继续执行载入任务;case3)如果系统类载入器也找不到这个类,抛出 java.lang.ClassNotFoundException 异常;
r1)为了在载入类中指定某些规则;r2)为了缓存已经载入的类;r3)为了实现类的预载入,方便使用;
r1)应用程序中的 servlet 只能引用部署在 WEB-INF/classes 目录及其子目录下的类;r2)但是,servlet类不能访问其他路径中的类,即使这些类包含在运行当前的Tomcat的 jvm 的 classpath 环境变量中;r3)此外, servlet类只能访问 WEB-INF/lib 目录下的库,其他目录中的类库不能访问;
<Context path="/myApp" docBase="myApp" debug="0" reloadable="true" />
package org.apache.catalina;
import java.beans.PropertyChangeListener;
public interface Loader {
public ClassLoader getClassLoader();
public Container getContainer();
public void setContainer(Container container);
public DefaultContext getDefaultContext();
public void setDefaultContext(DefaultContext defaultContext);
public boolean getDelegate();
public void setDelegate(boolean delegate);
public String getInfo();
public boolean getReloadable();
public void setReloadable(boolean reloadable);
public void addPropertyChangeListener(PropertyChangeListener listener);
public void addRepository(String repository);
public String[] findRepositories();
public boolean modified();
public void removePropertyChangeListener(PropertyChangeListener listener);
}
w1)创建一个类载入器;w2)设置仓库;w3)设置类路径;w4)设置访问权限;w5)启动一个新线程来支持自动重载;
public void start() throws LifecycleException {
// org.apache.catalina.loader.WebappLoader.start(),为了看其 outline,我没有对该method做删减
// Validate and update our current component state
if (started)
throw new LifecycleException
(sm.getString("webappLoader.alreadyStarted"));
if (debug >= 1)
log(sm.getString("webappLoader.starting"));
lifecycle.fireLifecycleEvent(START_EVENT, null);
started = true;
if (container.getResources() == null)
return;
// Register a stream handler factory for the JNDI protocol
URLStreamHandlerFactory streamHandlerFactory =
new DirContextURLStreamHandlerFactory();
try {
URL.setURLStreamHandlerFactory(streamHandlerFactory);
} catch (Throwable t) {
// Ignore the error here.
}
// Construct a class loader based on our current repositories list
try {
classLoader = createClassLoader(); // 创建一个类载入器,下面是设置类载入器
classLoader.setResources(container.getResources());
classLoader.setDebug(this.debug);
classLoader.setDelegate(this.delegate);
for (int i = 0; i < repositories.length; i++) {
classLoader.addRepository(repositories[i]);
}
// Configure our repositories
setRepositories(); // 设置仓库
setClassPath(); // 设置类路径
setPermissions(); // 设置访问权限
if (classLoader instanceof Lifecycle)
((Lifecycle) classLoader).start();
// Binding the Webapp class loader to the directory context
DirContextURLStreamHandler.bind
((ClassLoader) classLoader, this.container.getResources());
} catch (Throwable t) {
throw new LifecycleException("start: ", t);
}
// Validate that all required packages are actually available
validatePackages();
// Start our background thread if we are reloadable
if (reloadable) {
log(sm.getString("webappLoader.reloading"));
try {
threadStart(); // 启动一个新线程来支持自动重载
} catch (IllegalStateException e) {
throw new LifecycleException(e);
}
}
}A1)可以不使用 WebappClassLoader 类的实例,而使用其他类的实例作为类载入器;A2)createClassLoader()方法的返回值是: WebappLoader类型的, 因此,如果自定义类型没有继承自 WebappClassLoader l类,createClassLoader方法就会抛出一个异常;
private WebappClassLoader createClassLoader()
throws Exception { // org.apache.catalina.loader.WebappLoader.createClassLoader()
Class clazz = Class.forName(loaderClass);
WebappClassLoader classLoader = null; // highlight line.
if (parentClassLoader == null) {
// Will cause a ClassCast is the class does not extend WCL, but
// this is on purpose (the exception will be caught and rethrown)
classLoader = (WebappClassLoader) clazz.newInstance();
} else {
Class[] argTypes = { ClassLoader.class };
Object[] args = { parentClassLoader };
Constructor constr = clazz.getConstructor(argTypes);
classLoader = (WebappClassLoader) constr.newInstance(args);
}
return classLoader;
} private void setRepositories() { // org.apache.catalina.loader.WebappLoader.setRepositories()
if (!(container instanceof Context))
return;
ServletContext servletContext =
((Context) container).getServletContext();
if (servletContext == null)
return;
// Loading the work directory
File workDir =
(File) servletContext.getAttribute(Globals.WORK_DIR_ATTR);
if (workDir == null)
return;
log(sm.getString("webappLoader.deploy", workDir.getAbsolutePath()));
DirContext resources = container.getResources();
// Setting up the class repository (/WEB-INF/classes), if it exists
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();
copyDir(classes, classRepository);
}
log(sm.getString("webappLoader.classDeploy", classesPath,
classRepository.getAbsolutePath()));
// Adding the repository to the class loader
classLoader.addRepository(classesPath + "/", classRepository);
}
// Setting up the JAR repository (/WEB-INF/lib), if it exists
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
try {
NamingEnumeration enum = resources.listBindings(libPath);
while (enum.hasMoreElements()) {
Binding binding = (Binding) enum.nextElement();
String filename = libPath + "/" + binding.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, binding.getName());
log(sm.getString("webappLoader.jarDeploy", filename,
destFile.getAbsolutePath()));
Resource jarResource = (Resource) binding.getObject();
if (copyJars) {
if (!copy(jarResource.streamContent(),
new FileOutputStream(destFile)))
continue;
}
JarFile jarFile = new JarFile(destFile);
classLoader.addJar(filename, jarFile, destFile);
}
} catch (NamingException e) {
// Silent catch: it's valid that no /WEB-INF/lib directory
// exists
} catch (IOException e) {
e.printStackTrace();
} }
}private void setClassPath() { // org.apache.catalina.loader.WebappLoader.setClassPath()
// Validate our current state information
if (!(container instanceof Context))
return;
ServletContext servletContext =
((Context) container).getServletContext();
if (servletContext == null)
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 ((layers < 3) && (loader != null)) {
if (!(loader instanceof URLClassLoader))
break;
URL repositories[] =
((URLClassLoader) loader).getURLs();
for (int i = 0; i < repositories.length; i++) {
String repository = repositories[i].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++;
}
// Store the assembled class path as a servlet context attribute
servletContext.setAttribute(Globals.CLASS_PATH_ATTR,
classpath.toString());
}private void setPermissions() {
// org.apache.catalina.loader.WebappLoader.setPermission()
if (System.getSecurityManager() == null)
return;
if (!(container instanceof Context))
return;
// Tell the class loader the root of the context
ServletContext servletContext =
((Context) container).getServletContext();
// Assigning permissions for the work directory
File workDir =
(File) servletContext.getAttribute(Globals.WORK_DIR_ATTR);
if (workDir != null) {
try {
String workDirPath = workDir.getCanonicalPath();
classLoader.addPermission
(new FilePermission(workDirPath, "read,write"));
classLoader.addPermission
(new FilePermission(workDirPath + File.separator + "-",
"read,write,delete"));
} catch (IOException e) {
// Ignore
}
}
try {
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);
if (contextRoot != null) {
if (libURL != null) {
File rootDir = new File(contextRoot);
File libDir = new File(rootDir, "WEB-INF/lib/");
try {
String path = libDir.getCanonicalPath();
classLoader.addPermission(path);
} catch (IOException e) {
}
}
} else {
if (workDir != null) {
if (libURL != null) {
File libDir = new File(workDir, "WEB-INF/lib/");
try {
String path = libDir.getCanonicalPath();
classLoader.addPermission(path);
} catch (IOException e) {
}
}
if (classesURL != null) {
File classesDir = new File(workDir, "WEB-INF/classes/");
try {
String path = classesDir.getCanonicalPath();
classLoader.addPermission(path);
} catch (IOException e) {
}
}
}
}
} catch (MalformedURLException e) {
}
}o1)使线程休眠一段时间,时长由变量checkInterval指定,以秒为单位;o2)调用WebappLoader 实例的类载入器的modified方法检查已经载入的类是否被修改,若没有类修改,则重新执行循环;o3)若某个已经载入的类被修改了,则调用私有方法 notifyContext(),通知与 WebappLoader实例关联的 Context容器重新载入相关类;
public synchronized void reload() { //org.apache.catalina.core.StandardContext.reload()
// Validate our current component state
if (!started)
throw new IllegalStateException
(sm.getString("containerBase.notStarted", logName()));
// Make sure reloading is enabled
// if (!reloadable)
// throw new IllegalStateException
// (sm.getString("standardContext.notReloadable"));
log(sm.getString("standardContext.reloadingStarted"));
// Stop accepting requests temporarily
setPaused(true);
// Binding thread
ClassLoader oldCCL = bindThread();
// Shut down our session manager
if ((manager != null) && (manager instanceof Lifecycle)) {
try {
((Lifecycle) manager).stop();
} catch (LifecycleException e) {
log(sm.getString("standardContext.stoppingManager"), e);
}
}
// Shut down the current version of all active servlets
Container children[] = findChildren();
for (int i = 0; i < children.length; i++) {
Wrapper wrapper = (Wrapper) children[i];
if (wrapper instanceof Lifecycle) {
try {
((Lifecycle) wrapper).stop();
} catch (LifecycleException e) {
log(sm.getString("standardContext.stoppingWrapper",
wrapper.getName()),
e);
}
}
}
// Shut down application event listeners
listenerStop();
// Clear all application-originated servlet context attributes
if (context != null)
context.clearAttributes();
// Shut down filters
filterStop();
if (isUseNaming()) {
// Start
namingContextListener.lifecycleEvent
(new LifecycleEvent(this, Lifecycle.STOP_EVENT));
}
// Binding thread
unbindThread(oldCCL);
// Shut down our application class loader
if ((loader != null) && (loader instanceof Lifecycle)) {
try {
((Lifecycle) loader).stop();
} catch (LifecycleException e) {
log(sm.getString("standardContext.stoppingLoader"), e);
}
}
// Binding thread
oldCCL = bindThread();
// Restart our application class loader
if ((loader != null) && (loader instanceof Lifecycle)) {
try {
((Lifecycle) loader).start();
} catch (LifecycleException e) {
log(sm.getString("standardContext.startingLoader"), e);
}
}
// Binding thread
unbindThread(oldCCL);
// Create and register the associated naming context, if internal
// naming is used
boolean ok = true;
if (isUseNaming()) {
// Start
namingContextListener.lifecycleEvent
(new LifecycleEvent(this, Lifecycle.START_EVENT));
}
// Binding thread
oldCCL = bindThread();
// Restart our application event listeners and filters
if (ok) {
if (!listenerStart()) {
log(sm.getString("standardContext.listenerStartFailed"));
ok = false;
}
}
if (ok) {
if (!filterStart()) {
log(sm.getString("standardContext.filterStartFailed"));
ok = false;
}
}
// Restore the "Welcome Files" and "Resources" context attributes
postResources();
postWelcomeFiles();
// Restart our currently defined servlets
for (int i = 0; i < children.length; i++) {
if (!ok)
break;
Wrapper wrapper = (Wrapper) children[i];
if (wrapper instanceof Lifecycle) {
try {
((Lifecycle) wrapper).start();
} catch (LifecycleException e) {
log(sm.getString("standardContext.startingWrapper",
wrapper.getName()),
e);
ok = false;
}
}
}
// Reinitialize all load on startup servlets
loadOnStartup(children);
// Restart our session manager (AFTER naming context recreated/bound)
if ((manager != null) && (manager instanceof Lifecycle)) {
try {
((Lifecycle) manager).start();
} catch (LifecycleException e) {
log(sm.getString("standardContext.startingManager"), e);
}
}
// Unbinding thread
unbindThread(oldCCL);
// Start accepting requests again
if (ok) {
log(sm.getString("standardContext.reloadingCompleted"));
} else {
setAvailable(false);
log(sm.getString("standardContext.reloadingFailed"));
}
setPaused(false);
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(Context.RELOAD_EVENT, null);
}private static final String[] triggers = {
"javax.servlet.Servlet"
};private static final String[] packageTriggers = {
"javax",
"org.xml.sax",
"org.w3c.dom",
"org.apache.xerces",
"org.apache.xalan",
};public class ResourceEntry { // org.apache.catalina.loader.ResourceEntry
/**
* The "last modified" time of the origin file at the time this class
* was loaded, in milliseconds since the epoch.
*/
public long lastModified = -1;
/**
* Binary content of the resource.
*/
public byte[] binaryContent = null;
/**
* Loaded class.
*/
public Class loadedClass = null;
/**
* URL source from where the object was loaded.
*/
public URL source = null;
/**
* URL of the codebase from where the object was loaded.
*/
public URL codeBase = null;
/**
* Manifest (if the resource was loaded from a JAR).
*/
public Manifest manifest = null;
/**
* Certificates (if the resource was loaded from a JAR).
*/
public Certificate[] certificates = null;
}r1)因为所有已经载入的类都会缓存起来,所以载入类时要先检查本地缓存;r2)若本地缓存中没有,则检查上一层缓存,即调用 java.lang.ClassLoader 类的findLoadedClass() 方法;r3)若两个缓存中都没有,则使用系统的类载入器进行加载,防止 web 应用程序中的类覆盖J2EE 的类;r4)若启用了 SecurityManager,则检查是否允许载入该类。若该类是禁止载入的类,抛出 ClassNotFoundException异常;r5)若打开标志位 delegate,或者待载入的类是属于包触发器中的包名,则调用父载入器来载入相关类。如果父载入器是null,则使用系统的类载入器;r6)从当前仓库中载入相关的类;r7)若当前仓库中没有需要的类,且标志位delegate关闭,则使用父类载入器。若父类载入器为 null, 则使用系统的类载入器进行加载;r8)若仍未找到需要的类,则抛出 ClassNotFoundException 异常;
public final class Bootstrap {
public static void main(String[] args) {
//invoke: http://localhost:8080/Modern or http://localhost:8080/Primitive
// 为了通知StandardContext 实例到哪里查找应用程序目录,需要设置一个名为"catalina.base"的系统属性,其值为"user.dir"属性的值;
System.setProperty("catalina.base", System.getProperty("user.dir"));
Connector connector = new HttpConnector(); //实例化默认连接器
Wrapper wrapper1 = new SimpleWrapper(); // 为两个servlet类创建两个Wrapper实例;
wrapper1.setName("Primitive");
wrapper1.setServletClass("PrimitiveServlet");
Wrapper wrapper2 = new SimpleWrapper();
wrapper2.setName("Modern");
wrapper2.setServletClass("ModernServlet");
// 创建StandardContext 的一个实例,设置应用程序路径和上下文的文档根路径
Context context = new StandardContext();
// StandardContext's start method adds a default mapper
context.setPath("/myApp");
context.setDocBase("myApp");
// 上面的代码在功能上等同于下面的在server.xml 文件中的配置
// <Context path="/myApp" docBase="myApp" />
context.addChild(wrapper1); // 将两个Wrapper实例添加到Context容器中
context.addChild(wrapper2);
// 为它们设置访问路径的映射关系,这样Context 容器就能够定位到他们
// context.addServletMapping(pattern, name);
context.addServletMapping("/Primitive", "Primitive");
context.addServletMapping("/Modern", "Modern");
// add ContextConfig. This listener is important because it configures
// StandardContext (sets configured to true), otherwise StandardContext
// won't start
// 下一步,实例化一个监听器,并通过 Context容器注册它.
LifecycleListener listener = new SimpleContextConfig();
((Lifecycle) context).addLifecycleListener(listener);
// 接着,它会实例化WebappLoader类,并将其关联到Context容器.
// here is our loader
Loader loader = new WebappLoader();
// associate the loader with the Context
context.setLoader(loader);
// 然后,将Context容器与默认连接器相关联,调用默认连接器的initialize() and start()方法,
// 再调用 Context容器的 start() 方法,这样servlet容器准备就绪,可以处理servlet请求了.
connector.setContainer(context);
try {
connector.initialize();
((Lifecycle) connector).start();
((Lifecycle) context).start(); // attention: 从这一行开始(包括这一行),进行spec analysis.
// 接下来的几行代码仅仅显示出资源的docBase属性值和类载入器中所有的仓库的名字.
// now we want to know some details about WebappLoader
WebappClassLoader classLoader = (WebappClassLoader) loader.getClassLoader();
System.out.println("Resources' docBase: " + ((ProxyDirContext)classLoader.getResources()).getDocBase());
String[] repositories = classLoader.findRepositories();
for (int i=0; i<repositories.length; i++) {
System.out.println(" repository: " + repositories[i]);
}
// 最后,用户输入任意键后,程序退出.
// make the application wait until we press a key.
System.in.read();
((Lifecycle) context).stop();
}
catch (Exception e) {
e.printStackTrace();
}
}
}C1)web 应用程序中的载入器,或一个简单的载入器,都是 Catalina中最重要的组件;(干货——载入器是Catalina中最重要的组件)C2)载入器负责载入应用程序所需要的类,因此会使用一个内部类载入器:这个内部类载入器是一个自定义类,tomcat使用这个自定义的类载入器对 web应用程序上下文中要载入的类进行一些约束;C3)此外,自定义类载入器可以支持对载入类的缓存和对一个或多个被修改的类的自动重载;
E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\src>java -cp .;lib/servlet.jar;lib/catalina_4_1_24.jar;lib/catalina-5.5.4.jar;lib/naming-resources.jar;lib/naming-common.jar;lib/commons-collectio ns.jar;lib/catalina.jar;E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\webroot com.tomcat.chapter8.startup.Bootstrap HttpConnector Opening server socket on all host IP addresses HttpConnector[8080] Starting background thread WebappLoader[/myApp]: Deploying class repositories to work directory E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\src\work\_\_\myApp Starting Wrapper Primitive Starting Wrapper Modern StandardManager[/myApp]: Seeding random number generator class java.security.SecureRandom StandardManager[/myApp]: Seeding of random number generator has been completed Resources' docBase: E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\src\myApp // this line. Stopping wrapper Primitive Stopping wrapper Modern E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\src>Attention)这里还差了一个打印info, 在this line 下面一行: repository: /WEB-INF/classes/ ;
标签:
原文地址:http://blog.csdn.net/pacosonswjtu/article/details/51211142