Tomcat源码分析 v0.1

第一节 参考
一.参考资料
<深入分析Tomcat>
如果碰到check-sum校验不过的,直接xxx.checksum.enabled=false.
apache.org的地址改为http
https://blog.csdn.net/google2606/article/details/52529829

二.看源码前疑问
1.静态资源是在servlet容器中处理,还是具体的servlet框架中处理?
2.从tomcat接受socket请求,到servlet框架中,比如springmvc,struts,webx等的核心处理类中,
具体经过了哪些流程链处理?怎么区分不同的网站,url,servlet?
3.tomcat的常见报错,比如broken pipeline经历了什么样的错误或者异常处理过程?
4.不同的网站怎么做类加载,区分包路径相同得类?

第二节 架构
一.概述
tomcat核心由两大部分组成,connector和container.connector处理与客户端的socket通信,主要是多线程监听,建立,接受,处理socket连接。支持http1.1(bio和nio),apr,memory,ajp协议.container解析处理具体的http请求报文,由外到内主要包括Server服务器,Service,Engine引擎(Connector和Engine并列统计别,不同的协议对应不同的Connector),Host主机,Context上下文,Wrapper四部分.Host主要用来区分同一个server上的多个虚拟主机。Context用来区分同一主机上的多个网站。wrapper用来区分同一网站的不同的Servlet请求.这几个域模型在tomcat源码中都有对应的interface,和StandardXXX的默认实现.域模型的生命周期通过集成LifecycleBase,或者实现Lefecycle接口实现.

第三节 源码细节

一.tomcat启动
入口地址Bootstrap.main().
(一).load()加载
1.调用init().先调用Bootstrap.initClassLoaders()加载classLoader.classLoader加载三种,commonLoader,catalinaLoader,sharedloader.三种classLoader加载的类路径在static代码块内,通过CatalinaProperties.loadProperties(),读取catalina.properties文件得到.common loader主要读取catalina home下lib文件夹的jar包.server和shared默认为空.设置当前主线程的cloass loader为catalinaLoader.加载床架org.apache.catalina.startup.Catalina类的对象,调用它的setParentClassLoader()方法设置Catalina的cloassLoader为java.lang.ClassLoader.
2.解析命令行命令,如果start,调用load().进入org.apache.catalina.startup.Catalina.load(),在这个方法里面完成tomcat初始化.代码如下:

public void load() {

    if (loaded) {
        return;
    }
    loaded = true;

    long t1 = System.nanoTime();
    //初始化java的tmp目录
    initDirs();

    // Before digester - it may be needed
    initNaming();

    // Set configuration source
	//读取conf/server.xml文件路径到ConfigFileLoader中.
    ConfigFileLoader.setSource(new CatalinaBaseConfigurationSource(Bootstrap.getCatalinaBaseFile(), getConfigFile()));
    //读取conf/server.xml到file中
    File file = configFile();

    // Create and execute our Digester
    //创建Digester,用来解析xml文件的接口,用来解析server.xml文件.
    //这里面定义了解析server.xml时,碰到server就创建
    Digester digester = createStartDigester();

    //通过Digester解析server.xml文件.解析完后所有的对象实例挂到StandardServer的对象中引用
    try (ConfigurationSource.Resource resource = ConfigFileLoader.getSource().getServerXml()) {
        InputStream inputStream = resource.getInputStream();
        InputSource inputSource = new InputSource(resource.getURI().toURL().toString());
        inputSource.setByteStream(inputStream);
        digester.push(this);
        digester.parse(inputSource);
    } catch (Exception e) {
        log.warn(sm.getString("catalina.configFail", file.getAbsolutePath()), e);
        if (file.exists() && !file.canRead()) {
            log.warn(sm.getString("catalina.incorrectPermissions"));
        }
        return;
    }

	//设置Server的Catalina属性,即tomcat目录
    getServer().setCatalina(this);
    getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
    getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());

    // Stream 
    //重定向System.out到log文件
    initStreams();

    // Start the new server
    try {
    	//生命周期的模板模式,进入StandardServer.initInternal()
        getServer().init();
    }
    ...
}

