类装载器
JDK中提供了3种不同的类加载器:启动类装载器,扩展类装载器和系统类装载器。引导类装载器,用于引导启动JAVA虚拟机,当执行一个JAVA程序时,就会启动引导类装载器,它是使用本地代码来实现的,会装载%JAVA_HOME%\\jre\lib\rt.jar,它是所有类装载器类的父装载器。扩展类装载器负责载入标准扩展目录中的类,其搜索路径是%JAVA_HOME%\jre\lib\ext,只需要将打包好的jar文件放入这个目录就可以了,给开发提供了很大的便利性。系统类装载器是默认的装载器,其搜索路径是classpath。
JVM到底使用的是哪一个类装载器,取决于类装载器的代理模式。每当需要装载一个类的时候,会首先调用系统类装载器,但是系统类装载器并不会立即装载,而是将其交给父装载器:扩展类装载器,扩展类装载器其将交给引导类装载器,引导类装载器没有父装载器,它会尝试装载这个类,如果找不到这个类,会交给扩展类装载器,如果扩展类装载器还是没有找到,会交给系统类装载器,如果系统类装载器还是没有找到这个类,则会抛出java.lang.ClassNotFoundException异常。代理模式主要是为了解决类装载的安全问题。例如:对于自定类的java.lang.Object类,永远得不到装载,除非,rt.jar中确实没有这个类。
tomcat也提供了几种不同的类装载器用于加载不同位置的jar包和class文件,特别是Context容器需要有一个单独的类装载器,因为不同应用可能有相同的类,如果用同一个类装载器去装载,就不知道该加载哪个应用里面的类了。这些类装载器之间的关系如下图所示:
系统类装载器
tomcat的系统类装载器和JDK的系统类装载器有点不同的地方是搜索路径并不相同,在catalina.bat中做了如下修改:
- rem Add on extra jar file to CLASSPATH
- rem Note that there are no quotes as we do not want to introduce random
- rem quotes into the CLASSPATH
- if "%CLASSPATH%" == "" goto emptyClasspath
- set "CLASSPATH=%CLASSPATH%;"
- :emptyClasspath
- set "CLASSPATH=%CLASSPATH%%CATALINA_HOME%\bin\bootstrap.jar"
- rem Add tomcat-juli.jar to classpath
- rem tomcat-juli.jar can be over-ridden per instance
- if not exist "%CATALINA_BASE%\bin\tomcat-juli.jar" goto juliClasspathHome
- set "CLASSPATH=%CLASSPATH%;%CATALINA_BASE%\bin\tomcat-juli.jar"
- public void init() throws Exception{
- // Set Catalina path
- setCatalinaHome();
- setCatalinaBase();
- initClassLoaders();
- Thread.currentThread().setContextClassLoader(catalinaLoader);
- SecurityClassLoad.securityClassLoad(catalinaLoader);
- ...
- }
- private void initClassLoaders() {
- try {
- commonLoader = createClassLoader("common", null);
- if( commonLoader == null ) {
- // no config file, default to this loader - we might be in a 'single' env.
- commonLoader=this.getClass().getClassLoader();
- }
- catalinaLoader = createClassLoader("server", commonLoader);
- sharedLoader = createClassLoader("shared", commonLoader);
- } catch (Throwable t) {
- handleThrowable(t);
- log.error("Class loader creation threw exception", t);
- System.exit(1);
- }
- }
Common Class Loader
- private ClassLoader createClassLoader(String name, ClassLoader parent)throws Exception {
- String value = CatalinaProperties.getProperty(name + ".loader");
- if ((value == null) || (value.equals("")))
- return parent;
- value = replace(value);
- List<Repository> repositories = new ArrayList<Repository>();
- StringTokenizer tokenizer = new StringTokenizer(value, ",");
- while (tokenizer.hasMoreElements()) {
- String repository = tokenizer.nextToken().trim();
- if (repository.length() == 0) {
- continue;
- }
- // Check for a JAR URL repository
- try {
- @SuppressWarnings("unused")
- URL url = new URL(repository);
- repositories.add(new Repository(repository, RepositoryType.URL));
- continue;
- } catch (MalformedURLException e) {
- // Ignore
- }
- // Local repository
- if (repository.endsWith("*.jar")) {
- repository = repository.substring(0, repository.length() - "*.jar".length());
- repositories.add(new Repository(repository, RepositoryType.GLOB));
- } else if (repository.endsWith(".jar")) {
- repositories.add(new Repository(repository, RepositoryType.JAR));
- } else {
- repositories.add(new Repository(repository, RepositoryType.DIR));
- }
- }
- ClassLoader classLoader = ClassLoaderFactory.createClassLoader(repositories, parent);
- ...
- return classLoader;
- }
Catalina Class Loader
- common.loader=${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar
- server.loader=
- shared.loader=
Webapp Class Loader
tomcat的一个service除了容器和连接器外还有很多组件,比如sessionManager,logger,loader等,这个类装载器是以组件的形式附着在每个容器上的,Engine和Host的这两个容器的loader组件为null,context里面是有值的,看看context的startInternal方法:
- protected synchronized void startInternal() throws LifecycleException {
- ...
- if (getLoader() == null) {
- WebappLoader webappLoader = new WebappLoader(getParentClassLoader());
- webappLoader.setDelegate(getDelegate());
- setLoader(webappLoader);
- }
- ...
- // Binding thread
- ClassLoader oldCCL = bindThread();
- try {
- if (ok) {
- // Start our subordinate components, if any
- if ((loader != null) && (loader instanceof Lifecycle))
- ((Lifecycle) loader).start();
- // since the loader just started, the webapp classloader is now
- // created.
- // By calling unbindThread and bindThread in a row, we setup the
- // current Thread CCL to be the webapp classloader
- unbindThread(oldCCL);
- oldCCL = bindThread();
- }
- ...
- } finally {
- // Unbinding thread
- unbindThread(oldCCL);
- }
- protected void startInternal() throws LifecycleException {
- ...
- // Construct a class loader based on our current repositories list
- try {
- classLoader = createClassLoader();
- classLoader.setResources(container.getResources());
- classLoader.setDelegate(this.delegate);
- classLoader.setSearchExternalFirst(searchExternalFirst);
- if (container instanceof StandardContext) {
- classLoader.setAntiJARLocking(
- ((StandardContext) container).getAntiJARLocking());
- classLoader.setClearReferencesStatic(
- ((StandardContext) container).getClearReferencesStatic());
- classLoader.setClearReferencesStopThreads(
- ((StandardContext) container).getClearReferencesStopThreads());
- classLoader.setClearReferencesStopTimerThreads(
- ((StandardContext) container).getClearReferencesStopTimerThreads());
- classLoader.setClearReferencesHttpClientKeepAliveThread(
- ((StandardContext) container).getClearReferencesHttpClientKeepAliveThread());
- }
- for (int i = 0; i < repositories.length; i++) {
- classLoader.addRepository(repositories[i]);
- }
- // Configure our repositories
- setRepositories();
- setClassPath();
- setPermissions();
- ((Lifecycle) classLoader).start();
- ...
- } catch (Throwable t) {
- ...
- }
- ...
- }
- 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;
- }
- private void setRepositories() throws IOException {
- ...
- // Setting up the class repository (/WEB-INF/classes), if it exists
- String classesPath = "/WEB-INF/classes";
- ...
- // 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";
- ...
- // Looking up directory /WEB-INF/lib in the context
- NamingEnumeration<NameClassPair> enumeration = libDir.list("");
- while (enumeration.hasMoreElements()) {
- NameClassPair ncPair = enumeration.nextElement();
- String filename = libPath + "/" + ncPair.getName();
- if (!filename.endsWith(".jar"))
- continue;
- ...
- try {
- JarFile jarFile = new JarFile(destFile);
- classLoader.addJar(filename, jarFile, destFile);
- } catch (Exception ex) {
- ...
- }
- ...
- }
- }
- protected static final String[] triggers = {
- "javax.servlet.Servlet", "javax.el.Expression" // Servlet API
- };
WebappClassLoader装载类
- public synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
- ...
- // 先检查本地缓存
- clazz = findLoadedClass0(name);
- if (clazz != null) {
- if (log.isDebugEnabled())
- log.debug(" Returning class from cache");
- if (resolve)
- resolveClass(clazz);
- return (clazz);
- }
- // 如果本地缓存没有,则检查上一级缓存
- clazz = findLoadedClass(name);
- if (clazz != null) {
- if (log.isDebugEnabled())
- log.debug(" Returning class from cache");
- if (resolve)
- resolveClass(clazz);
- return (clazz);
- }
- // 如果两个缓存都没有,则使用系统的类装载器进行装载,防止Web应用程序中的类覆盖J2EE的类
- try {
- clazz = system.loadClass(name);
- if (clazz != null) {
- if (resolve)
- resolveClass(clazz);
- return (clazz);
- }
- } catch (ClassNotFoundException e) {
- // Ignore
- }
- // 如果启用了SecurityManager,则检查此类是否允许被载入,如果不允许,则抛出异常
- if (securityManager != null) {
- int i = name.lastIndexOf('.');
- if (i >= 0) {
- try {
- securityManager.checkPackageAccess(name.substring(0, i));
- } catch (SecurityException se) {
- String error = "Security Violation, attempt to use " + "Restricted Class: " + name;
- log.info(error, se);
- throw new ClassNotFoundException(error, se);
- }
- }
- }
- boolean delegateLoad = delegate || filter(name);
- // 若打开了delegateLoad标志位,调用父装载器来加载。如果父装载器为null,使用系统类装载器装载
- if (delegateLoad) {
- if (log.isDebugEnabled())
- log.debug(" Delegating to parent classloader1 " + parent);
- ClassLoader loader = parent;
- if (loader == null)
- loader = system;
- try {
- clazz = Class.forName(name, false, loader);
- if (clazz != null) {
- if (log.isDebugEnabled())
- log.debug(" Loading class from parent");
- if (resolve)
- resolveClass(clazz);
- return (clazz);
- }
- } catch (ClassNotFoundException e) {
- // Ignore
- }
- }
- // 从本地仓库中载入相关类
- if (log.isDebugEnabled())
- log.debug(" Searching local repositories");
- try {
- clazz = findClass(name);
- if (clazz != null) {
- if (log.isDebugEnabled())
- log.debug(" Loading class from local repository");
- if (resolve)
- resolveClass(clazz);
- return (clazz);
- }
- } catch (ClassNotFoundException e) {
- // Ignore
- }
- // 若当前仓库中没有需要的类,且delegateLoad标志位关闭,则使用父装载器。若父装载器为null,使用系统类装载器来装载
- if (!delegateLoad) {
- if (log.isDebugEnabled())
- log.debug(" Delegating to parent classloader at end: " + parent);
- ClassLoader loader = parent;
- if (loader == null)
- loader = system;
- try {
- clazz = Class.forName(name, false, loader);
- if (clazz != null) {
- if (log.isDebugEnabled())
- log.debug(" Loading class from parent");
- if (resolve)
- resolveClass(clazz);
- return (clazz);
- }
- } catch (ClassNotFoundException e) {
- // Ignore
- }
- }
- //仍未找到,抛出异常
- throw new ClassNotFoundException(name);
- }
相关推荐
《深入解析Tomcat 5.0.18 ClassLoader源码》 在Java Web开发中,Tomcat作为一款广泛使用的应用服务器,其内部机制对于开发者来说具有极高的学习价值。尤其是Tomcat的ClassLoader机制,它是Java类加载的核心部分,...
《Tomcat学习与分析总结资料》是一份涵盖了Tomcat服务器核心知识的综合资源,适合对Java Web应用服务器感兴趣的开发者深入学习。Tomcat是Apache软件基金会的项目,是世界上最流行的开源Servlet容器,它实现了Java ...
- **Classloading**:Tomcat使用定制的ClassLoader加载Web应用的类,遵循“父类加载优先”原则。 6. **连接器与协议处理** - **NIO和Apr**:Tomcat提供了多种连接器实现,如基于Java NIO的 Coyote Connector 和...
5. **ClassLoader**:Tomcat的类加载机制允许它支持多个Web应用,同时防止类冲突。理解`org.apache.catalina.loader`包下的`WebappClassLoader`及其工作原理,是深入理解Tomcat多应用隔离的关键。 6. **线程池和...
Java编程世界中,ClassLoader是一个至关重要的概念,尤其是在服务器端应用如Tomcat的环境中。本资料“Tomcat.ClassLoader.rar”聚焦于Java的类加载器(Class Loader)以及它在Tomcat容器中的工作原理,这对于理解和...
5. **ClassLoader机制**:Tomcat使用自定义的ClassLoader来加载Web应用程序的类,确保不同应用之间的类隔离,防止冲突。 Tomcat 6.0.18版的特性包括: 1. **性能优化**:相对于之前的版本,6.0.18进行了性能调优,...
- **Connector**:它是Tomcat的核心组件之一,负责接收客户端请求并将请求传递给合适的Container进行处理。Connector支持多种协议,如HTTP/1.1、AJP/1.3等。通过配置不同的Connector,可以实现Tomcat同时支持多种...
5. **ClassLoader**:Tomcat使用自定义的ClassLoader来加载Web应用程序的类,这使得不同的应用程序可以使用相同的库而不会相互冲突。 6. **部署与管理**:可以通过修改conf/server.xml和conf/context.xml文件,或者...
源码中的`classloader`目录揭示了这一机制。 9. **错误处理与日志系统**:Tomcat使用自定义的日志框架,源码中`logging`目录下的类定义了如何记录和处理错误信息。 10. **网络编程**:Tomcat底层使用NIO(非阻塞I/...
5. **ClassLoader机制**:Tomcat的类加载机制允许每个Web应用拥有自己的类加载器,避免类冲突。理解这部分源码对于理解和解决部署问题至关重要。 6. **Session管理**:Tomcat处理用户会话,包括创建、跟踪和管理...
1. **Servlet和JSP标准实现**:Tomcat是Java Web应用的标准之一,它遵循Java Servlet和JSP规范。在源码中,你可以看到如何实现这些规范,包括请求处理、响应生成、会话管理以及生命周期管理。 2. **Catalina组件**...
《深入剖析Tomcat》这本书是Java开发者们探索Tomcat服务器内部机制的重要参考资料,它带领读者逐步揭开Tomcat的...通过学习和研究Tomcat源码,我们可以提升技术水平,解决实际问题,甚至为Tomcat社区贡献自己的力量。
通过阅读"TOMCAT源码分析.doc"和解压后的"apache-tomcat-6.0.0-src.zip",你可以深入了解上述知识点,并学习如何根据源码进行调试、优化或扩展Tomcat。这份资料对于Java Web开发者来说是一份宝贵的参考资料,有助于...
3. **学习部署和加载机制**:Tomcat如何加载和管理Web应用程序,包括WAR文件的部署和Classloader的工作原理。 4. **研究线程模型**:Tomcat如何使用线程来处理并发请求,以及线程池的配置和管理。 5. **深入JSP和...
《深入剖析Tomcat8源码》 ...通过深入学习和分析Tomcat8源码,我们可以更好地理解Web应用的工作原理,提升开发和调优能力。对于Java EE开发者来说,掌握Tomcat源码无疑能够增强对整个Web栈的掌控力。
理解`Classloader`的工作原理对于深入学习Java、优化应用性能以及进行安全控制具有重要意义。 首先,让我们来看看`Classloader`的基本结构。Java中的类加载器通常遵循一种委托模型(Delegation Model)。这意味着当...