tomcat6源码研究:tomcat类加载机制
作者:互联网
首先我们回顾一下java虚拟机的类加载机制。
bootstrap class loader(用于加载JRE\lib\rt.jar或者Xbootclasspath指定的jar包)
extension class loader(用于加载标准扩展目录/jdk/jre/lib/ext)
system class loader(用于加载 CLASSPATH环境变量下或者DJava.class.path的目录和jar文件)
Custom class loader(用户自己定义的类加载器)
这些类加载器遵守双亲委派模型,加载文件的时候首先会委派给自己的父加载器加载,父加载器已经加载,则成功。如果父加载器不能加载的情况下会向下委派给子加载器进行加载。
tomcat的Servlet容器需要定义自己的类加载器,有两个原因:
一方面可以阻止它加载别的不可信任但是却在虚拟机中运行的类,这样可以解决安全问题。tomcat的servlet容器只能加载WEB-INF/classes目录和其子目录和工程依赖的WEB-INF/lib目下的类,这种加载器需要实现接口 org.apache.catalina.Loader.java。
另一个方面可以当 WEB-INF/classes or WEB-INF/lib 下面的文件改变时自动加载,tomcat会启动一个线程去监测这两个目录下文件的变化,这种加载器实现接口org.apache.catalina.loader.Reloader.java。可以,本地调试时,修改类文件时是不用重启tomcat的
两个文件的源码如下:
public interface Loader {
// ------------------------------------------------------------- Properties
/**
* Execute a periodic task, such as reloading, etc. This method will be
* invoked inside the classloading context of this container. Unexpected
* throwables will be caught and logged.
*/
public void backgroundProcess();
/**
* Return the Java class loader to be used by this Container.
*/
public ClassLoader getClassLoader();
/**
* Return the Container with which this Loader has been associated.
*/
public Container getContainer();
/**
* Set the Container with which this Loader has been associated.
*
* @param container The associated Container
*/
public void setContainer(Container container);
/**
* Return the "follow standard delegation model" flag used to configure
* our ClassLoader.
*/
public boolean getDelegate();
/**
* Set the "follow standard delegation model" flag used to configure
* our ClassLoader.
*
* @param delegate The new flag
*/
public void setDelegate(boolean delegate);
/**
* Return descriptive information about this Loader implementation and
* the corresponding version number, in the format
* <code><description>/<version></code>.
*/
public String getInfo();
/**
* Return the reloadable flag for this Loader.
*/
public boolean getReloadable();
/**
* Set the reloadable flag for this Loader.
*
* @param reloadable The new reloadable flag
*/
public void setReloadable(boolean reloadable);
// --------------------------------------------------------- Public Methods
/**
* Add a property change listener to this component.
*
* @param listener The listener to add
*/
public void addPropertyChangeListener(PropertyChangeListener listener);
/**
* Add a new repository to the set of repositories for this class loader.
*
* @param repository Repository to be added
*/
public void addRepository(String repository);
/**
* Return the set of repositories defined for this class loader.
* If none are defined, a zero-length array is returned.
*/
public String[] findRepositories();
/**
* Has the internal repository associated with this Loader been modified,
* such that the loaded classes should be reloaded?
*/
public boolean modified();
/**
* Remove a property change listener from this component.
*
* @param listener The listener to remove
*/
public void removePropertyChangeListener(PropertyChangeListener listener);
}
public interface Reloader {
/**
* Add a new repository to the set of places this ClassLoader can look for
* classes to be loaded.
*
* @param repository Name of a source of classes to be loaded, such as a
* directory pathname, a JAR file pathname, or a ZIP file pathname
*
* @exception IllegalArgumentException if the specified repository is
* invalid or does not exist
*/
public void addRepository(String repository);
/**
* Return a String array of the current repositories for this class
* loader. If there are no repositories, a zero-length array is
* returned.
*/
public String[] findRepositories();
/**
* Have one or more classes or resources been modified so that a reload
* is appropriate?
*/
public boolean modified();
}
tomcat类加载器的UML图如下
先来看看WebappLoader.java,它创建了一个WebappClassLoader的实例作为自己的类加载器。它实现了Loader.java接口,并且跟其他catalina组件一样,也实现了 org.apache.catalina.Lifecycle接口以便能被其他容器打开和关闭。同时也实现了java.beans.PropertyChangeListener接口,当属性改变时通知相关容器。而WebappLoader本身的重启是由Context(tomcat的容器)来控制的。
WebappLoader的启动的时候会发生下面几个工作:
如果没有初始化,则初始化并且注册容器;如果是第一次初始化,为JNDI注册流处理器;创建类加载器;设置repository;设置classpath;设置权限;为目录绑定类加载器。
public void start() throws LifecycleException {
// Validate and update our current component state
if( ! initialized ) init();
if (started)
throw new LifecycleException
(sm.getString("webappLoader.alreadyStarted"));
if (log.isDebugEnabled())
log.debug(sm.getString("webappLoader.starting"));
lifecycle.fireLifecycleEvent(START_EVENT, null);
started = true;
if (container.getResources() == null) {
log.info("No resources for " + container);
return;
}
// Register a stream handler factory for the JNDI protocol
URLStreamHandlerFactory streamHandlerFactory =
new DirContextURLStreamHandlerFactory();
if (first) {
first = false;
try {
URL.setURLStreamHandlerFactory(streamHandlerFactory);
} catch (Exception e) {
// Log and continue anyway, this is not critical
log.error("Error registering jndi stream handler", e);
} catch (Throwable t) {
// This is likely a dual registration
log.info("Dual registration of jndi stream handler: "
+ t.getMessage());
}
}
// Construct a class loader based on our current repositories list
try {
classLoader = createClassLoader();
classLoader.setResources(container.getResources());
classLoader.setDelegate(this.delegate);
if (container instanceof StandardContext)
classLoader.setAntiJARLocking(((StandardContext) container).getAntiJARLocking());
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());
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);
} catch (Throwable t) {
log.error( "LifecycleException ", t );
throw new LifecycleException("start: ", t);
}
}
标签:container,tomcat,void,public,tomcat6,源码,loader,class,加载 来源: https://blog.51cto.com/u_15095774/2718562