分享一下我老师大神的人工智能教程!零基础,通俗易懂!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的启动过程
这部分可以用下图进行简化:
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进行响应的过程了。