• Tomcat Servlet工作原理



    前言

    Servlet实际上就是一个java类,只不过可以和浏览器进行一些数据的交换。有Servlet类就有管理Servlet的容器。

    Servlet的工作过程大致可以分为以下几个阶段:

    1. 启动Tomcat容器
    2. Web应用初始化
    3. 创建Servlet实例
    4. 初始化Servlet
    5. 执行Servlet的service方法

    Tomcat的启动过程

    Tomcat启动

    Web应用初始化

    初始化工作是由ContextConfig类的configureStart方法完成的,该方法的主要任务是完成web.xml配置文件的解析。下面解析的关键代码:

        Set<WebXml> defaults = new HashSet<WebXml>();
            defaults.add(getDefaultWebXmlFragment());
            WebXml webXml = createWebXml();
            // Parse context level web.xml
            InputSource contextWebXml = getContextWebXmlSource();
            parseWebXml(contextWebXml, webXml, false);
            ServletContext sContext = context.getServletContext();
            // Ordering is important here
            // Step 1. Identify all the JARs packaged with the application
            // If the JARs have a web-fragment.xml it will be parsed at this
            // point.
            Map<String,WebXml> fragments = processJarsForWebFragments(webXml);
            // Step 2. Order the fragments.
            Set<WebXml> orderedFragments = null;
            orderedFragments =
                    WebXml.orderWebFragments(webXml, fragments, sContext);
            // Step 3. Look for ServletContainerInitializer implementations
            if (ok) {
                processServletContainerInitializers(context.getServletContext());
            }
            if  (!webXml.isMetadataComplete() || typeInitializerMap.size() > 0) {
                // Step 4. Process /WEB-INF/classes for annotations
                if (ok) {
                    // Hack required by Eclipse's "serve modules without
                    // publishing" feature since this backs WEB-INF/classes by
                    // multiple locations rather than one.
                    NamingEnumeration<Binding> listBindings = null;
                    try {
                        try {
                            listBindings = context.getResources().listBindings(
                                    "/WEB-INF/classes");
                        } catch (NameNotFoundException ignore) {
                            // Safe to ignore
                        }
                        while (listBindings != null &&
                                listBindings.hasMoreElements()) {
                            Binding binding = listBindings.nextElement();
                            if (binding.getObject() instanceof FileDirContext) {
                                File webInfClassDir = new File(
                                        ((FileDirContext) binding.getObject()).getDocBase());
                                processAnnotationsFile(webInfClassDir, webXml,
                                        webXml.isMetadataComplete());
                            } else {
                                String resource =
                                        "/WEB-INF/classes/" + binding.getName();
                                try {
                                    URL url = sContext.getResource(resource);
                                    processAnnotationsUrl(url, webXml,
                                            webXml.isMetadataComplete());
                                } catch (MalformedURLException e) {
                                    log.error(sm.getString(
                                            "contextConfig.webinfClassesUrl",
                                            resource), e);
                                }
                            }
                        }
                    } catch (NamingException e) {
                        log.error(sm.getString(
                                "contextConfig.webinfClassesUrl",
                                "/WEB-INF/classes"), e);
                    }
                }
                // Step 5. Process JARs for annotations - only need to process
                // those fragments we are going to use
                if (ok) {
                    processAnnotations(
                            orderedFragments, webXml.isMetadataComplete());
                }
                // Cache, if used, is no longer required so clear it
                javaClassCache.clear();
            }
            if (!webXml.isMetadataComplete()) {
                // Step 6. Merge web-fragment.xml files into the main web.xml
                // file.
                if (ok) {
                    ok = webXml.merge(orderedFragments);
                }
                // Step 7. Apply global defaults
                // Have to merge defaults before JSP conversion since defaults
                // provide JSP servlet definition.
                webXml.merge(defaults);
                // Step 8. Convert explicitly mentioned jsps to servlets
                if (ok) {
                    convertJsps(webXml);
                }
                // Step 9. Apply merged web.xml to Context
                if (ok) {
                    webXml.configureContext(context);
                }
            } else {
                webXml.merge(defaults);
                convertJsps(webXml);
                webXml.configureContext(context);
            }
            // Step 9a. Make the merged web.xml available to other
            // components, specifically Jasper, to save those components
            // from having to re-generate it.
            // TODO Use a ServletContainerInitializer for Jasper
            String mergedWebXml = webXml.toXml();
            sContext.setAttribute(
                   org.apache.tomcat.util.scan.Constants.MERGED_WEB_XML,
                   mergedWebXml);
            if (context.getLogEffectiveWebXml()) {
                log.info("web.xml:
    " + mergedWebXml);
            }
            // Always need to look for static resources
            // Step 10. Look for static resources packaged in JARs
            if (ok) {
                // Spec does not define an order.
                // Use ordered JARs followed by remaining JARs
                Set<WebXml> resourceJars = new LinkedHashSet<WebXml>();
                if (orderedFragments != null) {
                    for (WebXml fragment : orderedFragments) {
                        resourceJars.add(fragment);
                    }
                }
                for (WebXml fragment : fragments.values()) {
                    if (!resourceJars.contains(fragment)) {
                        resourceJars.add(fragment);
                    }
                }
                processResourceJARs(resourceJars);
                // See also StandardContext.resourcesStart() for
                // WEB-INF/classes/META-INF/resources configuration
            }
            // Step 11. Apply the ServletContainerInitializer config to the
            // context
            if (ok) {
                for (Map.Entry<ServletContainerInitializer,
                        Set<Class<?>>> entry :
                            initializerClassMap.entrySet()) {
                    if (entry.getValue().isEmpty()) {
                        context.addServletContainerInitializer(
                                entry.getKey(), null);
                    } else {
                        context.addServletContainerInitializer(
                                entry.getKey(), entry.getValue());
                    }
                }
            }

    在代码已经清晰地说明了Web应用的初始化过程,由于在Tomcat7中增加了对注解(annotation)的支持,所以会对Servlet中的注解进行解析。首先查找jar包中的web-fragment.xml,并对其进行解析,接下来将对/WEB-INF/classes目录下的class进行注解的解析。之后,把web-fragment.xml文件合并到web.xml中,被解析后的web.xml文件的配置项将保存到WebXml对象中,然后把WebXml对象中的属性设置到Context容器中,这个过程是由configureContext方法来完成的。下面是其部分源码——完成Servlet的解析:

        for (ServletDef servlet : servlets.values()) {
                Wrapper wrapper = context.createWrapper();
                // Description is ignored
                // Display name is ignored
                // Icons are ignored
                // jsp-file gets passed to the JSP Servlet as an init-param
                if (servlet.getLoadOnStartup() != null) {
                    wrapper.setLoadOnStartup(servlet.getLoadOnStartup().intValue());
                }
                if (servlet.getEnabled() != null) {
                    wrapper.setEnabled(servlet.getEnabled().booleanValue());
                }
                wrapper.setName(servlet.getServletName());
                Map<String,String> params = servlet.getParameterMap();
                for (Entry<String, String> entry : params.entrySet()) {
                    wrapper.addInitParameter(entry.getKey(), entry.getValue());
                }
                wrapper.setRunAs(servlet.getRunAs());
                Set<SecurityRoleRef> roleRefs = servlet.getSecurityRoleRefs();
                for (SecurityRoleRef roleRef : roleRefs) {
                    wrapper.addSecurityReference(
                            roleRef.getName(), roleRef.getLink());
                }
                wrapper.setServletClass(servlet.getServletClass());
                MultipartDef multipartdef = servlet.getMultipartDef();
                if (multipartdef != null) {
                    if (multipartdef.getMaxFileSize() != null &&
                            multipartdef.getMaxRequestSize()!= null &&
                            multipartdef.getFileSizeThreshold() != null) {
                        wrapper.setMultipartConfigElement(new MultipartConfigElement(
                                multipartdef.getLocation(),
                                Long.parseLong(multipartdef.getMaxFileSize()),
                                Long.parseLong(multipartdef.getMaxRequestSize()),
                                Integer.parseInt(
                                        multipartdef.getFileSizeThreshold())));
                    } else {
                        wrapper.setMultipartConfigElement(new MultipartConfigElement(
                                multipartdef.getLocation()));
                    }
                }
                if (servlet.getAsyncSupported() != null) {
                    wrapper.setAsyncSupported(
                            servlet.getAsyncSupported().booleanValue());
                }
                wrapper.setOverridable(servlet.isOverridable());
                context.addChild(wrapper);
            }

    这段代码说明了把Servlet包装成StandardWrapper的过程,并把这个Wrapper实例设置到Context容器中。之所以要把Servlet封装成一个Wrapper,主要为了解耦,因为Wrapper是一个容器而Servlet是一个具体的类。

    到目前为止,已经完成了web应用的初始化,其中将web.xml进行了解析并完成了注解的解析,还把配置的Servlet类封装成了一个Wrapper容器,接下来就是根据这个Wrapper创建Servlet实例了。

    创建Servlet实例

    创建Servlet实例要回到我们分析Wrapper容器中提到的loadServlet方法了,这个方法是由Wrapper容器的标准实现类StandardWrapper完成的,通过loadServlet方法获取servletClass,然后把这个servletClass交给实例管理器(InstanceManager)完成servletClass.class的对象创建。

    初始化Servlet

    初始化的工作是由StandardWrapper的initServlet方法完成的,这个方法主要就是调用Servlet的init方法,然后把包装了StandardWrapper的StandardWrapperFacade交给Servlet。下面是这个方法的源码:

        private synchronized void initServlet(Servlet servlet)
                throws ServletException {
    
            if (instanceInitialized && !singleThreadModel) return;
            // Call the initialization method of this servlet
            try {
                instanceSupport.fireInstanceEvent(InstanceEvent.BEFORE_INIT_EVENT,
                                                  servlet);
                if( Globals.IS_SECURITY_ENABLED) {
                    boolean success = false;
                    try {
                        Object[] args = new Object[] { facade };
                        SecurityUtil.doAsPrivilege("init",
                                                   servlet,
                                                   classType,
                                                   args);
                        success = true;
                    } finally {
                        if (!success) {
                            // destroy() will not be called, thus clear the reference now
                            SecurityUtil.remove(servlet);
                        }
                    }
                } else {
                    servlet.init(facade);
                }
                instanceInitialized = true;
                instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,
                                                  servlet);
            } catch (UnavailableException f) {
                instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,
                                                  servlet, f);
                unavailable(f);
                throw f;
            } catch (ServletException f) {
                instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,
                                                  servlet, f);
                // If the servlet wanted to be unavailable it would have
                // said so, so do not call unavailable(null).
                throw f;
            } catch (Throwable f) {
                ExceptionUtils.handleThrowable(f);
                getServletContext().log("StandardWrapper.Throwable", f );
                instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,
                                                  servlet, f);
                // If the servlet wanted to be unavailable it would have
                // said so, so do not call unavailable(null).
                throw new ServletException
                    (sm.getString("standardWrapper.initException", getName()), f);
            }
        }

    这样就完成了Servlet的初始化,下面就是执行Servlet的service方法了。

    执行service方法

    在分析Wrapper方法中提到,StandardWrapper会调用allocate方法从实例池栈中弹出一个Servlet处理请求,这个Servlet实例在完成初始化后,并经过一系列过滤器的过滤后就到达Servlet实例的service方法,这个过程就是过滤器执行的过程。这样Servlet实例就顺利调用到了service方法,之后发生的过程就是我们熟悉的request获取参数并用response进行响应的过程了。


    原文博主地址:rhwayfunn

  • 相关阅读:
    Python 实现AEC CBC 加密解密方式
    redis 发布订阅方法与缺陷
    python paramiko 传输下载文件
    Redis 配置文件
    Redis 命令
    window11 | 虚拟机vmWare安装windows11
    十万个为什么 | 文化001-为什么猜灯谜又叫做打灯谜
    ffmpeg | 常用命令使用
    ffmpeg | 常用命令使用
    Adobe系列 | Animate(01)-软件安装
  • 原文地址:https://www.cnblogs.com/aixing/p/13327567.html
Copyright © 2020-2023  润新知