赞
踩
我们回到Tomcat启动过程探究 第一部分的结尾,回到StandardService的初始化方法initInternal()。
StandardService的重点方法有四个:
- <Connector port="8080" protocol="HTTP/1.1"
- connectionTimeout="20000"
- redirectPort="8443" /><!HTTP 1.1协议>
- <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /><!AJP 1.3协议>
在StandardServer.init()方法中,可以看到Server和Service是一对多的关系。为此,干脆先整理各层级容器直接的关系图:
接下来,我们关注Engine(StandardEngine)的初始化时会做些什么。
首先在StandardEngine构造方法里,我发现了有意思的东西:
- /**
- * Create a new StandardEngine component with the default basic Valve.
- */
- public StandardEngine() {
-
- super();
- pipeline.setBasic(new StandardEngineValve());
- /* Set the jmvRoute using the system property jvmRoute */
- try {
- setJvmRoute(System.getProperty("jvmRoute"));
- } catch(Exception ex) {
- log.warn(sm.getString("standardEngine.jvmRouteFail"));
- }
- // By default, the engine will hold the reloading thread
- backgroundProcessorDelay = 10;
-
- }
注意这一句:
pipeline.setBasic(new StandardEngineValve());
查询资料后,知道:
Pipeline(管道)采用了责任链的方式处理客户端的请求,每一个Valve(阀)表示责任链上的每一个处理器。
StandardPipeline重写了startInternal()方法,Pieline和Valve也将在start()方法中详细讨论。
至此,从Bootstrap.load()开始的长途跋涉就告一段落了。不过后面是流程更复杂的Bootstrap.start()。
经过了load的查看源码的经历,很容易就找到了start的关键路径为:Bootstrap.start()->Catalina.start()->StandardServer.startInternal()
- /**
- * Start nested components ({@link Service}s) and implement the requirements
- * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
- *
- * @exception LifecycleException if this component detects a fatal error
- * that prevents this component from being used
- */
- @Override
- protected void startInternal() throws LifecycleException {
-
- fireLifecycleEvent(CONFIGURE_START_EVENT, null);//使注册的监听器响应事件
- setState(LifecycleState.STARTING);//设置生命周期状态,使注册的监听器响应事件
-
- globalNamingResources.start();
-
- // Start our defined Services
- synchronized (servicesLock) {
- for (int i = 0; i < services.length; i++) {
- services[i].start();
- }
- }
- }
StandardServer此刻注册的监听器列表如下:
遍历启动StandardService.startInternal():
- /**
- * Start nested components ({@link Executor}s, {@link Connector}s and
- * {@link Container}s) and implement the requirements of
- * {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
- *
- * @exception LifecycleException if this component detects a fatal error
- * that prevents this component from being used
- */
- @Override
- protected void startInternal() throws LifecycleException {
-
- if(log.isInfoEnabled())
- log.info(sm.getString("standardService.start.name", this.name));
- setState(LifecycleState.STARTING);
-
- // Start our defined Container first
- if (engine != null) {
- synchronized (engine) {
- engine.start();
- }
- }
-
- synchronized (executors) {
- for (Executor executor: executors) {
- executor.start();
- }
- }
-
- mapperListener.start();
-
- // Start our defined Connectors second
- synchronized (connectorsLock) {
- for (Connector connector: connectors) {
- try {
- // If it has already failed, don't try and start it
- if (connector.getState() != LifecycleState.FAILED) {
- connector.start();
- }
- } catch (Exception e) {
- log.error(sm.getString(
- "standardService.connector.startFailed",
- connector), e);
- }
- }
- }
- }
此刻StandardService的监听器列表为空。
在Catalina.createStartDigester()中,给StandardEngine通过addChild添加了一个child,即StandardHost。
在容器抽象类ContainerBase中startInternal()方法有这样一段:
- // Start our child containers, if any
- Container children[] = findChildren();
- List<Future<Void>> results = new ArrayList<>();
- for (int i = 0; i < children.length; i++) {
- results.add(startStopExecutor.submit(new StartChild(children[i])));
- }
它代表了当一个父容器start的时候,子容器会开启新线程。并执行子容器的start()方法。
接下来我们关注StandardHost的startInternal()方法:
- /**
- * Start this component and implement the requirements
- * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
- *
- * @exception LifecycleException if this component detects a fatal error
- * that prevents this component from being used
- */
- @Override
- protected synchronized void startInternal() throws LifecycleException {
-
- // Set error report valve
- 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).getConstructor().newInstance();
- getPipeline().addValve(valve);
- }
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- log.error(sm.getString(
- "standardHost.invalidErrorReportValveClass",
- errorValve), t);
- }
- }
- super.startInternal();
- }
这个方法只设置了一个关闭阀门,并没有做太多的事情。
但此刻StandardHost的监听器列表:
我们知道接下来HostConfig会接收到Lifecycle.START_EVENT的消息。
我们看下HostConfig在接收到消息后会做什么:
- /**
- * Process the START event for an associated Host.
- *
- * @param event The lifecycle event that has occurred
- */
- @Override
- public void lifecycleEvent(LifecycleEvent event) {
-
- // Identify the host we are associated with
- try {
- host = (Host) event.getLifecycle();
- if (host instanceof StandardHost) {
- setCopyXML(((StandardHost) host).isCopyXML());
- setDeployXML(((StandardHost) host).isDeployXML());
- setUnpackWARs(((StandardHost) host).isUnpackWARs());
- setContextClass(((StandardHost) host).getContextClass());
- }
- } catch (ClassCastException e) {
- log.error(sm.getString("hostConfig.cce", event.getLifecycle()), e);
- return;
- }
-
- // Process the event that has occurred
- if (event.getType().equals(Lifecycle.PERIODIC_EVENT)) {
- check();
- } else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
- beforeStart();
- } else if (event.getType().equals(Lifecycle.START_EVENT)) {
- start();
- } else if (event.getType().equals(Lifecycle.STOP_EVENT)) {
- stop();
- }
- }
首先是标识符的设置,这边只说比较重要的标识符:
之后就是HostConfig.start()方法了:
- /**
- * Process a "start" event for this Host.
- */
- public void start() {
-
- if (log.isDebugEnabled())
- log.debug(sm.getString("hostConfig.start"));
-
- try {
- ObjectName hostON = host.getObjectName();
- oname = new ObjectName
- (hostON.getDomain() + ":type=Deployer,host=" + host.getName());
- Registry.getRegistry(null, null).registerComponent
- (this, oname, this.getClass().getName());
- } catch (Exception e) {
- log.warn(sm.getString("hostConfig.jmx.register", oname), e);
- }
-
- if (!host.getAppBaseFile().isDirectory()) {
- log.error(sm.getString("hostConfig.appBase", host.getName(),
- host.getAppBaseFile().getPath()));
- host.setDeployOnStartup(false);
- host.setAutoDeploy(false);
- }
-
- if (host.getDeployOnStartup())
- deployApps();
-
- }
这里比较有意思的一段是:
- Registry.getRegistry(null, null).registerComponent
- (this, oname, this.getClass().getName());
将当前监听器注册到MBean,由其管理。关于MBean,后面会另做一章介绍,这里只要知道是注册上去就可以了。
HostConfig.delpoyApps()方法:
- /**
- * Deploy applications for any directories or WAR files that are found
- * in our "application root" directory.
- */
- protected void deployApps() {
-
- File appBase = host.getAppBaseFile();
- File configBase = host.getConfigBaseFile();
- 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);
-
- }
这三个方法,其实是在不同的地方做同样一件事情,以deployDirectories为例:
- Class<?> clazz = Class.forName(host.getConfigClass());
- LifecycleListener listener = (LifecycleListener) clazz.getConstructor().newInstance();
这里的关系实际上已在Catalina.createStartDigester()中配置。
host.addChild(context);
接下来执行的是StandardContext.startInternal(),这个方法非常的冗长,这里只选取重要的部分:
- if (getLoader() == null) {
- WebappLoader webappLoader = new WebappLoader(getParentClassLoader());
- webappLoader.setDelegate(getDelegate());
- setLoader(webappLoader);
- }
- Loader loader = getLoader();
- if (loader instanceof Lifecycle) {
- ((Lifecycle) loader).start();
- }
上两段为创建和启动Web应用类加载器WebappClassLoader。
- // Notify our interested LifecycleListeners
- fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);
CONFIGURE_START_EVENT事件的,通知。顺便整理发送该通知时,StandardContext的监听列表:
ContextConfig接收到CONFIGURE_START_EVENT后,会执行configureStart()->webConfig()。
webConfig()是真正解析web.xml的方法,是和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.
- */
- WebXmlParser webXmlParser = new WebXmlParser(context.getXmlNamespaceAware(),
- context.getXmlValidation(), context.getXmlBlockExternal());
-
- Set<WebXml> defaults = new HashSet<>();
- defaults.add(getDefaultWebXmlFragment(webXmlParser));
-
- WebXml webXml = createWebXml();
-
- // Parse context level web.xml
- InputSource contextWebXml = getContextWebXmlSource();//解析容器基本的web.xml
- if (!webXmlParser.parseWebXml(contextWebXml, webXml, false)) {
- ok = false;
- }
-
- ServletContext sContext = context.getServletContext();
-
- // Ordering is important here
-
- // Step 1. Identify all the JARs packaged with the application and those
- // provided by the container. If any of the application JARs have a
- // web-fragment.xml it will be parsed at this point. web-fragment.xml
- // files are ignored for container provided JARs.
- //扫描/WEB-INF/lib下的所有JAR包,如果包含/META-INF/web-fragment.xml这类片段webXml
- //则创建片段webXml
- Map<String,WebXml> fragments = processJarsForWebFragments(webXml, webXmlParser);
-
- // Step 2. Order the fragments.
- //对片段webXml进行排序,排序结果影响了Filter的执行顺序
- Set<WebXml> orderedFragments = null;
- orderedFragments =
- WebXml.orderWebFragments(webXml, fragments, sContext);
-
- // Step 3. Look for ServletContainerInitializer implementations
- //查找并创建ServletContainerInitializer的实现类
- if (ok) {
- processServletContainerInitializers();
- }
-
- if (!webXml.isMetadataComplete() || typeInitializerMap.size() > 0) {
- // Steps 4 & 5.
- //注解配置解析,将解析结果合并到web.xml
- processClasses(webXml, orderedFragments);
- }
-
- if (!webXml.isMetadataComplete()) {
- // Step 6. Merge web-fragment.xml files into the main web.xml
- // file.
- //合并片段webXml按照顺序到web.xml
- if (ok) {
- ok = webXml.merge(orderedFragments);
- }
-
- // Step 7. Apply global defaults
- // Have to merge defaults before JSP conversion since defaults
- // provide JSP servlet definition.
- //配置JSP Servlet
- webXml.merge(defaults);
-
- // Step 8. Convert explicitly mentioned jsps to servlets
- if (ok) {
- convertJsps(webXml);
- }
-
- // Step 9. Apply merged web.xml to Context
- //使用合并后的web.xml配置当前StandardContext,包括Servlet、Filter、Listener
- //并针对Servlet创建StandardWrapper,添加到StandardContext
- if (ok) {
- configureContext(webXml);
- }
- } else {
- webXml.merge(defaults);
- convertJsps(webXml);
- configureContext(webXml);
- }
-
- if (context.getLogEffectiveWebXml()) {
- log.info("web.xml:\n" + webXml.toXml());
- }
-
- // Always need to look for static resources
- // Step 10. Look for static resources packaged in JARs
- //添加META-INF/resources/下的静态资源到standardContext
- if (ok) {
- // Spec does not define an order.
- // Use ordered JARs followed by remaining JARs
- Set<WebXml> resourceJars = new LinkedHashSet<>();
- for (WebXml fragment : orderedFragments) {
- resourceJars.add(fragment);
- }
- for (WebXml fragment : fragments.values()) {
- if (!resourceJars.contains(fragment)) {
- resourceJars.add(fragment);
- }
- }
- processResourceJARs(resourceJars);
- // See also StandardContext.resourcesStart() for
- // WEB-INF/classes/META-INF/resources configuration
- }
-
- // Step 11. Apply the ServletContainerInitializer config to the
- // context
- if (ok) {
- for (Map.Entry<ServletContainerInitializer,
- Set<Class<?>>> entry :
- initializerClassMap.entrySet()) {
- if (entry.getValue().isEmpty()) {
- context.addServletContainerInitializer(
- entry.getKey(), null);
- } else {
- context.addServletContainerInitializer(
- entry.getKey(), entry.getValue());
- }
- }
- }
- }
至此,终于真正的完成了web.xml的合并、解析,并应用于StandardContext。
我们回到StandardContext.startInternal(),看看接下来还做了什么:
- // Start our child containers, if not already started
- for (Container child : findChildren()) {
- if (!child.getState().isAvailable()) {
- child.start();
- }
- }
启动StandardWrapper后,StandarWrapper.startInternal()只有一个作用,即由MBean发送Notification广播。
随后沿着addChild这条线,会继续调用StandarWrapper.load()->loadServlet()方法,开始初始化和创建Servlet实例。
至此, Web应用的加载过程就完毕了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。