tomcat类加载机制源码解析

本文详细解析了Tomcat中的类加载机制,包括其如何打破双亲委派模型、自定义类加载器的作用及其实现方式,并介绍了Tomcat如何加载Spring框架。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

tomcat打破双亲委派机制

1.先在Web用本地目录下查找要加载的类

2.如果没找到,交给父加载器去查找,它的父加载器就是系统类加载器AppClassLoader

3.如果附加在其也没找到,则抛出ClassNotFound异常

 

Tomcat中自定义的类加载器

Tomcat 拥有不同的⾃定义类加载器,以实现对各种资源库的控制。一般来说,Tomcat 主要用类加载器解决以下 4 个问题。

  • 同一个Tomcat中,各个Web应⽤之间各⾃使⽤的Java类库要互相隔离。
  • 同一个Tomcat中,各个Web应⽤之间可以提供共享的Java类库。
  • 为了使Tomcat不受Web应用的影响,应该使服务器的类库与应⽤程序的类库互相独立。
  • Tomcat支持热部署。

在 Tomcat中,最重要的一个类加载器是 Common 类加载器,它的父类加载器是应用程序类加载器,负责加载 $CATALINA_ BASE/lib、$CATALINA_HOME/lib 两个目录下所有的.class 文件与.jar 文件。
Tomcat中一般会有多个WebApp类加载器-WebAppClassLoader ,每个类加载器负责加载一个 Web 程序。它的父类加载器是Common类加载器。
由于每个 Web 应该都有多个的 WebApp 类加载器,很好地使多个 Web 应用程序之间互相隔离且能通过创建新的 WebApp类加载器达到热部署。这种类加载器结构能有效使 Tomcat 不受 Web 应用程序影响,而 Common 类加载器的存在使多个 Web 应⽤程序能够互相共享类库。
 

安全机制

Tomcat中设置了了一些安全策略,默认的策略文件为conf/catalina.policy
Tomcat中设置了安全策略,规定了Tomcat在运⾏过程中拥有的权限,Tomcat管理者可以修改该权限,但是Tomcat中有⼀些类是必须能够被访问到的,所有Tomcat中在启动过程中会提前去加载这些类,如果发现没有对应的权限,那么将会启动失败
 

tomcat类加载源码:

org.apache.catalina.loader.WebappClassLoaderBase#loadClass(java.lang.String, boolean)

@Override
    public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {

        synchronized (getClassLoadingLock(name)) {
            if (log.isDebugEnabled())
                log.debug("loadClass(" + name + ", " + resolve + ")");
            Class<?> clazz = null;

            // Log access to stopped class loader
            checkStateForClassLoading(name);

            //todo 1.现在本地cache中查找该类有没有加载过,
            // (0) Check our previously loaded local class cache
            clazz = findLoadedClass0(name);
            if (clazz != null) {
                if (log.isDebugEnabled())
                    log.debug("  Returning class from cache");
                if (resolve)
                    resolveClass(clazz);
                return clazz;
            }

            //todo 2 从系统类加载器的cache中查找是否加载过
            // (0.1) Check our previously loaded class cache
            clazz = findLoadedClass(name);
            if (clazz != null) {
                if (log.isDebugEnabled())
                    log.debug("  Returning class from cache");
                if (resolve)
                    resolveClass(clazz);
                return clazz;
            }

            // (0.2) Try loading the class with the system class loader, to prevent
            //       the webapp from overriding Java SE classes. This implements
            //       SRV.10.7.2
            String resourceName = binaryNameToPath(name, false);

            ClassLoader javaseLoader = getJavaseClassLoader();
            boolean tryLoadingFromJavaseLoader;
            try {
                // Use getResource as it won't trigger an expensive
                // ClassNotFoundException if the resource is not available from
                // the Java SE class loader. However (see
                // https://bz.apache.org/bugzilla/show_bug.cgi?id=58125 for
                // details) when running under a security manager in rare cases
                // this call may trigger a ClassCircularityError.
                // See https://bz.apache.org/bugzilla/show_bug.cgi?id=61424 for
                // details of how this may trigger a StackOverflowError
                // Given these reported errors, catch Throwable to ensure any
                // other edge cases are also caught
                URL url;
                if (securityManager != null) {
                    PrivilegedAction<URL> dp = new PrivilegedJavaseGetResource(resourceName);
                    url = AccessController.doPrivileged(dp);
                } else {
                    url = javaseLoader.getResource(resourceName);
                }
                tryLoadingFromJavaseLoader = (url != null);
            } catch (Throwable t) {
                // Swallow all exceptions apart from those that must be re-thrown
                ExceptionUtils.handleThrowable(t);
                // The getResource() trick won't work for this class. We have to
                // try loading it directly and accept that we might get a
                // ClassNotFoundException.
                tryLoadingFromJavaseLoader = true;
            }

            //todo 3.尝试用extClassLoader加载
            if (tryLoadingFromJavaseLoader) {
                try {
                    clazz = javaseLoader.loadClass(name);
                    if (clazz != null) {
                        if (resolve)
                            resolveClass(clazz);
                        return clazz;
                    }
                } catch (ClassNotFoundException e) {
                    // Ignore
                }
            }

            // (0.5) Permission to access this class when using a 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, true);

            //todo 5.尝试使用系统类加载器appCLassLoader来加载
            // (1) Delegate to our parent if requested
            if (delegateLoad) {
                if (log.isDebugEnabled())
                    log.debug("  Delegating to parent classloader1 " + parent);
                try {
                    clazz = Class.forName(name, false, parent);
                    if (clazz != null) {
                        if (log.isDebugEnabled())
                            log.debug("  Loading class from parent");
                        if (resolve)
                            resolveClass(clazz);
                        return clazz;
                    }
                } catch (ClassNotFoundException e) {
                    // Ignore
                }
            }

            //todo 4.尝试在本地目录搜索class并加载
            // (2) Search local repositories
            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
            }

            //todo 5.尝试用系统类加载器(也就是appClassLoader来加载)
            // (3) Delegate to parent unconditionally
            if (!delegateLoad) {
                if (log.isDebugEnabled())
                    log.debug("  Delegating to parent classloader at end: " + parent);
                try {
                    clazz = Class.forName(name, false, parent);
                    if (clazz != null) {
                        if (log.isDebugEnabled())
                            log.debug("  Loading class from parent");
                        if (resolve)
                            resolveClass(clazz);
                        return clazz;
                    }
                } catch (ClassNotFoundException e) {
                    // Ignore
                }
            }
        }
        // todo 6.还是找不到,则抛出异常
        throw new ClassNotFoundException(name);
    }

 

tomcat如何加载spring

通过org.springframework.web.context.ContextLoaderListener加载spring的bean,通过org.springframework.web.servlet.DispatcherServlet加载springmvc的方法

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <!--        spring-bean的扫描路径-->
        <param-value>classpath:spring/applicationContext.xml</param-value>
    </context-param>

    <listener>
        <!--当调用standardContext的startInnner时候,会初始化webApp加载器,扫描web.xml文件配置的listener,即ContextLoaderListener
        完成找到classpath:spring/applicationContext.xml中指定的扫描路径,初始化类
        -->
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!--DispatcherServlet-->
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <!--配置springmvc的扫描路径-->
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring/spring-mvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
        <async-supported>true</async-supported>
    </servlet>

    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

 

springboot的机制也是一样的,因为也是tomcat拉起了webapp和spring

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值