3.解析server.xml创建对象,类似spring的ioc,根据xml配置创建对象.

protected Digester createStartDigester() {
    ...

    // Configure the actions we will be using
    //碰到server标签时,创建StandardServer类的对象
    digester.addObjectCreate("Server",
                             "org.apache.catalina.core.StandardServer",
                             "className");
    //碰到server节点的子节点时,调用server的setter方法设置进去
    digester.addSetProperties("Server");
    //当再次碰到server节点时,调用父节点的setServer方法.
    digester.addSetNext("Server",
                        "setServer",
                        "org.apache.catalina.Server");

    //创建NamingResourcesImpl子节点对象
    digester.addObjectCreate("Server/GlobalNamingResources",
                             "org.apache.catalina.deploy.NamingResourcesImpl");
    digester.addSetProperties("Server/GlobalNamingResources");
    digester.addSetNext("Server/GlobalNamingResources",
                        "setGlobalNamingResources",
                        "org.apache.catalina.deploy.NamingResourcesImpl");

    //创建Listener对象
    digester.addRule("Server/Listener",
            new ListenerCreateRule(null, "className"));
    digester.addSetProperties("Server/Listener");
    digester.addSetNext("Server/Listener",
                        "addLifecycleListener",
                        "org.apache.catalina.LifecycleListener");

    //对Server/Service节点创建StandardService对象
    digester.addObjectCreate("Server/Service",
                             "org.apache.catalina.core.StandardService",
                             "className");
    digester.addSetProperties("Server/Service");
    //再次碰到Service时,调用Server.addService方法
    digester.addSetNext("Server/Service",
                        "addService",
                        "org.apache.catalina.Service");

    digester.addObjectCreate("Server/Service/Listener",
                             null, // MUST be specified in the element
                             "className");
    digester.addSetProperties("Server/Service/Listener");
    digester.addSetNext("Server/Service/Listener",
                        "addLifecycleListener",
                        "org.apache.catalina.LifecycleListener");
	...

    // Add RuleSets for nested elements
    digester.addRuleSet(new NamingRuleSet("Server/GlobalNamingResources/"));
    //对Service下的Engine子节点调用EngineRuleSet.addRuleInstances()方法处理.
	digester.addRuleSet(new EngineRuleSet("Server/Service/"));
    digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
    digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));
    addClusterRuleSet(digester, "Server/Service/Engine/Host/Cluster/");
    digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Context/"));

    // When the 'engine' is found, set the parentClassLoader.
    digester.addRule("Server/Service/Engine",
                     new SetParentClassLoaderRule(parentClassLoader));
    addClusterRuleSet(digester, "Server/Service/Engine/Cluster/");

 	 ...
    return digester;
}


初始化java的tmp目录,创建Digester,加载解析tomcat的核心配置文件conf/server.xml.比如解析到一个Server标签,进入org.apache.catalina.core.StandardServer.addService(Service)方法添加Server.tomcat启动时,每一级模型会调用下一级别的addxxx方法实现,比如Server.addService(Service)方法.但是Service.addConnector(Connector)特别,因为所有的Connector共享线程连接池,不同的协议对应不同的Connector,比如http和https.
4.在Catalina.load()方法中继续进入StandardServer.initInternal()方法注册MBeanFactory工厂。依次加载tomcat的lib目录下的lib文件.
在StandardServer.initInternal()方法的最后,遍历所有的Service子节点,进入StandardService.initInternal()方法.这个方法代码如下:

protected void initInternal() throws LifecycleException {
        super.initInternal();
        //对service下的engine子节点调用初始化方法.这里可以看出只能有一个engine子节点.
        if (engine != null) {
            engine.init();
        }

        // Initialize any Executors
        //对service节点下的Executor线程池子节点调用初始化
        for (Executor executor : findExecutors()) {
            if (executor instanceof JmxEnabled) {
                ((JmxEnabled) executor).setDomain(getDomain());
            }
            executor.init();
        }

        // Initialize mapper listener
        //mapper存储了url和处理host的映射关系
        mapperListener.init();

        // Initialize our defined Connectors
        //调用所有的connector子节点初始化,比如http,ajp两种协议的Connector
        //不同的connector共享前面的线程池
        synchronized (connectorsLock) {
            for (Connector connector : connectors) {
                connector.init();
            }
        }
    }

