深入理解Tomcat系列之六 Servlet工作原理

分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow

也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

                       

前言

Servlet是Web开发中的核心技术,作为一名合格的开发人员,就必须清楚Servlet的工作原理。本章没有对Servlet技术本身进行详细的说明,只是针对开发过程中一次Servlet的请求的处理过程进行分析的。Servlet实际上就是一个java类,只不过可以和浏览器进行一些数据的交换。有Servlet类就有管理Servlet的容器,种类有很多,这里主要针对Tomcat对Servlet的工作原理进行说明。为了说清楚Servlet工作原理,需要知道Servlet的工作过程大致可以分为以下几个阶段:

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

Tomcat的启动过程

这部分可以用下图进行简化:

Tomcat容器启动序列图

Web应用初始化

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

代码清单5-1:

    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:\n" + 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());                }            }        }
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140

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

代码清单5-2:

    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);        }
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

这段代码说明了把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。下面是这个方法的源码:

代码清单5-3:

    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);        }    }
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

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

执行service方法

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

           

给我老师的人工智能教程打call!http://blog.csdn.net/jiangjunshow

这里写图片描述

猜你喜欢

转载自blog.csdn.net/qq_43667968/article/details/84070427