2013-10-06java
是org.apache.catalina.Context的標準實現,繼承自ContainerBase基容器,具有容器的功能,在tomcat結構層次圖中位於Host內部,包含ServletWrapper容器,它的角色是管理在其內部的ServletWrapper,從Host那裏接收請求信息,加載實例化Servlet,而後選擇一個合適的ServletWrapper處理,同一個Context內部中Servlet數據共享,不一樣Context之間數據隔離。在Context層面上,Filter的做用就出來了,Filter是用來過濾Servlet的組件,Context負責在每一個Servlet上面調用Filter過濾,Context與開發Servlet息息相關,跟前面的幾個組件相比,Context可以提供更多的信息給Servlet。一個webapp有一個Context,Context負責管理在其內部的組件:Servlet,Cookie,Session等等。web
Context
tomcat定義的Servlet上下文接口,也即一個webapp的應用環境。tomcat定義了一個Context所必須具有的功能,定義對Cookie,Session,ServletWrapper等組件的管理,爲一個請求選項合適的Servlet方法,他要解決的問題是爲請求選擇Servlet,Servlet之間的數據共享問題。apache
ContainerBase
容器基類,StandardContext繼承該類,具有了基容器的功能和方法。緩存
StandardContext()
默認構造器,設定basicValve, 初始化NotificationBroadcasterSupport,用戶支持j2ee企業功能。tomcat
StandardContextValve
Context的標準BasicValve,做用鏈接Context和ServletWrapper的工具。StandardContextValve主要完成的功能是:安全
URLEncoder
跟URL相關,具備對URL編碼的功能,替換特殊字符功能。cookie
charsetMapper : CharsetMapper
Context多支持的字符集,跟本地化有關,負責處理本地字符問題。session
context : ApplicationContext
Servlet的上下文,一個wepapp中提供一個全局的servlet上下,提供了一些列的訪問資源的接口,servlet經過ApplicationContext獲取servlet的運行環境信息,加載資源,獲取資源。與StandardContext的做用是相輔相成的關係,兩種的角色不一樣,ApplicationContext負責的是servlet運行環境上下信息,不關心session管理,cookie管理,servlet的加載,servlet的選擇問題,請求信息,一句話就是:他是servlet管家。StandardContext須要負責管理session,Cookie,Servlet的加載和卸載,負責請求信息的處理,掌握控制權,他是servlet的爹媽。app
exceptionPages : HashMap
錯誤頁面信息匹配,對於404,403等HTTP錯誤碼,tomcat經過映射頁面來定製提示信息。webapp
filterConfigs : HashMap
k/v的方式保存Filter與Fifter配置信息,在過濾請求信息是使用到。
filterDefs : HashMap
k/v的方式保存Filter與Filter的定義信息。
filterMaps : FilterMap[]
webapp中全部的Filter集合。別名映射關係。
mapper : Mapper
Servlet與訪問url匹配集合。
namingContextListener : NamingContextListener
是JNDI中的內容,不瞭解,比較複雜,只知道是負責監聽javax.naming.Context資源事件。NamingContextListener實現了LifecycleListener、ContainerListener、PropertyChangeListener3個接口,具有監聽Lifecycle組件,Container組件、PropertyChange的事件能力。
namingResources : NamingResources
是JNDI中的內容,不瞭解,比較複雜,知道它管理命名資源,將要加載的資源封裝成對象,能夠直接從NamingResources獲取對象了。
servletMappings : HashMap
訪問url匹配與Servlet類映射。
wrapperClassName : String
用來處理Servlet包裝的Wrapper類類名,負責加載管理Servlet。
wrapperClass : Class
用來處理Servlet包裝的Wrapper類,負責加載管理Servlet。
addServletMapping(String, String, boolean)
添加servlet和url模式映射關係。從web.xml或者註解中獲取Servlet與url的關係,而後保存到Context中。
createWrapper()
實例化Wrapper類,建立一個容納Servlet的容器,並在Wrapper上註冊監聽器。主要完成如下步驟:
/** * Factory method to create and return a new Wrapper instance, of the Java * implementation class appropriate for this Context implementation. The * constructor of the instantiated Wrapper will have been called, but no * properties will have been set. */ public Wrapper createWrapper() { Wrapper wrapper = null; if (wrapperClass != null) { try { wrapper = (Wrapper) wrapperClass.newInstance(); } catch (Throwable t) { log.error("createWrapper", t); return (null); } } else { wrapper = new StandardWrapper(); } synchronized (instanceListenersLock) { for (int i = 0; i < instanceListeners.length; i++) { try { Class clazz = Class.forName(instanceListeners[i]); InstanceListener listener = (InstanceListener) clazz .newInstance(); wrapper.addInstanceListener(listener); } catch (Throwable t) { log.error("createWrapper", t); return (null); } } } synchronized (wrapperLifecyclesLock) { for (int i = 0; i < wrapperLifecycles.length; i++) { try { Class clazz = Class.forName(wrapperLifecycles[i]); LifecycleListener listener = (LifecycleListener) clazz .newInstance(); if (wrapper instanceof Lifecycle) ((Lifecycle) wrapper).addLifecycleListener(listener); } catch (Throwable t) { log.error("createWrapper", t); return (null); } } } synchronized (wrapperListenersLock) { for (int i = 0; i < wrapperListeners.length; i++) { try { Class clazz = Class.forName(wrapperListeners[i]); ContainerListener listener = (ContainerListener) clazz .newInstance(); wrapper.addContainerListener(listener); } catch (Throwable t) { log.error("createWrapper", t); return (null); } } } return (wrapper); }
reload()
從新加載數據,用於當類或者配置文件發送變化時從新加載,從代碼中能夠看出來,tomcat是經過重啓來完成的,主要有幾個步驟
filterStart()
配置和初始化過濾Servlet的Filter集合。
filterStop()
釋放和清除Filter集合配置。
listenerStart()
配置並實例化註冊在Context上的監聽器集合,用於監聽在Context上面觸發的事件,監聽器用於監聽Conext事件和Servlet事件。分下面幾個步驟:
/** * Configure the set of instantiated application event listeners for this * Context. Return <code>true</code> if all listeners wre initialized * successfully, or <code>false</code> otherwise. */ public boolean listenerStart() { if (log.isDebugEnabled()) log.debug("Configuring application event listeners"); // Instantiate the required listeners //取當前Context的類加載器,用於實例化監聽器須要。不一樣Context的類加載可能不一樣,出於安全考慮 ClassLoader loader = getLoader().getClassLoader(); String listeners[] = findApplicationListeners(); Object results[] = new Object[listeners.length]; boolean ok = true; //遍歷全部監聽器,並實例化到results列表中 for (int i = 0; i < results.length; i++) { if (getLogger().isDebugEnabled()) getLogger().debug( " Configuring event listener class '" + listeners[i] + "'"); try { Class clazz = loader.loadClass(listeners[i]); results[i] = clazz.newInstance(); // Annotation processing if (!getIgnoreAnnotations()) { getAnnotationProcessor().processAnnotations(results[i]); getAnnotationProcessor().postConstruct(results[i]); } } catch (Throwable t) { getLogger().error( sm.getString("standardContext.applicationListener", listeners[i]), t); ok = false; } } if (!ok) { getLogger().error( sm.getString("standardContext.applicationSkipped")); return (false); } // Sort listeners in two arrays ArrayList eventListeners = new ArrayList(); ArrayList lifecycleListeners = new ArrayList(); //遍歷監聽器,區分ContextEvent監聽器和LifecycleEvent監聽器 for (int i = 0; i < results.length; i++) { if ((results[i] instanceof ServletContextAttributeListener) || (results[i] instanceof ServletRequestAttributeListener) || (results[i] instanceof ServletRequestListener) || (results[i] instanceof HttpSessionAttributeListener)) { eventListeners.add(results[i]); } if ((results[i] instanceof ServletContextListener) || (results[i] instanceof HttpSessionListener)) { lifecycleListeners.add(results[i]); } } //註冊監聽器 setApplicationEventListeners(eventListeners.toArray()); //註冊監聽器 setApplicationLifecycleListeners(lifecycleListeners.toArray()); // Send application start events if (getLogger().isDebugEnabled()) getLogger().debug("Sending application start events"); Object instances[] = getApplicationLifecycleListeners(); if (instances == null) return (ok); ServletContextEvent event = new ServletContextEvent(getServletContext()); //觸發Context實例化事件,通知監聽 for (int i = 0; i < instances.length; i++) { if (instances[i] == null) continue; if (!(instances[i] instanceof ServletContextListener)) continue; ServletContextListener listener = (ServletContextListener) instances[i]; try { fireContainerEvent("beforeContextInitialized", listener); listener.contextInitialized(event); fireContainerEvent("afterContextInitialized", listener); } catch (Throwable t) { fireContainerEvent("afterContextInitialized", listener); getLogger().error( sm.getString("standardContext.listenerStart", instances[i].getClass().getName()), t); ok = false; } } return (ok); }
listenerStop()
在Conext上中止監聽器的監聽,步驟與啓動時相反,首先觸發中止監聽事件,而後再清除資源。
resourcesStart()
分配Context的目錄資源,這裏涉及到目錄服務。
resourcesStop()
中止Context的目錄資源,這裏涉及到目錄服務。
loadOnStartup(Container[])
這個方法是用來處理在web.xml中配置的loadonstartup Servlet,須要在Context啓動時加載實例化,而不是要等待請求觸發才實例化。特殊servlet提早實例化加快了第一次訪問速度。主要有兩個步驟:
/** * Load and initialize all servlets marked "load on startup" in the web * application deployment descriptor. * * @param children * Array of wrappers for all currently defined servlets * (including those not declared load on startup) */ public void loadOnStartup(Container children[]) { // Collect "load on startup" servlets that need to be initialized TreeMap map = new TreeMap(); for (int i = 0; i < children.length; i++) { Wrapper wrapper = (Wrapper) children[i]; int loadOnStartup = wrapper.getLoadOnStartup(); if (loadOnStartup < 0) continue; Integer key = Integer.valueOf(loadOnStartup); ArrayList list = (ArrayList) map.get(key); if (list == null) { list = new ArrayList(); map.put(key, list); } list.add(wrapper); } // Load the collected "load on startup" servlets Iterator keys = map.keySet().iterator(); while (keys.hasNext()) { Integer key = (Integer) keys.next(); ArrayList list = (ArrayList) map.get(key); Iterator wrappers = list.iterator(); while (wrappers.hasNext()) { Wrapper wrapper = (Wrapper) wrappers.next(); try { wrapper.load(); } catch (ServletException e) { getLogger() .error(sm.getString( "standardWrapper.loadException", getName()), StandardWrapper.getRootCause(e)); // NOTE: load errors (including a servlet that throws // UnavailableException from tht init() method) are NOT // fatal to application startup } } } }
init()
負責初始化Context,爲Context啓動作準備工做。在方法中完成了Context配置類的實例化,並將其註冊到生命週期監聽器集合中,調用父類初始化函數super.init(),而後出發初始化事件。
start()
負責啓動Context,啓動web app應用,在啓動方法中要完成不少步驟:
/** * Start this Context component. * * @exception LifecycleException * if a startup error occurs */ public synchronized void start() throws LifecycleException { // if (lazy ) return; if (started) { if (log.isInfoEnabled()) log.info(sm .getString("containerBase.alreadyStarted", logName())); return; } if (!initialized) { try { init(); } catch (Exception ex) { throw new LifecycleException("Error initializaing ", ex); } } if (log.isDebugEnabled()) log.debug("Starting " + ("".equals(getName()) ? "ROOT" : getName())); // Set JMX object name for proper pipeline registration preRegisterJMX(); if ((oname != null) && (Registry.getRegistry(null, null).getMBeanServer() .isRegistered(oname))) { // As things depend on the JMX registration, the context // must be reregistered again once properly initialized Registry.getRegistry(null, null).unregisterComponent(oname); } // Notify our interested LifecycleListeners lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null); setAvailable(false); setConfigured(false); boolean ok = true; // Add missing components as necessary if (webappResources == null) { // (1) Required by Loader if (log.isDebugEnabled()) log.debug("Configuring default Resources"); try { if ((docBase != null) && (docBase.endsWith(".war")) && (!(new File(getBasePath())).isDirectory())) setResources(new WARDirContext()); else setResources(new FileDirContext()); } catch (IllegalArgumentException e) { log.error("Error initializing resources: " + e.getMessage()); ok = false; } } if (ok) { if (!resourcesStart()) { log.error("Error in resourceStart()"); ok = false; } } // Look for a realm - that may have been configured earlier. // If the realm is added after context - it'll set itself. // TODO: what is the use case for this ? if (realm == null && mserver != null) { ObjectName realmName = null; try { realmName = new ObjectName(getEngineName() + ":type=Realm,host=" + getHostname() + ",path=" + getPath()); if (mserver.isRegistered(realmName)) { mserver.invoke(realmName, "init", new Object[] {}, new String[] {}); } } catch (Throwable t) { if (log.isDebugEnabled()) log.debug("No realm for this host " + realmName); } } if (getLoader() == null) { WebappLoader webappLoader = new WebappLoader(getParentClassLoader()); webappLoader.setDelegate(getDelegate()); setLoader(webappLoader); } // Initialize character set mapper getCharsetMapper(); // Post work directory postWorkDirectory(); // Validate required extensions boolean dependencyCheck = true; try { dependencyCheck = ExtensionValidator.validateApplication( getResources(), this); } catch (IOException ioe) { log.error("Error in dependencyCheck", ioe); dependencyCheck = false; } if (!dependencyCheck) { // do not make application available if depency check fails ok = false; } // Reading the "catalina.useNaming" environment variable String useNamingProperty = System.getProperty("catalina.useNaming"); if ((useNamingProperty != null) && (useNamingProperty.equals("false"))) { useNaming = false; } if (ok && isUseNaming()) { if (namingContextListener == null) { namingContextListener = new NamingContextListener(); namingContextListener.setName(getNamingContextName()); addLifecycleListener(namingContextListener); } } // Standard container startup if (log.isDebugEnabled()) log.debug("Processing standard container startup"); // Binding thread ClassLoader oldCCL = bindThread(); boolean mainOk = false; try { if (ok) { started = true; // Start our subordinate components, if any if ((loader != null) && (loader instanceof Lifecycle)) ((Lifecycle) loader).start(); // since the loader just started, the webapp classloader is now // created. // By calling unbindThread and bindThread in a row, we setup the // current Thread CCL to be the webapp classloader unbindThread(oldCCL); oldCCL = bindThread(); // Initialize logger again. Other components might have used it // too early, // so it should be reset. logger = null; getLogger(); if ((logger != null) && (logger instanceof Lifecycle)) ((Lifecycle) logger).start(); if ((cluster != null) && (cluster instanceof Lifecycle)) ((Lifecycle) cluster).start(); if ((realm != null) && (realm instanceof Lifecycle)) ((Lifecycle) realm).start(); if ((resources != null) && (resources instanceof Lifecycle)) ((Lifecycle) resources).start(); // Start our child containers, if any Container children[] = findChildren(); for (int i = 0; i < children.length; i++) { if (children[i] instanceof Lifecycle) ((Lifecycle) children[i]).start(); } // Start the Valves in our pipeline (including the basic), // if any if (pipeline instanceof Lifecycle) { ((Lifecycle) pipeline).start(); } // Notify our interested LifecycleListeners lifecycle.fireLifecycleEvent(START_EVENT, null); // Acquire clustered manager Manager contextManager = null; if (manager == null) { if ((getCluster() != null) && distributable) { try { contextManager = getCluster().createManager( getName()); } catch (Exception ex) { log.error("standardContext.clusterFail", ex); ok = false; } } else { contextManager = new StandardManager(); } } // Configure default manager if none was specified if (contextManager != null) { setManager(contextManager); } if (manager != null && (getCluster() != null) && distributable) { // let the cluster know that there is a context that is // distributable // and that it has its own manager getCluster().registerManager(manager); } mainOk = true; } } finally { // Unbinding thread unbindThread(oldCCL); if (!mainOk) { // An exception occurred // Register with JMX anyway, to allow management registerJMX(); } } if (!getConfigured()) { log.error("Error getConfigured"); ok = false; } // We put the resources into the servlet context if (ok) getServletContext().setAttribute(Globals.RESOURCES_ATTR, getResources()); // Initialize associated mapper mapper.setContext(getPath(), welcomeFiles, resources); // Binding thread oldCCL = bindThread(); // Set annotation processing parameter for Jasper (unfortunately, since // this can be configured in many places and not just in // /WEB-INF/web.xml, // there are not many solutions) // Initialize annotation processor if (ok && !getIgnoreAnnotations()) { if (annotationProcessor == null) { if (isUseNaming() && namingContextListener != null) { annotationProcessor = new DefaultAnnotationProcessor( namingContextListener.getEnvContext()); } else { annotationProcessor = new DefaultAnnotationProcessor(null); } } getServletContext().setAttribute( AnnotationProcessor.class.getName(), annotationProcessor); } try { // Create context attributes that will be required if (ok) { postWelcomeFiles(); } // Set up the context init params mergeParameters(); if (ok) { // Notify our interested LifecycleListeners lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null); } // Configure and call application event listeners if (ok) { if (!listenerStart()) { log.error("Error listenerStart"); ok = false; } } try { // Start manager if ((manager != null) && (manager instanceof Lifecycle)) { ((Lifecycle) getManager()).start(); } // Start ContainerBackgroundProcessor thread super.threadStart(); } catch (Exception e) { log.error("Error manager.start()", e); ok = false; } // Configure and call application filters if (ok) { if (!filterStart()) { log.error("Error filterStart"); ok = false; } } // Load and initialize all "load on startup" servlets if (ok) { loadOnStartup(findChildren()); } } finally { // Unbinding thread unbindThread(oldCCL); } // Set available status depending upon startup success if (ok) { if (log.isDebugEnabled()) log.debug("Starting completed"); setAvailable(true); } else { log.error(sm.getString("standardContext.startFailed", getName())); try { stop(); } catch (Throwable t) { log.error(sm.getString("standardContext.startCleanup"), t); } setAvailable(false); } // JMX registration registerJMX(); startTime = System.currentTimeMillis(); // Send j2ee.state.running notification if (ok && (this.getObjectName() != null)) { Notification notification = new Notification("j2ee.state.running", this.getObjectName(), sequenceNumber++); broadcaster.sendNotification(notification); } // Close all JARs right away to avoid always opening a peak number // of files on startup if (getLoader() instanceof WebappLoader) { ((WebappLoader) getLoader()).closeJARs(true); } // Reinitializing if something went wrong if (!ok && started) { stop(); } // cacheContext(); }
cacheContext()
緩存Context對象,將對象序列化到本地文件中。
mergeParameters()
合併Context初始化參數配置
stop()
負責中止Context,清理Context中組件的狀態,以啓動Context的順序到這來:
/** * Stop this Context component. * * @exception LifecycleException * if a shutdown error occurs */ public synchronized void stop() throws LifecycleException { // Validate and update our current component state if (!started) { if (log.isInfoEnabled()) log.info(sm.getString("containerBase.notStarted", logName())); return; } // Notify our interested LifecycleListeners lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null); // Send j2ee.state.stopping notification if (this.getObjectName() != null) { Notification notification = new Notification("j2ee.state.stopping", this.getObjectName(), sequenceNumber++); broadcaster.sendNotification(notification); } // Mark this application as unavailable while we shut down setAvailable(false); // Binding thread ClassLoader oldCCL = bindThread(); try { // Stop our child containers, if any Container[] children = findChildren(); for (int i = 0; i < children.length; i++) { if (children[i] instanceof Lifecycle) ((Lifecycle) children[i]).stop(); } // Stop our filters filterStop(); // Stop ContainerBackgroundProcessor thread super.threadStop(); if ((manager != null) && (manager instanceof Lifecycle)) { ((Lifecycle) manager).stop(); } // Stop our application listeners listenerStop(); // Finalize our character set mapper setCharsetMapper(null); // Normal container shutdown processing if (log.isDebugEnabled()) log.debug("Processing standard container shutdown"); // Notify our interested LifecycleListeners lifecycle.fireLifecycleEvent(STOP_EVENT, null); started = false; // Stop the Valves in our pipeline (including the basic), if any if (pipeline instanceof Lifecycle) { ((Lifecycle) pipeline).stop(); } // Clear all application-originated servlet context attributes if (context != null) context.clearAttributes(); // Stop resources resourcesStop(); if ((realm != null) && (realm instanceof Lifecycle)) { ((Lifecycle) realm).stop(); } if ((cluster != null) && (cluster instanceof Lifecycle)) { ((Lifecycle) cluster).stop(); } if ((logger != null) && (logger instanceof Lifecycle)) { ((Lifecycle) logger).stop(); } if ((loader != null) && (loader instanceof Lifecycle)) { ((Lifecycle) loader).stop(); } } finally { // Unbinding thread unbindThread(oldCCL); } // Send j2ee.state.stopped notification if (this.getObjectName() != null) { Notification notification = new Notification("j2ee.state.stopped", this.getObjectName(), sequenceNumber++); broadcaster.sendNotification(notification); } // Reset application context context = null; // This object will no longer be visible or used. try { resetContext(); } catch (Exception ex) { log.error("Error reseting context " + this + " " + ex, ex); } // Notify our interested LifecycleListeners lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null); if (log.isDebugEnabled()) log.debug("Stopping complete"); }
destroy()
負責銷燬Context任務,在方法中主要是調用基類的destroy()方法銷燬Context。
/** * Destroy needs to clean up the context completely. * * The problem is that undoing all the config in start() and restoring a * 'fresh' state is impossible. After stop()/destroy()/init()/start() we * should have the same state as if a fresh start was done - i.e read * modified web.xml, etc. This can only be done by completely removing the * context object and remapping a new one, or by cleaning up everything. * * XXX Should this be done in stop() ? * */ public void destroy() throws Exception { if (oname != null) { // Send j2ee.object.deleted notification Notification notification = new Notification("j2ee.object.deleted", this.getObjectName(), sequenceNumber++); broadcaster.sendNotification(notification); } super.destroy(); // Notify our interested LifecycleListeners lifecycle.fireLifecycleEvent(DESTROY_EVENT, null); synchronized (instanceListenersLock) { instanceListeners = new String[0]; } }
resetContext()
負責重置Context容器,將恢復至原始狀態。
adjustURLPattern(String)
負責將URL規範化。
/** * Adjust the URL pattern to begin with a leading slash, if appropriate * (i.e. we are running a servlet 2.2 application). Otherwise, return the * specified URL pattern unchanged. * * @param urlPattern * The URL pattern to be adjusted (if needed) and returned */ protected String adjustURLPattern(String urlPattern) { if (urlPattern == null) return (urlPattern); if (urlPattern.startsWith("/") || urlPattern.startsWith("*.")) return (urlPattern); if (!isServlet22()) return (urlPattern); if (log.isDebugEnabled()) log.debug(sm.getString("standardContext.urlPattern.patternWarning", urlPattern)); return ("/" + urlPattern); }
bindThread()
綁定線程類加載器,保存現有線程的類加載器,而後將Context的類加載器綁定至當前線程中,將Context資源服務綁定至當前線程,綁定命名服務類加載器,這樣能夠確保在Context中的資源在同一個類加載器上面。
/** * Bind current thread, both for CL purposes and for JNDI ENC support during * : startup, shutdown and realoading of the context. * * @return the previous context class loader */ private ClassLoader bindThread() { ClassLoader oldContextClassLoader = Thread.currentThread() .getContextClassLoader(); if (getResources() == null) return oldContextClassLoader; if (getLoader().getClassLoader() != null) { Thread.currentThread().setContextClassLoader( getLoader().getClassLoader()); } DirContextURLStreamHandler.bindThread(getResources()); if (isUseNaming()) { try { ContextBindings.bindThread(this, this); } catch (NamingException e) { // Silent catch, as this is a normal case during the early // startup stages } } return oldContextClassLoader; }
unbindThread(ClassLoader)
解綁線程類加載器,與bindThrea()方法的功能相反,還原線程類加載器。
/** * Unbind thread. */ private void unbindThread(ClassLoader oldContextClassLoader) { if (isUseNaming()) { ContextBindings.unbindThread(this, this); } DirContextURLStreamHandler.unbindThread(); Thread.currentThread().setContextClassLoader(oldContextClassLoader); }
validateURLPattern(String)
驗證訪問URL是否合法。
/** * Validate the syntax of a proposed <code><url-pattern></code> for * conformance with specification requirements. * * @param urlPattern * URL pattern to be validated */ private boolean validateURLPattern(String urlPattern) { if (urlPattern == null) return (false); if (urlPattern.indexOf('\n') >= 0 || urlPattern.indexOf('\r') >= 0) { return (false); } if (urlPattern.startsWith("*.")) { if (urlPattern.indexOf('/') < 0) { checkUnusualURLPattern(urlPattern); return (true); } else return (false); } if ((urlPattern.startsWith("/")) && (urlPattern.indexOf("*.") < 0)) { checkUnusualURLPattern(urlPattern); return (true); } else return (false); }
getServlets()
返回Context中全部的Servlet。
Context做爲web app的上下文,負責維護和管理在Context中的組件的生命週期,Session管理,Filter過濾,加載Servlet,接收響應請求,應用所需資源加載和維護,Context的數據共享和數據安全隔離等一個webapp所須要的所有功能,上面講到的是Context中的部份內容,其餘內容還請本身查看。
有不足之處請斧正,歡迎吐槽...
堅持,騷年,你能夠的