进入StandardEngine.initInternal()方法.在StandardService.initInternal()方法中继续调用不同协议的Connector.init()方法.比如调用Http11NioProtocol.init()处理http协议.所有协议都继承自AbstractProtocol,实现ProtocolHandler接口.协议的源和目标地址继承自AbstractJsseEndpoint,通过该方法的bind()方法实现地址绑定。不同的IO模型,都实现bind()方法,比如NIO模型的org.apache.tomcat.util.net.NioEndpoint.bind()方法,实现NIO通信.在NioEndpoint.bind()方法中,会创建服务端的ServerSocketChannel套接字,绑定服务端口号.在StandService.initInternal()中,还会初始化AJP/1.3-8009连接器.

protected void initInternal() throws LifecycleException {
    super.initInternal();

    //connector对应不同的http,ajp协议.
    if (protocolHandler == null) {
        throw new LifecycleException(
                sm.getString("coyoteConnector.protocolHandlerInstantiationFailed"));
    }

    // Initialize adapter
    //CoyoteAdapter实现协议到容器Container之间的桥接,创建request,response请求
    adapter = new CoyoteAdapter(this);
    protocolHandler.setAdapter(adapter);
    if (service != null) {
        protocolHandler.setUtilityExecutor(service.getServer().getUtilityExecutor());
    }

    // Make sure parseBodyMethodsSet has a default
    //默认post方法解析body
    if (null == parseBodyMethodsSet) {
        setParseBodyMethods(getParseBodyMethods());
    }

    ...
    try {
        //调用协议的初始化方法.进入AbstractProtocol.init()
        protocolHandler.init();
    } catch (Exception e) {
        throw new LifecycleException(
                sm.getString("coyoteConnector.protocolHandlerInitializationFailed"), e);
    }
}

AbstractProtocol.init()调用AbstractEndpoint#.init()->NioEndpoint.initServerSocket()调用bind()方法绑定默认的8080端口.默认用NIO非阻塞NioEndPoint.如下图:


5.在bind()方法里面调用NioSelectorPool.open().在这里面通过BlockPoller类启动poller机制.
(二).start()启动
1.进入Bootstrap.start()方法.进入StandardServer.startInternal().按照和init()类似的机制,进入子节点,所有service的start()方法.依次进入StandardService.startInternal()->StandardEngine.startInternal()->ContainerBase.startInternal()方法.
2.在ContainerBase.startInternal中,StandardHost也是继承自ContainerBase类.每个Container封装到一个StartChild,它继承Callable,实现start方法,通过新线程调用内部Container的start方法.新线程进入ContainerBase.StartChild.call().进入StandardHost.startInternal().在这个方法里面遍历Host标签内部所有的Valve阀门标签,使用分散式职责链模式,每个Valve保存下一个Valve的引用.Valves都存储在Pipeline管道内部。目前tomcat内部的valve有AccessLogValve,ErrorReportValve,JDBCAccessLogValve,RequestFilterValve,SSLValve等等.
3.在StandardHost.startInternal中继续调用StandardPipeline.start,进入StandardPipeline.startInternal().在这个方法内部,会依次遍历所有的Valve,调用Valve的start方法.依次调用了AccessLogValve,ErrorReportValve,StandardHostValve。先调用了AccessLogValve.startInternal()初始化日志格式.StandardHostValve继承自ValveBase.
(三).HostConfig监听start事件
HostConfig继承自LifecycleListener,在处理start事件时,部署各种app网站.代码如下:

