您的位置:首页 > 运维架构 > Tomcat

tomcat6源码研究:tomcat类加载机制

2013-12-24 15:04 471 查看
首先我们回顾一下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接口,当属性改变时通知相关容器。而[b]WebappLoader本身的重启是由Context(tomcat的容器)来控制的。[/b]
[b][b]WebappLoader的启动的时候会发生下面几个工作:

[/b][/b]
[b][b]如果没有初始化,则初始化并且注册容器;如果是第一次初始化,为JNDI注册流处理器;创建类加载器;设置repository;设置classpath;设置权限;为目录绑定类加载器。[/b][/b]
public void start() throws LifecycleException {
[b] // 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);

}

}

[/b]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: