- 浏览: 981004 次
文章分类
- 全部博客 (428)
- Hadoop (2)
- HBase (1)
- ELK (1)
- ActiveMQ (13)
- Kafka (5)
- Redis (14)
- Dubbo (1)
- Memcached (5)
- Netty (56)
- Mina (34)
- NIO (51)
- JUC (53)
- Spring (13)
- Mybatis (17)
- MySQL (21)
- JDBC (12)
- C3P0 (5)
- Tomcat (13)
- SLF4J-log4j (9)
- P6Spy (4)
- Quartz (12)
- Zabbix (7)
- JAVA (9)
- Linux (15)
- HTML (9)
- Lucene (0)
- JS (2)
- WebService (1)
- Maven (4)
- Oracle&MSSQL (14)
- iText (11)
- Development Tools (8)
- UTILS (4)
- LIFE (8)
最新评论
-
Donald_Draper:
Donald_Draper 写道刘落落cici 写道能给我发一 ...
DatagramChannelImpl 解析三(多播) -
Donald_Draper:
刘落落cici 写道能给我发一份这个类的源码吗Datagram ...
DatagramChannelImpl 解析三(多播) -
lyfyouyun:
请问楼主,执行消息发送的时候,报错:Transport sch ...
ActiveMQ连接工厂、连接详解 -
ezlhq:
关于 PollArrayWrapper 状态含义猜测:参考 S ...
WindowsSelectorImpl解析一(FdMap,PollArrayWrapper) -
flyfeifei66:
打算使用xmemcache作为memcache的客户端,由于x ...
Memcached分布式客户端(Xmemcached)
Tomcat的Engine初始化,启动过程:http://donald-draper.iteye.com/blog/2327119
Tomcat配置文件:
Host作为Engine的子容器,我们来看看Host的初始化与启动
//StandardHostValve
//ErrorPage
在StandardHost中我们看到有一个StandardContext,
StandardContext也继承了ContainerBase
从上面可以分析得出StardardContext主要,是根据Web.xml的配置,
初始化欢迎界面,角色权限,初始参数,错误处理,Listener,Filter
和Sevlet等。
来看StandardWrapper从load函数,StandardWrapper同样继承里ContainerBase
,作为一个容器
//StandardWrapperValve,Servlet请求处理包装类
//从StandardWrapperValve可以看出,其通过包装Request,StandardWrapper,Servlet
到ApplicationFilterChain,然后执行Filter的doFilter方法;
下面我们看一ApplicationFilterChain
上面我们分析 StandardWraper时,他的invove()函数获取Servlet有一个方法
allocate,这个方法的实现在StandardWrape
//从StandardWraper中获取Servlet实例,下面我们分析这个函数
servlet = wrapper.allocate();
//从分析StrandardWrapper可以看出,从StrandardWrapper的allocate方法,看出,当Servlet是单线程模型是则直接返回instance,否则加载一个新的Servlet,push到Servlet栈instancePool中。
总结:
从上面可以分析得出每个Host关联一个StardardContext,StardardContext是根据Web.xml的配置,初始化欢迎界面,角色权限,初始参数,错误处理,Listener,Filter和Sevlet等。
在加载Servlet的过程中,通过反射,调用Servlet的init方法。每个StandardContext有多个StandardWrappe子容器,StandardWrappe关联一个StandardPileLine,StandardPileLine中装配这StandardWrappeValve,StandardWrappeValve处理HTTP请求,通过invoke(),在invoke中,同StandardWrappe的allocate方法获取Servlet实例instance,然后将Request,StandardWrapper,Servlet包装到ApplicationFilterChain,然后执行Filter的doFilter方法;在Filter的doFilter中,限制性filter操作,然后通过反射调用servlet的service方法。下篇文章将介绍ContextConfig
附:
//tld配置
//StandardContextValve
Tomcat配置文件:
<Engine name="Catalina" defaultHost="localhost"> <!-- <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/> --> <Realm className="org.apache.catalina.realm.LockOutRealm"> <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/> </Realm> <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true"> <!-- SingleSignOn valve, share authentication between web applications Documentation at: /docs/config/valve.html --> <!-- <Valve className="org.apache.catalina.authenticator.SingleSignOn" /> --> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log." suffix=".txt" pattern="%h %l %u %t "%r" %s %b" /> </Host> </Engine>
Host作为Engine的子容器,我们来看看Host的初始化与启动
public class StandardHost extends ContainerBase implements Host { private final Object aliasesLock = new Object(); /**The application root for this Host.默认应用根目录*/ private String appBase = "webapps"; /** The XML root for this Host.*/ private String xmlBase = null; /** The auto deploy flag for this Host.*/ private boolean autoDeploy = true; /** * The Java class name of the default context configuration class * for deployed web applications. */ //上下文配置类 private String configClass = "org.apache.catalina.startup.ContextConfig"; /** * The Java class name of the default Context implementation class for * deployed web applications. */ //Host的上下文加载实现类 private String contextClass = "org.apache.catalina.core.StandardContext"; /** The deploy on startup flag for this Host.*/ private boolean deployOnStartup = true; /** deploy Context XML config files property. */ private boolean deployXML = !Globals.IS_SECURITY_ENABLED; /** * Should XML files be copied to * $CATALINA_BASE/conf/<engine>/<host> by default when * a web application is deployed? */ private boolean copyXML = false; /** * The Java class name of the default error reporter implementation class * for deployed web applications. */ private String errorReportValveClass = "org.apache.catalina.valves.ErrorReportValve"; //实现类类信息 private static final String info = "org.apache.catalina.core.StandardHost/1.0"; //是否解压WARs private boolean unpackWARs = true; //applications的工作目录 private String workDir = null; //启动 appBase and xmlBase的时候,是否创建目录 private boolean createDirs = true; //子ClassLoaderMap,以便检测内存泄漏情况 private Map<ClassLoader, String> childClassLoaders = new WeakHashMap<ClassLoader, String>(); private Pattern deployIgnore = null; private boolean undeployOldVersions = false; private boolean failCtxIfServletStartFails = false; //构造HOST public StandardHost() { super(); pipeline.setBasic(new StandardHostValve()); } //启动 protected synchronized void startInternal() throws LifecycleException { String errorValve = getErrorReportValveClass(); if ((errorValve != null) && (!errorValve.equals(""))) { try { boolean found = false; Valve[] valves = getPipeline().getValves(); for (Valve valve : valves) { if (errorValve.equals(valve.getClass().getName())) { found = true; break; } } if(!found) { Valve valve = (Valve) Class.forName(errorValve).newInstance(); //添加valve到Pipeline getPipeline().addValve(valve); } } } //这个调用的是ContainerBase的startInternal //这个在上面我们已经说过 super.startInternal(); } }
//StandardHostValve
final class StandardHostValve extends ValveBase { static final boolean STRICT_SERVLET_COMPLIANCE; static final boolean ACCESS_SESSION; static { STRICT_SERVLET_COMPLIANCE = Globals.STRICT_SERVLET_COMPLIANCE; String accessSession = System.getProperty( "org.apache.catalina.core.StandardHostValve.ACCESS_SESSION"); if (accessSession == null) { ACCESS_SESSION = STRICT_SERVLET_COMPLIANCE; } else { ACCESS_SESSION = Boolean.parseBoolean(accessSession); } } //根据request的URI选择一个child context来处理Request, //如果没有匹配的上下文,则返回HTTP error public final void invoke(Request request, Response response) throws IOException, ServletException { //获取请求的上下文 Context context = request.getContext(); if (context == null) { response.sendError (HttpServletResponse.SC_INTERNAL_SERVER_ERROR, sm.getString("standardHost.noContext")); return; } // Bind the context CL to the current thread if( context.getLoader() != null ) { // Not started - it should check for availability first // This should eventually move to Engine, it's generic. if (Globals.IS_SECURITY_ENABLED) { PrivilegedAction<!-- <Void> --> pa = new PrivilegedSetTccl( context.getLoader().getClassLoader()); AccessController.doPrivileged(pa); } else { Thread.currentThread().setContextClassLoader (context.getLoader().getClassLoader()); } } if (request.isAsyncSupported()) { request.setAsyncSupported(context.getPipeline().isAsyncSupported()); } boolean asyncAtStart = request.isAsync(); boolean asyncDispatching = request.isAsyncDispatching(); if (asyncAtStart || context.fireRequestInitEvent(request)) { Throwable t = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION); // Look for (and render if found) an application level error page if (response.isErrorReportRequired()) { if (t != null) { throwable(request, response, t); } else { //返回处理状态 status(request, response); } } } } //返回处理状态,如果异常发生,返回错误状态码,对应的错误页面(web.xml-errorPage) private void status(Request request, Response response) { int statusCode = response.getStatus(); // Handle a custom error page for this status code Context context = request.getContext(); if (context == null) return; /* Only look for error pages when isError() is set. * isError() is set when response.sendError() is invoked. This * allows custom error pages without relying on default from * web.xml. */ if (!response.isError()) return; //从Context中,获取statusCode对应的页面 ErrorPage errorPage = context.findErrorPage(statusCode); if (errorPage == null) { // Look for a default error page errorPage = context.findErrorPage(0); } if (errorPage != null && response.isErrorReportRequired()) { response.setAppCommitted(false); request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE, Integer.valueOf(statusCode)); String message = response.getMessage(); if (message == null) message = ""; request.setAttribute(RequestDispatcher.ERROR_MESSAGE, message); request.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR, errorPage.getLocation()); request.setAttribute(Globals.DISPATCHER_TYPE_ATTR, DispatcherType.ERROR); Wrapper wrapper = request.getWrapper(); if (wrapper != null) request.setAttribute(RequestDispatcher.ERROR_SERVLET_NAME, wrapper.getName()); request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI, request.getRequestURI()); if (custom(request, response, errorPage)) { response.setErrorReported(); try { response.finishResponse(); } catch (ClientAbortException e) { // Ignore } catch (IOException e) { container.getLogger().warn("Exception Processing " + errorPage, e); } } } }
//ErrorPage
public class ErrorPage implements Serializable { //错误码 private int errorCode = 0; private String exceptionType = null; //处理异常的Handler位置(context) private String location = null; }
在StandardHost中我们看到有一个StandardContext,
StandardContext也继承了ContainerBase
public class StandardContext extends ContainerBase implements Context, NotificationEmitter { public StandardContext() { super(); pipeline.setBasic(new StandardContextValve()); broadcaster = new NotificationBroadcasterSupport(); // Set defaults if (!Globals.STRICT_SERVLET_COMPLIANCE) { // Strict servlet compliance requires all extension mapped servlets // to be checked against welcome files resourceOnlyServlets.add("jsp"); } //url安全字符集 protected static URLEncoder urlEncoder; static { urlEncoder = new URLEncoder(); urlEncoder.addSafeCharacter('~'); urlEncoder.addSafeCharacter('-'); urlEncoder.addSafeCharacter('_'); urlEncoder.addSafeCharacter('.'); urlEncoder.addSafeCharacter('*'); urlEncoder.addSafeCharacter('/'); } /** * The set of application listener class names configured for this * application, in the order they were encountered in the resulting merged * web.xml file. */ private ApplicationListener applicationListeners[] =new ApplicationListener[0]; private final Object applicationListenersLock = new Object(); /** The ordered set of ServletContainerInitializers for this web application.*/ private Map<ServletContainerInitializer,Set<Class<?>>> initializers = new LinkedHashMap<ServletContainerInitializer,Set<Class<?>>>(); /**The set of application parameters defined for this application.*/ private ApplicationParameter applicationParameters[] = new ApplicationParameter[0]; private final Object applicationParametersLock = new Object(); /** The Locale to character set mapper for this application.*/ private CharsetMapper charsetMapper = null; /** The Java class name of the CharsetMapper class to be created.*/ private String charsetMapperClass ="org.apache.catalina.util.CharsetMapper"; /** The URL of the XML descriptor for this context.*/ private URL configFile = null; /** The "correctly configured" flag for this Context.*/ private boolean configured = false; /** The security constraints for this web application.*/ private volatile SecurityConstraint constraints[] = new SecurityConstraint[0]; private final Object constraintsLock = new Object(); /** The ServletContext implementation associated with this Context.*/ protected ApplicationContext context = null; /** * The wrapped version of the associated ServletContext that is presented * to listeners that are required to have limited access to ServletContext * methods. See Servlet 3.0 section 4.4. */ private NoPluggabilityServletContext noPluggabilityServletContext = null; /**Compiler classpath to use.*/ private String compilerClasspath = null; /**Should we attempt to use cookies for session id communication?*/ private boolean cookies = true; /** * Should we allow the <code>ServletContext.getContext()</code> method * to access the context of other web applications in this server? */ private boolean crossContext = false; /**Encoded path.*/ private String encodedPath = null; /**Unencoded path for this web application.*/ private String path = null; /** * The "follow standard delegation model" flag that will be used to * configure our ClassLoader. */ private boolean delegate = false; /** The display name of this web application.*/ private String displayName = null; /** Override the default context xml location.*/ private String defaultContextXml; /** Override the default web xml location.*/ private String defaultWebXml; /** The distributable flag for this web application.*/ private boolean distributable = false; /** The document root for this web application.*/ private String docBase = null; //异常页面定义映射 private HashMap<String, ErrorPage> exceptionPages = new HashMap<String, ErrorPage>(); /** * The set of filter configurations (and associated filter instances) we * have initialized, keyed by filter name. */ private HashMap<String, ApplicationFilterConfig> filterConfigs = new HashMap<String, ApplicationFilterConfig>(); //filter定义 private HashMap<String, FilterDef> filterDefs = new HashMap<String, FilterDef>(); //Filter映射 private final ContextFilterMaps filterMaps = new ContextFilterMaps(); /** * The set of classnames of InstanceListeners that will be added * to each newly created Wrapper by <code>createWrapper()</code>. */ private String instanceListeners[] = new String[0]; private final Object instanceListenersLock = new Object(); /** The login configuration descriptor for this web application. */ private LoginConfig loginConfig = null; /** The mapper associated with this context.*/ private org.apache.tomcat.util.http.mapper.Mapper mapper = new org.apache.tomcat.util.http.mapper.Mapper(); /** The naming context listener for this web application.*/ private NamingContextListener namingContextListener = null; /** The naming resources for this web application. */ private NamingResources namingResources = null; /** The message destinations for this web application.*/ private HashMap<String, MessageDestination> messageDestinations = new HashMap<String, MessageDestination>(); /** The MIME mappings for this web application, keyed by extension.*/ private HashMap<String, String> mimeMappings =new HashMap<String, String>(); /** Special case: error page for status 200. */ private ErrorPage okErrorPage = null; /** * The context initialization parameters for this web application,keyed by name. */ private final ConcurrentMap<String, String> parameters = new ConcurrentHashMap<String, String>(); /** Context level override for default {@link StandardHost#isCopyXML()}.*/ private boolean copyXML = false; /** The default context override flag for this web application.*/ private boolean override = false; /** The original document root for this web application.*/ private String originalDocBase = null; //角色映射 private HashMap<String, String> roleMappings =new HashMap<String, String>(); //安全角色 private String securityRoles[] = new String[0]; private final Object securityRolesLock = new Object(); //SevletMapping private HashMap<String, String> servletMappings = new HashMap<String, String>(); private final Object servletMappingsLock = new Object(); /** The session timeout (in minutes) for this web application.*/ private int sessionTimeout = 30; /** The notification sequence number.*/ private AtomicLong sequenceNumber = new AtomicLong(0); private HashMap<Integer, ErrorPage> statusPages =new HashMap<Integer, ErrorPage>(); //应用欢迎文件 private String welcomeFiles[] = new String[0]; private final Object welcomeFilesLock = new Object(); /** The pathname to the work directory for this context (relative to the server's home if not absolute). */ //工作目录 private String workDir = null; //Wrapper的实现类Java class name private String wrapperClassName = StandardWrapper.class.getName(); private Class<?> wrapperClass = null; /**Caching allowed flag.*/ private boolean cachingAllowed = true; /** Cache TTL in ms.*/ protected int cacheTTL = 5000; private String sessionCookieName; private String sessionCookiePath; //ApplicationContext#createServlet(Class),Servlet集合 private Set<Servlet> createdServlets = new HashSet<Servlet>(); //初始化 protected void initInternal() throws LifecycleException { super.initInternal(); if (processTlds) { //添加tld监听器 this.addLifecycleListener(new TldConfig()); } // Register the naming resources if (namingResources != null) { namingResources.init(); } // Send j2ee.object.created notification if (this.getObjectName() != null) { Notification notification = new Notification("j2ee.object.created", this.getObjectName(), sequenceNumber.getAndIncrement()); broadcaster.sendNotification(notification); } } protected synchronized void startInternal() throws LifecycleException { if(log.isDebugEnabled()) log.debug("Starting " + getBaseName()); // Send j2ee.state.starting notification if (this.getObjectName() != null) { Notification notification = new Notification("j2ee.state.starting", this.getObjectName(), sequenceNumber.getAndIncrement()); broadcaster.sendNotification(notification); } setConfigured(false); boolean ok = true; if (namingResources != null) { namingResources.start();//配置namingResources } // 初始化Cached,CacheTTL,CacheMaxSize,CacheObjectMaxSize等 if (webappResources == null) { // (1) Required by Loader try { String docBase = getDocBase(); if (docBase == null) { setResources(new EmptyDirContext()); } else if (docBase.endsWith(".war") && !(new File(getBasePath())).isDirectory()) { setResources(new WARDirContext()); } else { setResources(new FileDirContext()); } } } if (ok) { if (!resourcesStart()) { throw new LifecycleException("Error in resourceStart()"); } } //配置WebappLoader 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); } // 绑定线程ClassLoader ClassLoader oldCCL = bindThread(); try { if (ok) { // 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 ((cluster != null) && (cluster instanceof Lifecycle)) ((Lifecycle) cluster).start(); Realm realm = getRealmInternal(); if ((realm != null) && (realm instanceof Lifecycle)) ((Lifecycle) realm).start(); if ((resources != null) && (resources instanceof Lifecycle)) ((Lifecycle) resources).start(); // Notify our interested LifecycleListeners fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null); // Start our child containers, if not already started for (Container child : findChildren()) { if (!child.getState().isAvailable()) { child.start(); } } // Start the Valves in our pipeline (including the basic), // if any if (pipeline instanceof Lifecycle) { ((Lifecycle) pipeline).start(); } // Acquire clustered manager Manager contextManager = null; if (manager == null) { if (log.isDebugEnabled()) { log.debug(sm.getString("standardContext.cluster.noManager", Boolean.valueOf((getCluster() != null)), Boolean.valueOf(distributable))); } 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) { if (log.isDebugEnabled()) { log.debug(sm.getString("standardContext.manager", contextManager.getClass().getName())); } 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); } } } finally { // Unbinding thread unbindThread(oldCCL); } if (!getConfigured()) { log.error(sm.getString("standardContext.configurationFail")); ok = false; } // We put the resources into the servlet context if (ok) getServletContext().setAttribute (Globals.RESOURCES_ATTR, getResources()); //初始化欢迎页面 mapper.setContext(getPath(), welcomeFiles, resources); // Binding thread oldCCL = bindThread(); if (ok ) { if (getInstanceManager() == null) { javax.naming.Context context = null; if (isUseNaming() && getNamingContextListener() != null) { context = getNamingContextListener().getEnvContext(); } Map<String, Map<String, String>> injectionMap = buildInjectionMap( getIgnoreAnnotations() ? new NamingResources(): getNamingResources()); setInstanceManager(new DefaultInstanceManager(context, injectionMap, this, this.getClass().getClassLoader())); getServletContext().setAttribute( InstanceManager.class.getName(), getInstanceManager()); } } try { // Create context attributes that will be required if (ok) { getServletContext().setAttribute( JarScanner.class.getName(), getJarScanner()); } //初始化上下文参数 mergeParameters(); // Call ServletContainerInitializers for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry : initializers.entrySet()) { try { entry.getKey().onStartup(entry.getValue(), getServletContext()); } // Configure and call application event listeners if (ok) { //初始化应用监听器 if (!listenerStart()) { log.error(sm.getString("standardContext.listenerFail")); ok = false; } } try { // Start manager if ((manager != null) && (manager instanceof Lifecycle)) { ((Lifecycle) getManager()).start(); } } // Configure and call application filters if (ok) { //初始化Fliter if (!filterStart()) { log.error(sm.getString("standardContext.filterFail")); ok = false; } } // Load and initialize all "load on startup" servlets if (ok) { //初始化servlet关键步骤,StandardWrapper为Sevlet的包装类 if (!loadOnStartup(findChildren())){ log.error(sm.getString("standardContext.servletFail")); ok = false; } } // Start ContainerBackgroundProcessor thread super.threadStart(); } finally { // Unbinding thread unbindThread(oldCCL); } startTime=System.currentTimeMillis(); // Send j2ee.state.running notification if (ok && (this.getObjectName() != null)) { Notification notification = new Notification("j2ee.state.running", this.getObjectName(), sequenceNumber.getAndIncrement()); 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) { setState(LifecycleState.FAILED); } else { setState(LifecycleState.STARTING); } } //初始化Fliter public boolean filterStart() { // Instantiate and record a FilterConfig for each defined filter boolean ok = true; synchronized (filterConfigs) { filterConfigs.clear(); for (Entry<String, FilterDef> entry : filterDefs.entrySet()) { String name = entry.getKey(); if (getLogger().isDebugEnabled()) getLogger().debug(" Starting filter '" + name + "'"); ApplicationFilterConfig filterConfig = null; try { filterConfig = new ApplicationFilterConfig(this, entry.getValue()); filterConfigs.put(name, filterConfig); } } } return (ok); } //加载StandardWrapper,即Servlet包装类 public boolean loadOnStartup(Container children[]) { // Collect "load on startup" servlets that need to be initialized TreeMap<Integer, ArrayList<Wrapper>> map = new TreeMap<Integer, ArrayList<Wrapper>>(); 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<Wrapper> list = map.get(key); if (list == null) { list = new ArrayList<Wrapper>(); map.put(key, list); } list.add(wrapper); } // Load the collected "load on startup" servlets for (ArrayList<Wrapper> list : map.values()) { for (Wrapper wrapper : list) { try { //关键在这个load中 wrapper.load(); } } } return true; } }
从上面可以分析得出StardardContext主要,是根据Web.xml的配置,
初始化欢迎界面,角色权限,初始参数,错误处理,Listener,Filter
和Sevlet等。
来看StandardWrapper从load函数,StandardWrapper同样继承里ContainerBase
,作为一个容器
public class StandardWrapper extends ContainerBase implements ServletConfig, Wrapper, NotificationEmitter { protected static final String[] DEFAULT_SERVLET_METHODS = new String[] { "GET", "HEAD", "POST" }; //为StandardWrapper创建一个默认的StandardWrapperValve public StandardWrapper() { super(); swValve=new StandardWrapperValve(); pipeline.setBasic(swValve); broadcaster = new NotificationBroadcasterSupport(); } ** * The count of allocations that are currently active (even if they * are for the same instance, as will be true on a non-STM servlet). */ protected AtomicInteger countAllocated = new AtomicInteger(0); /** * The load-on-startup order value (negative value means load on * first call) for this servlet. */ protected int loadOnStartup = -1; protected volatile Servlet instance = null; /** * Mappings associated with the wrapper. */ protected ArrayList<String> mappings = new ArrayList<String>(); /** * The initialization parameters for this servlet, keyed by * parameter name. */ protected HashMap<String, String> parameters = new HashMap<String, String>(); /** * True if this StandardWrapper is for the JspServlet */ protected boolean isJspServlet; /** * The ObjectName of the JSP monitoring mbean */ protected ObjectName jspMonitorON; //Servlet栈 protected Stack<Servlet> instancePool = null; //启动Wrapper容器 protected synchronized void startInternal() throws LifecycleException { // Send j2ee.state.starting notification if (this.getObjectName() != null) { Notification notification = new Notification("j2ee.state.starting", this.getObjectName(), sequenceNumber++); broadcaster.sendNotification(notification); } // Start up this component super.startInternal(); setAvailable(0L); // Send j2ee.state.running notification if (this.getObjectName() != null) { Notification notification = new Notification("j2ee.state.running", this.getObjectName(), sequenceNumber++); broadcaster.sendNotification(notification); } } //创建一个Servlet实例 public synchronized void load() throws ServletException { //加载Servlet实例 instance = loadServlet(); if (!instanceInitialized) { //初始化Servlet initServlet(instance); } //JSP页面Servlet的处理 if (isJspServlet) { StringBuilder oname = new StringBuilder(MBeanUtils.getDomain(getParent())); oname.append(":type=JspMonitor,name="); oname.append(getName()); oname.append(getWebModuleKeyProperties()); try { jspMonitorON = new ObjectName(oname.toString()); Registry.getRegistry(null, null) .registerComponent(instance, jspMonitorON, null); } } } //加载Servlet public synchronized Servlet loadServlet() throws ServletException { // Nothing to do if we already have an instance or an instance pool if (!singleThreadModel && (instance != null)) return instance; PrintStream out = System.out; if (swallowOutput) { SystemLogHandler.startCapture(); } Servlet servlet; try { long t1=System.currentTimeMillis(); // Complain if no servlet class has been specified if (servletClass == null) { unavailable(null); throw new ServletException (sm.getString("standardWrapper.notClass", getName())); } InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager(); try { //加载servletClass实例 servlet = (Servlet) instanceManager.newInstance(servletClass); } if (multipartConfigElement == null) { MultipartConfig annotation = servlet.getClass().getAnnotation(MultipartConfig.class); if (annotation != null) { multipartConfigElement = new MultipartConfigElement(annotation); } } processServletSecurityAnnotation(servlet.getClass()); // Special handling for ContainerServlet instances if ((servlet instanceof ContainerServlet) && (isContainerProvidedServlet(servletClass) || ((Context) getParent()).getPrivileged() )) { ((ContainerServlet) servlet).setWrapper(this); } classLoadTime=(int) (System.currentTimeMillis() -t1); if (servlet instanceof SingleThreadModel) { if (instancePool == null) { //初始化Servlet栈 instancePool = new Stack<Servlet>(); } singleThreadModel = true; } initServlet(servlet); fireContainerEvent("load", this); loadTime=System.currentTimeMillis() -t1; } finally { if (swallowOutput) { String log = SystemLogHandler.stopCapture(); if (log != null && log.length() > 0) { if (getServletContext() != null) { getServletContext().log(log); } else { out.println(log); } } } } return servlet; } //初始化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 { [color=red] //初始化Servlet,这个方法大家应该熟悉 servlet.init(facade);[/color] } instanceInitialized = true; instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT, servlet); } } }
//StandardWrapperValve,Servlet请求处理包装类
final class StandardWrapperValve extends ValveBase { //------------------------------------------------------ Constructor public StandardWrapperValve() { super(true); } // ----------------------------------------------------- Instance Variables // Some JMX statistics. This valve is associated with a StandardWrapper. // We expose the StandardWrapper as JMX ( j2eeType=Servlet ). The fields // are here for performance. private volatile long processingTime; private volatile long maxTime; private volatile long minTime = Long.MAX_VALUE; private volatile int requestCount; private volatile int errorCount; //处理Http请求,根据Servlet的映射 public final void invoke(Request request, Response response) throws IOException, ServletException { // Initialize local variables we may need boolean unavailable = false; Throwable throwable = null; // This should be a Request attribute... long t1=System.currentTimeMillis(); requestCount++; StandardWrapper wrapper = (StandardWrapper) getContainer(); Servlet servlet = null; //从StandardWrapper中获取Context Context context = (Context) wrapper.getParent(); // 检查Context状态是否可利用,否返回错误 if (!context.getState().isAvailable()) { response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, sm.getString("standardContext.isUnavailable")); unavailable = true; } // Check for the servlet being marked unavailable if (!unavailable && wrapper.isUnavailable()) { container.getLogger().info(sm.getString("standardWrapper.isUnavailable", wrapper.getName())); long available = wrapper.getAvailable(); if ((available > 0L) && (available < Long.MAX_VALUE)) { //设置头部 response.setDateHeader("Retry-After", available); response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, sm.getString("standardWrapper.isUnavailable", wrapper.getName())); } else if (available == Long.MAX_VALUE) { response.sendError(HttpServletResponse.SC_NOT_FOUND, sm.getString("standardWrapper.notFound", wrapper.getName())); } unavailable = true; } // Allocate a servlet instance to process this request try { if (!unavailable) { //从StandardWraper中获取Servlet实例,下面我们分析这个函数 servlet = wrapper.allocate(); } } // Identify if the request is Comet related now that the servlet has been allocated boolean comet = false; if (servlet instanceof CometProcessor && Boolean.TRUE.equals(request.getAttribute( Globals.COMET_SUPPORTED_ATTR))) { comet = true; request.setComet(true); } MessageBytes requestPathMB = request.getRequestPathMB(); DispatcherType dispatcherType = DispatcherType.REQUEST; if (request.getDispatcherType()==DispatcherType.ASYNC) dispatcherType = DispatcherType.ASYNC; request.setAttribute(Globals.DISPATCHER_TYPE_ATTR,dispatcherType); request.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR, requestPathMB); // Create the filter chain for this request ApplicationFilterFactory factory = ApplicationFilterFactory.getInstance(); //创建应用Filter链 ApplicationFilterChain filterChain = factory.createFilterChain(request, wrapper, servlet); // Reset comet flag value after creating the filter chain request.setComet(false); // Call the filter chain for this request // NOTE: This also calls the servlet's service() method try { if ((servlet != null) && (filterChain != null)) { // Swallow output if needed if (context.getSwallowOutput()) { try { SystemLogHandler.startCapture(); if (request.isAsyncDispatching()) { request.getAsyncContextInternal().doInternalDispatch(); } else if (comet) { filterChain.doFilterEvent(request.getEvent()); request.setComet(true); } else { //Filter处理 filterChain.doFilter(request.getRequest(), response.getResponse()); } } finally { String log = SystemLogHandler.stopCapture(); if (log != null && log.length() > 0) { context.getLogger().info(log); } } } else { if (request.isAsyncDispatching()) { request.getAsyncContextInternal().doInternalDispatch(); } else if (comet) { request.setComet(true); filterChain.doFilterEvent(request.getEvent()); } else { filterChain.doFilter (request.getRequest(), response.getResponse()); } } } } // Release the filter chain (if any) for this request if (filterChain != null) { if (request.isComet()) { // If this is a Comet request, then the same chain will be used for the // processing of all subsequent events. filterChain.reuse(); } else { filterChain.release(); } } // Deallocate the allocated servlet instance try { if (servlet != null) { wrapper.deallocate(servlet); } } catch (Throwable e) { ExceptionUtils.handleThrowable(e); container.getLogger().error(sm.getString("standardWrapper.deallocateException", wrapper.getName()), e); if (throwable == null) { throwable = e; exception(request, response, e); } } // If this servlet has been marked permanently unavailable, // unload it and release this instance try { if ((servlet != null) && (wrapper.getAvailable() == Long.MAX_VALUE)) { wrapper.unload(); } } catch (Throwable e) { ExceptionUtils.handleThrowable(e); container.getLogger().error(sm.getString("standardWrapper.unloadException", wrapper.getName()), e); if (throwable == null) { throwable = e; exception(request, response, e); } } long t2=System.currentTimeMillis(); long time=t2-t1; processingTime += time; if( time > maxTime) maxTime=time; if( time < minTime) minTime=time; } }
//从StandardWrapperValve可以看出,其通过包装Request,StandardWrapper,Servlet
到ApplicationFilterChain,然后执行Filter的doFilter方法;
下面我们看一ApplicationFilterChain
final class ApplicationFilterChain implements FilterChain, CometFilterChain { // Used to enforce requirements of SRV.8.2 / SRV.14.2.5.1 //ThreadLocal,线程本地变量,隔离多线程下,每个线程ServletRequest,ServletResponse //这种思想值得借鉴 private static final ThreadLocal<ServletRequest> lastServicedRequest; private static final ThreadLocal<ServletResponse> lastServicedResponse; static { if (ApplicationDispatcher.WRAP_SAME_OBJECT) { lastServicedRequest = new ThreadLocal<ServletRequest>(); lastServicedResponse = new ThreadLocal<ServletResponse>(); } else { lastServicedRequest = null; lastServicedResponse = null; } } /** * The servlet instance to be executed by this chain. */ private Servlet servlet = null; /** * Static class array used when the SecurityManager is turned on and * <code>doFilter</code> is invoked. */ private static Class<?>[] classType = new Class[]{ServletRequest.class, ServletResponse.class, FilterChain.class}; /** * Static class array used when the SecurityManager is turned on and * <code>service</code> is invoked. */ private static Class<?>[] classTypeUsedInService = new Class[]{ ServletRequest.class, ServletResponse.class}; //过滤Servlet public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { if( Globals.IS_SECURITY_ENABLED ) { final ServletRequest req = request; final ServletResponse res = response; try { java.security.AccessController.doPrivileged( new java.security.PrivilegedExceptionAction<Void>() { @Override public Void run() throws ServletException, IOException { internalDoFilter(req,res); return null; } } ); } } //内部过滤处理 private void internalDoFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { // Call the next filter if there is one if (pos < n) { ApplicationFilterConfig filterConfig = filters[pos++]; Filter filter = null; try { filter = filterConfig.getFilter(); support.fireInstanceEvent(InstanceEvent.BEFORE_FILTER_EVENT, filter, request, response); if (request.isAsyncSupported() && "false".equalsIgnoreCase( filterConfig.getFilterDef().getAsyncSupported())) { request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE); } if( Globals.IS_SECURITY_ENABLED ) { //检查权限,获取权限 final ServletRequest req = request; final ServletResponse res = response; Principal principal = ((HttpServletRequest) req).getUserPrincipal(); Object[] args = new Object[]{req, res, this}; SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal); } else { //调用Filter的doFilter函数 filter.doFilter(request, response, this); } support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT, filter, request, response); } } // We fell off the end of the chain -- call the servlet instance try { if (ApplicationDispatcher.WRAP_SAME_OBJECT) { lastServicedRequest.set(request); lastServicedResponse.set(response); } support.fireInstanceEvent(InstanceEvent.BEFORE_SERVICE_EVENT, servlet, request, response); if (request.isAsyncSupported() && !support.getWrapper().isAsyncSupported()) { request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE); } // Use potentially wrapped request from this point if ((request instanceof HttpServletRequest) && (response instanceof HttpServletResponse)) { if( Globals.IS_SECURITY_ENABLED ) { final ServletRequest req = request; final ServletResponse res = response; Principal principal = ((HttpServletRequest) req).getUserPrincipal(); Object[] args = new Object[]{req, res}; //如果有权限限制,这检查权限,执行Service() SecurityUtil.doAsPrivilege("service", servlet, classTypeUsedInService, args, principal); } else { [color=red]//这个方法熟悉了吧,调用Servlet的service函数 servlet.service(request, response);[/color] } } else { servlet.service(request, response); } support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT, servlet, request, response); } } finally { if (ApplicationDispatcher.WRAP_SAME_OBJECT) { //清除线程的本地变量ServletRequest,ServletResponse lastServicedRequest.set(null); lastServicedResponse.set(null); } } } }
上面我们分析 StandardWraper时,他的invove()函数获取Servlet有一个方法
allocate,这个方法的实现在StandardWrape
//从StandardWraper中获取Servlet实例,下面我们分析这个函数
servlet = wrapper.allocate();
public class StandardWrapper extends ContainerBase implements ServletConfig, Wrapper, NotificationEmitter { /** * Stack containing the STM instances. */ //servlet实例栈池 protected Stack<Servlet> instancePool = null; /** * The count of allocations that are currently active (even if they * are for the same instance, as will be true on a non-STM servlet). */ protected AtomicInteger countAllocated = new AtomicInteger(0); /** * Does this servlet implement the SingleThreadModel interface? */ protected volatile boolean singleThreadModel = false; /** * Maximum number of STM instances. */ protected int maxInstances = 20; /** * Number of instances currently loaded for a STM servlet. */ protected int nInstances = 0; //如果Servlet是单例模式,则返回instance //否则创建一个新的Servlet,push到instancePool中 public Servlet allocate() throws ServletException { // If we are currently unloading this servlet, throw an exception if (unloading) { throw new ServletException(sm.getString("standardWrapper.unloading", getName())); } boolean newInstance = false; // If not SingleThreadedModel, return the same instance every time if (!singleThreadModel) { // Load and initialize our instance if necessary if (instance == null || !instanceInitialized) { synchronized (this) { if (instance == null) { try { //实例为null,则加载Servlet instance = loadServlet(); newInstance = true; if (!singleThreadModel) { // For non-STM, increment here to prevent a race // condition with unload. Bug 43683, test case // #3 //Servlet实例数据加1 countAllocated.incrementAndGet(); } } } if (!instanceInitialized) { initServlet(instance); } } } //如果是多例模式,默认为false if (singleThreadModel) { if (newInstance) { // Have to do this outside of the sync above to prevent a // possible deadlock synchronized (instancePool) { //如果是单例模式,则push到instancePool中 instancePool.push(instance); nInstances++; } } } else { //如果是单例模式,直接返回 if (log.isTraceEnabled()) { log.trace(" Returning non-STM instance"); } // For new instances, count will have been incremented at the // time of creation if (!newInstance) { countAllocated.incrementAndGet(); } return instance; } } synchronized (instancePool) { while (countAllocated.get() >= nInstances) { // Allocate a new instance if possible, or else wait if (nInstances < maxInstances) { //如果目前实例数,小于最大实例数 try { //加载一个新的Servlet,push到instancePool中 instancePool.push(loadServlet()); nInstances++; } } else { try { //否则等待 instancePool.wait(); } } } if (log.isTraceEnabled()) { log.trace(" Returning allocated STM instance"); } countAllocated.incrementAndGet(); return instancePool.pop(); } } }
//从分析StrandardWrapper可以看出,从StrandardWrapper的allocate方法,看出,当Servlet是单线程模型是则直接返回instance,否则加载一个新的Servlet,push到Servlet栈instancePool中。
总结:
从上面可以分析得出每个Host关联一个StardardContext,StardardContext是根据Web.xml的配置,初始化欢迎界面,角色权限,初始参数,错误处理,Listener,Filter和Sevlet等。
在加载Servlet的过程中,通过反射,调用Servlet的init方法。每个StandardContext有多个StandardWrappe子容器,StandardWrappe关联一个StandardPileLine,StandardPileLine中装配这StandardWrappeValve,StandardWrappeValve处理HTTP请求,通过invoke(),在invoke中,同StandardWrappe的allocate方法获取Servlet实例instance,然后将Request,StandardWrapper,Servlet包装到ApplicationFilterChain,然后执行Filter的doFilter方法;在Filter的doFilter中,限制性filter操作,然后通过反射调用servlet的service方法。下篇文章将介绍ContextConfig
附:
//tld配置
public final class TldConfig implements LifecycleListener { private static final String TLD_EXT = ".tld"; private static final String WEB_INF = "/WEB-INF/"; private static final String WEB_INF_LIB = "/WEB-INF/lib/"; //监听事件 public void lifecycleEvent(LifecycleEvent event) { // Identify the context we are associated with try { context = (Context) event.getLifecycle(); } if (event.getType().equals(Lifecycle.AFTER_INIT_EVENT)) { init(); } else if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) { try { execute(); } } //扫描所有的tld,lib,初始化 public void execute() { long t1=System.currentTimeMillis(); /* * Priority order of URIs required by spec is: * 1. J2EE platform taglibs - Tomcat doesn't provide these * 2. web.xml entries * 3. JARS in WEB-INF/lib & TLDs under WEB-INF (equal priority) * 4. Additional entries from the container * * Keep processing order in sync with o.a.j.compiler.TldLocationsCache */ // Stage 2 - web.xml entries tldScanWebXml(); // Stage 3a - TLDs under WEB-INF (not lib or classes) tldScanResourcePaths(WEB_INF); // Stages 3b & 4 JarScanner jarScanner = context.getJarScanner(); TldJarScannerCallback tldCallBack = new TldJarScannerCallback(); jarScanner.scan(context.getServletContext(), context.getLoader().getClassLoader(), tldCallBack, noTldJars); // Now add all the listeners we found to the listeners for this context String list[] = getTldListeners(); for( int i=0; i<list.length; i++ ) { context.addApplicationListener( new ApplicationListener(list[i], true)); } long t2=System.currentTimeMillis(); if( context instanceof StandardContext ) { ((StandardContext)context).setTldScanTime(t2-t1); } } }
//StandardContextValve
final class StandardContextValve extends ValveBase { /** * Select the appropriate child Wrapper to process this request, * based on the specified request URI. If no matching Wrapper can * be found, return an appropriate HTTP error. */ @Override public final void invoke(Request request, Response response) throws IOException, ServletException { // Disallow any direct access to resources under WEB-INF or META-INF MessageBytes requestPathMB = request.getRequestPathMB(); if ((requestPathMB.startsWithIgnoreCase("/META-INF/", 0)) || (requestPathMB.equalsIgnoreCase("/META-INF")) || (requestPathMB.startsWithIgnoreCase("/WEB-INF/", 0)) || (requestPathMB.equalsIgnoreCase("/WEB-INF"))) { response.sendError(HttpServletResponse.SC_NOT_FOUND); return; } // Select the Wrapper to be used for this Request Wrapper wrapper = request.getWrapper(); if (wrapper == null || wrapper.isUnavailable()) { response.sendError(HttpServletResponse.SC_NOT_FOUND); return; } // Acknowledge the request try { response.sendAcknowledgement(); } if (request.isAsyncSupported()) { request.setAsyncSupported(wrapper.getPipeline().isAsyncSupported()); } wrapper.getPipeline().getFirst().invoke(request, response); } }
发表评论
-
tomcat日志及log4j日志乱码
2017-04-26 15:00 5498Linux系统字符编码:http://www.cnblogs. ... -
SQL盲注的解决方案
2016-10-27 18:35 2570引起SQL盲注的原因:http://www-01.ibm.co ... -
Tomcat的HTTPS配置详解
2016-10-27 16:59 972HTTPS原理详解:http://blog.csdn.net/ ... -
Tomcat的StandardService,Executor初始化与启动
2016-10-13 09:15 1610Tomcat的Server初始化及启动过程:http://do ... -
Tomcat的JioEndPoint,处理HTTP请求
2016-10-12 19:02 1251Tomcat的Connector(Protocol,Coyot ... -
Tomcat的Connector(Protocol,CoyoteAdapterAdapter,AprEndPoint)初始化及请求处理过程
2016-10-12 16:46 1619Tomcat的Server初始化及启动过程:http://do ... -
Tomcat问题集
2016-10-09 17:50 809tcnative-1.dll: Can't load AMD ... -
Tomcat-ConfigContext监听器(Context.xml,web.xml)
2016-09-27 12:59 1983Tomcat的Host初始化(Context,Listener ... -
Tomcat的Engine初始化,启动过程
2016-09-26 15:33 2301Tomcat的Server初始化及启动过程:http://do ... -
Tomcat的Server初始化及启动过程
2016-09-26 11:02 2938Tomcat7,启动过程(BootStrap、Catalina ... -
Tomcat7,启动过程(BootStrap、Catalina)
2016-09-23 19:08 1861Tomcat 系统架构与设计模式,工作原理:http://ww ... -
405 request method post not supported
2016-08-18 18:09 32291.检查链接地址有没有错误 2.看RequestMethod, ... -
apache-tomcat-7.0.67\bin\tcnative-1.dll: Can't load AMD 64-bit .dll on a IA 3
2016-08-12 13:02 1125F:\apache-tomcat-7.0.67\bin\tcn ...
相关推荐
5. 初始化Context:每个Context对应一个Web应用。这个阶段会解析Web应用的配置,如`WEB-INF/web.xml`,加载Servlet、Filter、Listener等。 6. 初始化Servlet、Filter和Listener:这些组件是Web应用的核心部分,它们...
3. 初始化Servlet:`Context`调用Servlet的`init()`方法,完成Servlet实例的初始化。 4. 处理请求:接收到请求后,通过`Connector`转发到对应的`Executor`,然后由`Pipeline`和`Valve`链处理请求,最终调用Servlet的...
3. 容器级配置:容器可以根据需要提供全局的配置,比如全局的Filter或Listener,这些配置对整个Web应用或特定的Host、Context有效。 五、容器与源码分析 了解了容器的基本概念后,对于开发者来说,阅读Tomcat的源码...
`Servlet.xml`是每个Web应用中的核心配置文件,它定义了Servlet的实例化、映射、初始化参数等。例如: ```xml <servlet> <servlet-name>MyServlet</servlet-name> <servlet-class>com.example.MyServlet</...
web.xml是Web应用的部署描述文件,用于配置Servlet、Filter、Listener和Web应用的初始化参数等。 ### Tomcat优化 Tomcat的性能优化可以从多个维度进行,包括但不限于: 1. **JVM参数优化**:合理设置内存大小和...
- **启动流程**:通过bin目录下的startup.sh或startup.bat启动,加载Catalina,解析conf/server.xml配置,初始化各个组件。 - **请求处理**:Coyote接收HTTP请求,通过ProtocolHandler传递给Catalina,Catalina...
- `<context-param>`: 初始化参数。 - `<filter>`: 配置过滤器。 - `<listener>`: 监听器配置。 - `<servlet>`: Servlet配置。 - `<welcome-file-list>`: 欢迎页面列表。 #### 四、Tomcat的工作模式 - **独立...
2. **Servlet生命周期**: 在Tomcat中,Servlet经历初始化、服务、销毁三个阶段。开发者可以通过重写`init()`, `service()`, `destroy()`方法自定义行为。 3. **Context、Host和Engine**: 这是Tomcat容器的三个层次...
Tomcat5采用容器-组件模型,容器管理一组组件,如Servlet、Filter和Listener。这种设计使得扩展和配置变得更加简单。 8. **安全** Tomcat5支持多种安全机制,如JAAS(Java Authentication and Authorization ...
StandardWrapper是Tomcat中处理Servlet实例的核心类,它负责Servlet的实例化、初始化、服务和销毁。这个章节将详细阐述StandardWrapper如何与Servlet生命周期结合,以及如何管理Servlet的多个实例。 第12章:...
- 可以定义Servlet、过滤器(Filter)、监听器(Listener)等组件。 - 示例配置: ```xml <servlet> <servlet-name>exampleServlet</servlet-name> <servlet-class>com.example.ExampleServlet</servlet-class> ...
监听器则可以监听特定事件,如ServletContext、Session和HttpSession等事件,实现自动初始化、日志记录等功能。 最后,性能优化包括配置Tomcat的线程池、连接器设置、内存分配等,以确保服务器能应对高并发场景。...
例如,`ContextListener`可以用来初始化和清理应用上下文,`JMXRemoteLifecycleListener`用于启用JMX远程访问。在`server.xml`中的`<Server>`或`<Host>`元素下添加相应的`<Listener>`标签即可配置监听器。 ### 三、...
- `init()`: 初始化Servlet,仅在加载时调用一次。 - `destroy()`: 释放资源,当Servlet不再被使用时调用。 - `doGet()` 和 `doPost()`: 分别处理HTTP GET和POST请求。 通过以上分析,我们可以看到JSP和Tomcat...
- **作用**:用于声明全局的初始化参数,这些参数可以在整个Web应用中访问。 - **示例**: ```xml <context-param> <param-name>encoding <param-value>UTF-8 </context-param> ``` #### <filter> - **作用*...
- **初始化阶段**:当服务器启动时,会加载并实例化Servlet。 - **请求处理阶段**:每当客户端发出请求,服务器就会调用Servlet的`service()`方法进行处理。 - **销毁阶段**:当服务器关闭时,会调用`destroy()`...