protected void deployApps() {
	//指向webapps目录
    File appBase = host.getAppBaseFile();
    //指向conf/Catalina/localhost目录
    File configBase = host.getConfigBaseFile();
    //指向webapps下的所有子目录
    String[] filteredAppPaths = filterAppPaths(appBase.list());
    // Deploy XML descriptors from 
    //部署xml文件描述的网站
    deployDescriptors(configBase, configBase.list());
    // Deploy WARs
    //部署war包网站
    deployWARs(appBase, filteredAppPaths);
    // Deploy expanded folders
    //部署webapps下的所有目录,对每个目录创建线程调用HostConfig.deployDirectory
    deployDirectories(appBase, filteredAppPaths);
}

1.线程进入HostConfig.deployDirectory()中先查找,使用digester库解析网站下的context.xml文件.解析到StandardContext对象里面.一个Context对象代表一个网站.继续调用StandardHost.addChild(),把网站加入到StandardHost对象中,形成级联关系. 在addChildInternal()中.进入StandardContext.start().开始对每个网站的start启动过程.
2.StandardContext.startInternal()方法逻辑比较多,完成网站的启动操作.代码如下:

protected synchronized void startInternal() throws LifecycleException {
        ...

        setConfigured(false);
        boolean ok = true;

        // Currently this is effectively a NO-OP but needs to be called to
        // ensure the NamingResources follows the correct lifecycle
        if (namingResources != null) {
            namingResources.start();
        }

        // Post work directory
        //设置catalinaHome,ServletContext等属性.
        postWorkDirectory();

        // Add missing components as necessary
        //解析resources目录到StandardRoot对象里面.
        if (getResources() == null) {   // (1) Required by Loader
            if (log.isDebugEnabled())
                log.debug("Configuring default Resources");

            try {
                setResources(new StandardRoot(this));
            } catch (IllegalArgumentException e) {
                log.error(sm.getString("standardContext.resourcesInit"), e);
                ok = false;
            }
        }
        if (ok) {
        	//调用资源文件StandardRoot的init,start启动方法
        	//遍历/WEB-INF/lib目录,搜索class类文件
            resourcesStart();
        }

        if (getLoader() == null) {
            //加载搜索到的class类
            WebappLoader webappLoader = new WebappLoader(getParentClassLoader());
            webappLoader.setDelegate(getDelegate());
            setLoader(webappLoader);
        }

        // An explicit cookie processor hasn't been specified; use the default
        if (cookieProcessor == null) {
            cookieProcessor = new Rfc6265CookieProcessor();
        }

        // Initialize character set mapper
        getCharsetMapper();
        ...


        // Binding thread
        ClassLoader oldCCL = bindThread();

        try {
            if (ok) {
                // Start our subordinate components, if any
                Loader loader = getLoader();
                if (loader instanceof Lifecycle) {
                	//这里进入WebappLoader.startInternal()->WebappClassLoaderBase.start()
                	//类加载器加载/WEB-INF/classes目录,/WEB-INF/lib
                    ((Lifecycle) loader).start();
                }

				...

				//这里触发Context网站的配置,进入ContextConfig.configureContext()
				//读取web.xml,创建网站的Servlet
				fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);

                // Start our child containers, if not already started
                for (Container child : findChildren()) {
                    if (!child.getState().isAvailable()) {
                      //进入StandardWrapper#startInternal,调用各种Servlet的start方法.
                        child.start();
                    }
                }

                // Start the Valves in our pipeline (including the basic),
                // if any
                if (pipeline instanceof Lifecycle) {
                    ((Lifecycle) pipeline).start();
                }
            	...
                (Lifecycle) manager).start();
                }
                ...
                //启动所有的servlet
                if (ok) {
                if (!loadOnStartup(findChildren())){
                    log.error(sm.getString("standardContext.servletFail"));
                    ok = false;
                }
            }
            	...
}

 3.网站配置初始化ContextConfig.configureContext():

