前一篇文章讲了org.apache.catalina.startup.hostconfig的lifecycleevent方法中所做的事情。最后看到在tomcat启动时或启动后(后台线程定时扫描)会调用hostconfig类的deployapps方法:
/** * deploy applications for any directories or war files that are found * in our "application root" directory. */ protected void deployapps() { file appbase = appbase(); file configbase = configbase(); string[] filteredapppaths = filterapppaths(appbase.list()); // deploy xml descriptors from configbase deploydescriptors(configbase, configbase.list()); // deploy wars deploywars(appbase, filteredapppaths); // deploy expanded folders deploydirectories(appbase, filteredapppaths); }
可以看到这里部署应用有三种方式:xml文件描述符、war包、文件目录。三种方式部署的总体流程很相似,都是一个web应用分配一个线程来处理,这里统一放到与host内部的线程池对象中(startstopexecutor),所以有时会看到在默认配置下tomcat启动后可能有一个叫“-startstop-”的线程还会运行一段时间才结束。但浏览这三种部署方式的实现代码,里面都是构建一个context对象,并将构建好的context对象与host组件关联起来(即调用host.addchild(context)这句,具体代码在hostconfig类的deploydescriptor(contextname cn, file contextxml)、deploydirectory(contextname cn, file dir)、deploywar(contextname cn, file war)三个方法中,这里不再贴出代码来详细分析)。
前一篇文章只分析到这步,可以看出与一个web应用相对应的一个context对象已经构建出来了,但如果容器只执行到这里根本无法响应一个浏览器的一次请求。就web服务器的实现来看一次请求过来除了需要根据内部context构建找到这次请求访问的web应用具体所对应的context对象,还需要包含web应用中具体的哪个servlet来处理这次请求,中间是否还需要执行相应的过滤器(filter)、监听器(listener)等,做过java的web开发的都知道,这些信息是配置在一个web应用的web-inf\web.xml文件的(servlet3中已经支持将这些配置信息放到java文件的注解中,但万变不离其宗,总归要在web应用的某个地方说明,并在容器启动时加载,这样才能真正提供web服务,响应请求)。
看到这里可以猜到tomcat容器加载web应用时必定会有对于每个应用的web.xml文件的解析过程,本文就来看看这个解析过程。
在本文开头提到的三种部署应用的实现代码中有一些共通的代码,这里摘出来说明一下:
class clazz = class.forname(host.getconfigclass()); lifecyclelistener listener = (lifecyclelistener) clazz.newinstance(); context.addlifecyclelistener(listener);
host.addchild(context);
第一段是在所有context对象构建时会添加一个监听器,这里监听器的类名是standardhost类的实例变量configclass,其默认值就是org.apache.catalina.startup.contextconfig。第二段是将当前构建的context对象添加到父容器host对象中。
先看下standardhost的addchild方法的实现:
public void addchild(container child) { child.addlifecyclelistener(new memoryleaktrackinglistener()); if (!(child instanceof context)) throw new illegalargumentexception (sm.getstring("standardhost.notcontext")); super.addchild(child); }
可以看到这段代码最后调用了父类的addchild方法:
public void addchild(container child) { if (globals.is_security_enabled) { privilegedactiondp = new privilegedaddchild(child); accesscontroller.doprivileged(dp); } else { addchildinternal(child); } }
这里看下addchildinternal方法的实现:
private void addchildinternal(container child) { if( log.isdebugenabled() ) log.debug("add child " child " " this); synchronized(children) { if (children.get(child.getname()) != null) throw new illegalargumentexception("addchild: child name '" child.getname() "' is not unique"); child.setparent(this); // may throw iae children.put(child.getname(), child); } // start child // don't do this inside sync block - start can be a slow process and // locking the children object can cause problems elsewhere if ((getstate().isavailable() || lifecyclestate.starting_prep.equals(getstate())) && startchildren) { try { child.start(); } catch (lifecycleexception e) { log.error("containerbase.addchild: start: ", e); throw new illegalstateexception ("containerbase.addchild: start: " e); } } firecontainerevent(add_child_event, child); }
可以看到会调用子容器的start方法,就是指调用standardcontext的start方法。
即给host对象添加子容器时将会调用子容器的start方法,,调用standardcontext的start方法最终会调用org.apache.catalina.core.standardcontext类的startinternal方法(该方法代码较长,建议自己阅读,不再贴出),这里将会发布一系列事件,按调用前后顺序这些事件包括:before_init_event、after_init_event、before_start_event、configure_start_event、start_event、after_start_event。
前面提到在构建context对象时都会注册一个监听器org.apache.catalina.startup.contextconfig,看下这个类的lifecycleevent方法中(为什么会执行这个方法可以看)监听了哪些事件:
/** * process events for an associated context. * * @param event the lifecycle event that has occurred */ @override public void lifecycleevent(lifecycleevent event) { // identify the context we are associated with try { context = (context) event.getlifecycle(); } catch (classcastexception e) { log.error(sm.getstring("contextconfig.cce", event.getlifecycle()), e); return; } // process the event that has occurred if (event.gettype().equals(lifecycle.configure_start_event)) { configurestart(); } else if (event.gettype().equals(lifecycle.before_start_event)) { beforestart(); } else if (event.gettype().equals(lifecycle.after_start_event)) { // restore docbase for management tools if (originaldocbase != null) { context.setdocbase(originaldocbase); } } else if (event.gettype().equals(lifecycle.configure_stop_event)) { configurestop(); } else if (event.gettype().equals(lifecycle.after_init_event)) { init(); } else if (event.gettype().equals(lifecycle.after_destroy_event)) { destroy(); } }
与context的start方法调用相关的事件监听前后顺序为:after_init_event(执行init方法)、before_start_event(执行beforestart方法)、configure_start_event(执行configurestart方法)。
在configurestart方法将直接调用webconfig方法,正是在这个方法中将会解析web.xml文件:
/** * scan the web.xml files that apply to the web application and merge them * using the rules defined in the spec. for the global web.xml files, * where there is duplicate configuration, the most specific level wins. ie * an application's web.xml takes precedence over the host level or global * web.xml file. */ protected void webconfig() { /* * anything and everything can override the global and host defaults. * this is implemented in two parts * - handle as a web fragment that gets added after everything else so * everything else takes priority * - mark servlets as overridable so sci configuration can replace * configuration from the defaults */ /* * the rules for annotation scanning are not as clear-cut as one might * think. tomcat implements the following process: * - as per srv.1.6.2, tomcat will scan for annotations regardless of * which servlet spec version is declared in web.xml. the eg has * confirmed this is the expected behaviour. * - as per http://java.net/jira/browse/servlet_spec-36, if the main * web.xml is marked as metadata-complete, jars are still processed * for scis. * - if metadata-complete=true and an absolute ordering is specified, * jars excluded from the ordering are also excluded from the sci * processing. * - if an sci has a @handlestype annotation then all classes (except * those in jars excluded from an absolute ordering) need to be * scanned to check if they match. */ setdefaults = new hashset (); 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 fragments = processjarsforwebfragments(); // step 2. order the fragments. set orderedfragments = null; orderedfragments = webxml.orderwebfragments(webxml, fragments, scontext); // step 3. look for servletcontainerinitializer implementations if (ok) { processservletcontainerinitializers(orderedfragments); } 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 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); processannotations); } 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 resourcejars = new linkedhashset (); 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 >> entry : initializerclassmap.entryset()) { if (entry.getvalue().isempty()) { context.addservletcontainerinitializer( entry.getkey(), null); } else { context.addservletcontainerinitializer( entry.getkey(), entry.getvalue()); } } } }
这个方法里面做的事情,在英文注释中说的很清楚了,概括起来包括合并tomcat全局web.xml、当前应用中的web.xml、web-fragment.xml和web应用的注解中的配置信息,并将解析出的各种配置信息(如servlet配置、filter配置等)关联到context对象中(在上面的代码第140行:webxml.configurecontext(context))。
看下configurecontext方法:
/** * configure a {@link context} using the stored web.xml representation. * * @param context the context to be configured */ public void configurecontext(context context) { // as far as possible, process in alphabetical order so it is easy to // check everything is present // some validation depends on correct public id context.setpublicid(publicid); // everything else in order context.seteffectivemajorversion(getmajorversion()); context.seteffectiveminorversion(getminorversion()); for (entryentry : contextparams.entryset()) { context.addparameter(entry.getkey(), entry.getvalue()); } context.setdisplayname(displayname); context.setdistributable(distributable); for (contextlocalejb ejblocalref : ejblocalrefs.values()) { context.getnamingresources().addlocalejb(ejblocalref); } for (contextejb ejbref : ejbrefs.values()) { context.getnamingresources().addejb(ejbref); } for (contextenvironment environment : enventries.values()) { context.getnamingresources().addenvironment(environment); } for (errorpage errorpage : errorpages.values()) { context.adderrorpage(errorpage); } for (filterdef filter : filters.values()) { if (filter.getasyncsupported() == null) { filter.setasyncsupported("false"); } context.addfilterdef(filter); } for (filtermap filtermap : filtermaps) { context.addfiltermap(filtermap); } for (jsppropertygroup jsppropertygroup : jsppropertygroups) { jsppropertygroupdescriptor descriptor = new applicationjsppropertygroupdescriptor(jsppropertygroup); context.getjspconfigdescriptor().getjsppropertygroups().add( descriptor); } for (string listener : listeners) { context.addapplicationlistener( new applicationlistener(listener, false)); } for (entry entry : localeencodingmappings.entryset()) { context.addlocaleencodingmappingparameter(entry.getkey(), entry.getvalue()); } // prevents iae if (loginconfig != null) { context.setloginconfig(loginconfig); } for (messagedestinationref mdr : messagedestinationrefs.values()) { context.getnamingresources().addmessagedestinationref(mdr); } // messagedestinations were ignored in tomcat 6, so ignore here context.setignoreannotations(metadatacomplete); for (entry entry : mimemappings.entryset()) { context.addmimemapping(entry.getkey(), entry.getvalue()); } // name is just used for ordering for (contextresourceenvref resource : resourceenvrefs.values()) { context.getnamingresources().addresourceenvref(resource); } for (contextresource resource : resourcerefs.values()) { context.getnamingresources().addresource(resource); } for (securityconstraint constraint : securityconstraints) { context.addconstraint(constraint); } for (string role : securityroles) { context.addsecurityrole(role); } for (contextservice service : servicerefs.values()) { context.getnamingresources().addservice(service); } 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 params = servlet.getparametermap(); for (entry entry : params.entryset()) { wrapper.addinitparameter(entry.getkey(), entry.getvalue()); } wrapper.setrunas(servlet.getrunas()); set 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); } for (entry entry : servletmappings.entryset()) { context.addservletmapping(entry.getkey(), entry.getvalue()); } if (sessionconfig != null) { if (sessionconfig.getsessiontimeout() != null) { context.setsessiontimeout( sessionconfig.getsessiontimeout().intvalue()); } sessioncookieconfig scc = context.getservletcontext().getsessioncookieconfig(); scc.setname(sessionconfig.getcookiename()); scc.setdomain(sessionconfig.getcookiedomain()); scc.setpath(sessionconfig.getcookiepath()); scc.setcomment(sessionconfig.getcookiecomment()); if (sessionconfig.getcookiehttponly() != null) { scc.sethttponly(sessionconfig.getcookiehttponly().booleanvalue()); } if (sessionconfig.getcookiesecure() != null) { scc.setsecure(sessionconfig.getcookiesecure().booleanvalue()); } if (sessionconfig.getcookiemaxage() != null) { scc.setmaxage(sessionconfig.getcookiemaxage().intvalue()); } if (sessionconfig.getsessiontrackingmodes().size() > 0) { context.getservletcontext().setsessiontrackingmodes( sessionconfig.getsessiontrackingmodes()); } } for (entry entry : taglibs.entryset()) { taglibdescriptor descriptor = new applicationtaglibdescriptor( entry.getvalue(), entry.getkey()); context.getjspconfigdescriptor().gettaglibs().add(descriptor); } // context doesn't use version directly for (string welcomefile : welcomefiles) { /* * the following will result in a welcome file of "" so don't add * that to the context * * */ if (welcomefile != null && welcomefile.length() > 0) { context.addwelcomefile(welcomefile); } } // do this last as it depends on servlets for (jsppropertygroup jsppropertygroup : jsppropertygroups) { string jspservletname = context.findservletmapping("*.jsp"); if (jspservletname == null) { jspservletname = "jsp"; } if (context.findchild(jspservletname) != null) { for (string urlpattern : jsppropertygroup.geturlpatterns()) { context.addservletmapping(urlpattern, jspservletname, true); } } else { if(log.isdebugenabled()) { for (string urlpattern : jsppropertygroup.geturlpatterns()) { log.debug("skiping " urlpattern " , no servlet " jspservletname); } } } } for (entry* entry : postconstructmethods.entryset()) { context.addpostconstructmethod(entry.getkey(), entry.getvalue()); } for (entry entry : predestroymethods.entryset()) { context.addpredestroymethod(entry.getkey(), entry.getvalue()); } }
可以看到里面对context调用了各种set、add方法,从而将web.xml中的各种配置信息与表示一个web应用的context对象关联起来。
相关推荐
本书共分4部分,从xml、servlet、jsp和应用的角度向读者展示了java web开发中各种技术的应用,循序渐进地引导读者快速掌握java web开发。. 本书内容全面,涵盖了从事java web开发所应掌握的所有知识。在知识的讲解...
本书共分4部分,从xml、servlet、jsp和应用的角度向读者展示了java web开发中各种技术的应用,循序渐进地引导读者快速掌握java web开发。. 本书内容全面,涵盖了从事java web开发所应掌握的所有知识。在知识的讲解...
本书共分4部分,从xml、servlet、jsp和应用的角度向读者展示了java web开发中各种技术的应用,循序渐进地引导读者快速掌握java web开发。. 本书内容全面,涵盖了从事java web开发所应掌握的所有知识。在知识的讲解...
本书共分4部分,从xml、servlet、jsp和应用的角度向读者展示了java web开发中各种技术的应用,循序渐进地引导读者快速掌握java web开发。. 本书内容全面,涵盖了从事java web开发所应掌握的所有知识。在知识的讲解...
1. 当服务器接受到客户端浏览器的请求后,会解析请求url路径,获取访问的servlet的资源路径。在上图 的url中,获取的资源...4. tomcat会将字节码文件加载进内存,并且创建其对象 5. 调用其方法(主要调用service方法)
项目使用springboot启动一个web项目,在启动阶段看到console中出现了异常“1.10.3-1.4.3\hdf5.jar 系统找不到指定的文件”,虽然这些异常不影响项目的正常运行,但作为一个严谨的技术人员,看到这些异常就像见到...
1.tomcat axis的安装配置 首先机子上应该安装jdk1.5版本以上(带有xml解析包)。我这里是1.5.0.6 从apache的官方网站(www.apache.org)下载tomcat安装软件。下载版本要在4.0以上。这里用的是5.0版本。安装完后,...
一. java 基础部分............................................................................................................43、java 中的异常处理机制的简单原理和应用。 .....................................
1. 目录 1. 2. 目录 .........................................................................................................................................................1 jvm ........................
84.2. 我们在web应用开发过程中经常遇到输出某种编码的字符,如iso8859-1等,如何输出一个某种编码的字符串? 106 84.3. 设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1。写出程序。以下程序使用...
14.1 认识xml解析技术 14.1.1 什么是xml 14.1.2 xml的处理技术 14.2 xml处理利器:xstream 14.2.1 xstream概述 14.2.2 快速入门 14.2.3 使用xstream别名 14.2.4 xstream转换器 14.2.5 xstream注解 14.2.6 流化对象 ...
用gridview来呈现,从自建的tomcat上得到的图片.android端得到服务器下发的xml文件数据,解析后得到图片路径,然后根据图片路径下载保存在tomcat服务器上的图片.点击图片后,可以保存到sd卡的指定位置.
blister是一个用于操作苹果二进制plist文件格式的java开源类库(可用于发送数据给ios应用程序)。 重复文件检查工具 finddup.tar finddup 是一个简单易用的工具,用来检查计算机上重复的文件。 openid的java客户端...
14.1 认识xml解析技术 14.1.1 什么是xml 14.1.2 xml的处理技术 14.2 xml处理利器:xstream 14.2.1 xstream概述 14.2.2 快速入门 14.2.3 使用xstream别名 14.2.4 xstream转换器 14.2.5 xstream注解 14.2.6 流化对象 ...
blister是一个用于操作苹果二进制plist文件格式的java开源类库(可用于发送数据给ios应用程序)。 重复文件检查工具 finddup.tar finddup 是一个简单易用的工具,用来检查计算机上重复的文件。 openid的java客户端...
blister是一个用于操作苹果二进制plist文件格式的java开源类库(可用于发送数据给ios应用程序)。 重复文件检查工具 finddup.tar finddup 是一个简单易用的工具,用来检查计算机上重复的文件。 openid的java客户端...
blister是一个用于操作苹果二进制plist文件格式的java开源类库(可用于发送数据给ios应用程序)。 重复文件检查工具 finddup.tar finddup 是一个简单易用的工具,用来检查计算机上重复的文件。 openid的java客户端...
blister是一个用于操作苹果二进制plist文件格式的java开源类库(可用于发送数据给ios应用程序)。 重复文件检查工具 finddup.tar finddup 是一个简单易用的工具,用来检查计算机上重复的文件。 openid的java客户端...