java中载入器分为以下3种:
关于类加载器可以参考:http://blog.csdn.net/shakespeare001/article/details/51765353
Tomcat使用自定义载入器的原因:
Resources 和 Repositories:
Loader接口及其实现类UML图:
本章使用的Context容器是由Tomcat提供的StandardContext。
启动类的代码:
package ex08.pyrmont.startup;
import ex08.pyrmont.core.SimpleWrapper;
import ex08.pyrmont.core.SimpleContextConfig;
import org.apache.catalina.Connector;
import org.apache.catalina.Context;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.Loader;
import org.apache.catalina.Wrapper;
import org.apache.catalina.connector.http.HttpConnector;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.loader.WebappClassLoader;
import org.apache.catalina.loader.WebappLoader;
import org.apache.naming.resources.ProxyDirContext;
public final class Bootstrap {
public static void main(String[] args) {
System.setProperty("catalina.base", System.getProperty("user.dir"));
Connector connector = new HttpConnector();
Wrapper wrapper1 = new SimpleWrapper();
wrapper1.setName("Primitive");
wrapper1.setServletClass("PrimitiveServlet");
Wrapper wrapper2 = new SimpleWrapper();
wrapper2.setName("Modern");
wrapper2.setServletClass("ModernServlet");
Context context = new StandardContext();
context.setPath("/myApp");//设置context的path属性,path是访问这个web应用的URL
context.setDocBase("myApp");//设置context的docBase属性,docBase是web应用的本地路径
context.addChild(wrapper1);
context.addChild(wrapper2);
context.addServletMapping("/Primitive", "Primitive");
context.addServletMapping("/Modern", "Modern");
LifecycleListener listener = new SimpleContextConfig();
((Lifecycle) context).addLifecycleListener(listener);
Loader loader = new WebappLoader();创建一个WebappLoader
context.setLoader(loader);
connector.setContainer(context);
try {
connector.initialize();
((Lifecycle) connector).start();
((Lifecycle) context).start();//Context容器启动时,同时新起一个线程来运行关联的WebappLoader
WebappClassLoader classLoader = (WebappClassLoader) loader.getClassLoader();从WebappLoader中获取一个类加载器
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]);
}
System.in.read();
((Lifecycle) context).stop();
}
catch (Exception e) {
e.printStackTrace();
}
}
}
WebappLoader类start()方法执行时,主要完成以下4个工作:
createClassLoader()执行时时序图:
setRepositories()方法时序图,该方法执行后类载入器就能在WEB-INF/classes和WEB-INF/lib两个目录下载入相关类
setClassPath()的代码如下:
private void setClassPath() {
if (!(container instanceof Context))//验证容器信息
return;
ServletContext servletContext = ((Context) container).getServletContext();
if (servletContext == null)
return;
StringBuffer classpath = new StringBuffer();//将所有Class路径存放到classpath字符串中
ClassLoader loader = getClassLoader();
int layers = 0;
int n = 0;
//循环最多执行3次(ExtensionClassLoader、ApplicationClassLoader、UserClassLoader),将所有类的URL都添加到classpath中
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++;
}
//将classpath和Globals.CLASS_PATH_ATTR属性关联
servletContext.setAttribute(Globals.CLASS_PATH_ATTR,
classpath.toString());
}