private void configureContext(WebXml webxml) {
    ...
    for (Entry<String, String> entry :
            webxml.getMimeMappings().entrySet()) {
        //添加excel,xml等Mime类型的处理mapping 
        context.addMimeMapping(entry.getKey(), entry.getValue());
    }
   	...
   	//添加web.xml中配置的各种servlet.比如StandardWrapper,JspServlet,
    for (ServletDef servlet : webxml.getServlets().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

        //设置servlet的启动级别
        if (servlet.getLoadOnStartup() != null) {
            wrapper.setLoadOnStartup(servlet.getLoadOnStartup().intValue());
        }
        if (servlet.getEnabled() != null) {
            wrapper.setEnabled(servlet.getEnabled().booleanValue());
        }
        //设置servlet的name
        wrapper.setName(servlet.getServletName());
        Map<String,String> params = servlet.getParameterMap();
        for (Entry<String, String> entry : params.entrySet()) {
            wrapper.addInitParameter(entry.getKey(), entry.getValue());
        }
        wrapper.setRunAs(servlet.getRunAs());
        Set<SecurityRoleRef> roleRefs = servlet.getSecurityRoleRefs();
        for (SecurityRoleRef roleRef : roleRefs) {
            wrapper.addSecurityReference(
                    roleRef.getName(), roleRef.getLink());
        }
        //设置servlet对应的Class对象
        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);
    }
    //添加servlet对应的url路径,比如*.jsp
    for (Entry<String, String> entry :
            webxml.getServletMappings().entrySet()) {
        context.addServletMappingDecoded(entry.getKey(), entry.getValue());
    }
    //设置session,cookie配置
    SessionConfig sessionConfig = webxml.getSessionConfig();
    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());
        }
    }

    // Context doesn't use version directly
    //添加欢迎页.
    for (String welcomeFile : webxml.getWelcomeFiles()) {
        /*
         * The following will result in a welcome file of "" so don't add
         * that to the context
         * <welcome-file-list>
         *   <welcome-file/>
         * </welcome-file-list>
         */
        if (welcomeFile != null && welcomeFile.length() > 0) {
            context.addWelcomeFile(welcomeFile);
        }
    }

    // Do this last as it depends on servlets
    for (JspPropertyGroup jspPropertyGroup :
            webxml.getJspPropertyGroups()) {
        String jspServletName = context.findServletMapping("*.jsp");
        if (jspServletName == null) {
            jspServletName = "jsp";
        }
        if (context.findChild(jspServletName) != null) {
            for (String urlPattern : jspPropertyGroup.getUrlPatterns()) {
                context.addServletMappingDecoded(urlPattern, jspServletName, true);
            }
        } else {
            if(log.isDebugEnabled()) {
                for (String urlPattern : jspPropertyGroup.getUrlPatterns()) {
                    log.debug("Skipping " + urlPattern + " , no servlet " +
                            jspServletName);
                }
            }
        }
    }
	...
}

先初始化网站路径,然后创建ApplicationContext,调用webresources.StandardRoot.initInternal()进行资源文件的初始化,比如通过DirResourceSet处理目录.在startInternal中继续创建Rfc6265CookieProcessor处理Cookie.调用WebappLoader.startInternal().进入WebappClassLoaderBase.start().进入ContextConfig.configureStart()扫描web.xml文件加载网站的配置.调用方法StandardContext.createWrapper()创建Warpper,每个Wrapper对应一个Servlet,开始创建的是DefaultServlet.DefaultServlet继承自HttpServlet,GenericServlet,实现了Servlet接口.当调用StandardContext.addChild(Container)时,进入StandardWrapper.startInternal().调用StandardWrapperValve.startInternal().在ContextConfig.configureStart中继续加载StandardWrapper对jsp的解析对象,调用StandardWrapperValve对jsp的配置.添加完Servlet后调用StandardContext.addServletMappingDecoded()添加Servlet的url的mapping.在StandardContxt中有个servletMappings属性保存了url和servlet之间的映射关系.如果是/,进入名字叫default的servlet.在在ContextConfig.configureStart中继续中继续配置SessionConfig,SessionCookieConfig.welcomeFile欢迎页面(欢迎页默认是index.html, index.htm, index.jsp).最后初始化jsp引擎JasperInitializer.
在StandardHost.addChild中继续调用JasperInitializer.onStartup().调用TldScanner.scan()扫描jsp标签.StandardHost.addChild中继续调用StandardManager.startInternal().进入SessionIdGeneratorBase.startInternal().调用StandardManager.doLoad().在StandardContext.startInternal中,调用tandardContext.loadOnStartup(Container[]).装载servlet.比如.DefaultServlet.进入DefaultServlet.init().
4.在StandardService.startInternal()中调用Connector.startInternal(),进入不同协议Connector的start方法,比如AjpNioProtocol.start(),Http11NioProtocol.start.

二 http请求处理流程


如下图:
1.过程为Socket处理线程进入SocketProcessorBase.run()->NioEndpoint.SocketProcessor.doRun().NioEndpoint有个Handler属性->ConnectionHandler,进入AbstractProtocol.ConnectionHandler#process().协议里面有Processor属性->AbstractProcessorLight#process()->进入Http协议的Processor,Http11Processor#service().这个http协议Processor里面,有request,response,Adapter属性,在这个方法里面初始化Request,Response对象.完成Socket到Request,Response的转换.->CoyoteAdapter#service().在service()方法里先调用CoyoteAdapter#postParseRequest()匹配Context网站,servlet容器.再调用connector.getService().getContainer().getPipeline().getFirst().invoke()容器的管道进入网站处理.Adapter指向CoyoteAdapter,Adapter是Socket和容器之间的桥接.里面有server.xml配置的connector字段.在CoyoteAdapter#postParseRequest()里面获取请求的url,判断是否是http支持的get,head,post,put等请求.然后依次调用->在mapper.Mapper#internalMap()中根据MappingData中存储的url路径中的的host(比如localhost),取出所有的context网站.然后根据url路径匹配对应的Context网站->匹配到网站后,进入Mapper.internalMapWrapper方法中匹配用来处理请求的Wrapper,即Servlet.代码如下:
2.匹配url的网站mapper.Mapper#internalMap()

private final void internalMap(CharChunk host, CharChunk uri,
            String version, MappingData mappingData) throws IOException {
    ...
    // Virtual host mapping
    //取出所有的host主机名
    MappedHost[] hosts = this.hosts;
    //匹配主机名
    MappedHost mappedHost = exactFindIgnoreCase(hosts, host);
    //如果url路径中的主机名为空,重新从url中获取.
    if (mappedHost == null) {
       ...
    }
    mappingData.host = mappedHost.object;

    if (uri.isNull()) {
        // Can't map context or wrapper without a uri
        return;
    }

    uri.setLimit(-1);

    // Context mapping
    //从匹配出的主机中,取出主机支持的contexts网站列表.
    ContextList contextList = mappedHost.contextList;
    MappedContext[] contexts = contextList.contexts;
    //从url路径中匹配网站名.
    int pos = find(contexts, uri);
    if (pos == -1) {
        return;
    }

    //通配符匹配
    int lastSlash = -1;
    int uriEnd = uri.getEnd();
    int length = -1;
    boolean found = false;
    MappedContext context = null;
    while (pos >= 0) {
        context = contexts[pos];
        if (uri.startsWith(context.name)) {
            length = context.name.length();
            if (uri.getLength() == length) {
                found = true;
                break;
            } else if (uri.startsWithIgnoreCase("/", length)) {
                found = true;
                break;
            }
        }
        if (lastSlash == -1) {
            lastSlash = nthSlash(uri, contextList.nesting + 1);
        } else {
            lastSlash = lastSlash(uri);
        }
        uri.setEnd(lastSlash);
        pos = find(contexts, uri);
    }
    uri.setEnd(uriEnd);

    if (!found) {
        if (contexts[0].name.equals("")) {
            context = contexts[0];
        } else {
            context = null;
        }
    }
    //如果没有匹配的网站,直接返回
    if (context == null) {
        return;
    }

    mappingData.contextPath.setString(context.name);
	...

    // Wrapper mapping
    //匹配网站Context下的处理的Wrapper,即Servelet
    if (!contextVersion.isPaused()) {
        internalMapWrapper(contextVersion, uri, mappingData);
    }

3.匹配Servlet

private final void internalMapWrapper(ContextVersion contextVersion,
                                          CharChunk path,
                                          MappingData mappingData) throws IOException {
  	...
  	//精确匹配
    // Rule 1 -- Exact Match
    MappedWrapper[] exactWrappers = contextVersion.exactWrappers;
    internalMapExactWrapper(exactWrappers, path, mappingData);

    //匹配前缀通配符
    // Rule 2 -- Prefix Match
    boolean checkJspWelcomeFiles = false;
    MappedWrapper[] wildcardWrappers = contextVersion.wildcardWrappers;
    if (mappingData.wrapper == null) {
        internalMapWildcardWrapper(wildcardWrappers, contextVersion.nesting,
                                   path, mappingData);
        if (mappingData.wrapper != null && mappingData.jspWildCard) {
            char[] buf = path.getBuffer();
            if (buf[pathEnd - 1] == '/') {
                mappingData.wrapper = null;
                checkJspWelcomeFiles = true;
            } else {
                // See Bugzilla 27704
                mappingData.wrapperPath.setChars(buf, path.getStart(),
                                                 path.getLength());
                mappingData.pathInfo.recycle();
            }
        }
    }

    if(mappingData.wrapper == null && noServletPath &&
         ...
    }

    //扩展匹配
    // Rule 3 -- Extension Match
    MappedWrapper[] extensionWrappers = contextVersion.extensionWrappers;
    if (mappingData.wrapper == null && !checkJspWelcomeFiles) {
        internalMapExtensionWrapper(extensionWrappers, path, mappingData,
                true);
    }
    //欢迎页匹配
    // Rule 4 -- Welcome resources processing for servlets
    if (mappingData.wrapper == null) {
        boolean checkWelcomeFiles = checkJspWelcomeFiles;
        if (!checkWelcomeFiles) {
            char[] buf = path.getBuffer();
            checkWelcomeFiles = (buf[pathEnd - 1] == '/');
        }
        if (checkWelcomeFiles) {
            for (int i = 0; (i < contextVersion.welcomeResources.length)
                     && (mappingData.wrapper == null); i++) {
                path.setOffset(pathOffset);
                path.setEnd(pathEnd);
                path.append(contextVersion.welcomeResources[i], 0,
                        contextVersion.welcomeResources[i].length());
                path.setOffset(servletPath);

                // Rule 4a -- Welcome resources processing for exact macth
                internalMapExactWrapper(exactWrappers, path, mappingData);

                // Rule 4b -- Welcome resources processing for prefix match
                if (mappingData.wrapper == null) {
                    internalMapWildcardWrapper
                        (wildcardWrappers, contextVersion.nesting,
                         path, mappingData);
                }

                // Rule 4c -- Welcome resources processing
                //            for physical folder
                if (mappingData.wrapper == null
                    && contextVersion.resources != null) {
                    String pathStr = path.toString();
                    WebResource file =
                            contextVersion.resources.getResource(pathStr);
                    if (file != null && file.isFile()) {
                        internalMapExtensionWrapper(extensionWrappers, path,
                                                    mappingData, true);
                        if (mappingData.wrapper == null
                            && contextVersion.defaultWrapper != null) {
                           ...
                        }
                    }
                }
            }

            ...
        }

    }

    //默认的DefaultServlet来处理
    // Rule 7 -- Default servlet
    if (mappingData.wrapper == null && !checkJspWelcomeFiles) {
        if (contextVersion.defaultWrapper != null) {
            mappingData.wrapper = contextVersion.defaultWrapper.object;
            ...
            mappingData.matchType = MappingMatch.DEFAULT;
        }
        // Redirection to a folder
        char[] buf = path.getBuffer();
        if (contextVersion.resources != null && buf[pathEnd -1 ] != '/') {
            ...
        }
    }
}

4.在CoyoteAdapter#service()中通过
connector.getService().getContainer().getPipeline().getFirst().invoke();一行进入Service容器,进入server.xml中配置的Service下一级Engine处理.这里,开始Engine->Host->Context->Wrapper的各级管道.进入StandardEngineValve#invoke().

三.connector
poller机制

四.container和Service

五.host

六.context

七.wrapper

八.生命周期

九.类加载器

十.session管理器

十一.网络IO模型
 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值