阅读 1322

jetty启动web项目源码分析

jetty是做什么的?

jetty是HTTP服务,HTTP客户端,和javax.servlet的容器。它本身被设计成嵌入式模式,应该将jetty集成到自己的应用,jetty本身可以实例化,能像任何POJO一样使用,用jetty就相当于把Http服务塞进了自己的应用

jetty的口号“Don't deploy your application in Jetty, deploy Jetty in your application.”

启动jetty java -jar start.jar

运行jetty java -jar start.jar等效于 java -jar start.jar etc/jetty.xml[默认的jetty配置文件]

启动jetty若需要的更多参数,可以统一通过 start.ini 文件来配置

#===========================================================
# Jetty start.ini example
#-----------------------------------------------------------
OPTIONS=Server
etc/jetty.xml
etc/jetty-http.xml
复制代码

官网启动Jetty
OPTIONS:指定构建过程中这个目录下面的所有jar都需要添加
etc/jetty.xml:它会添加到启动start.jar命令的后头

在start.ini中同时可以指定JVM的参数,只是必须添加 --exec

#===========================================================
# Jetty start.jar arguments
#-----------------------------------------------------------
--exec
-Xmx512m
-XX:OnOutOfMemoryError='kill -3 %p'
-Dcom.sun.management.jmxremote
OPTIONS=Server,jmx,resources
etc/jetty-jmx.xml
etc/jetty.xml
etc/jetty-ssl.xml
复制代码

这么做是因为这里添加的JVM 参数并没有影响start.jar的启动,而是另起一个新的JVM,会加上这些参数来运行

Jetty的启动start.jar分析

主要逻辑在Main.java中

Jetty Main源码

以包含java的参数运行为例

 // execute Jetty in another JVM
if (args.isExec()){
    //获取参数
    CommandLineBuilder cmd = args.getMainArgs(true);
    ...
    ProcessBuilder pbuilder = new ProcessBuilder(cmd.getArgs());
    StartLog.endStartLog();
    final Process process = pbuilder.start();
    ...
    process.waitFor();
    System.exit(0); // exit JVM when child process ends.
    return;
}
复制代码

提取参数的过程中,对于非JPMS,会在最后添加

cmd.addRawArg("-cp");
cmd.addRawArg(classpath.toString());
cmd.addRawArg(getMainClassname());
复制代码

可以追踪MainClassname得到

private static final String MAIN_CLASS = "org.eclipse.jetty.xml.XmlConfiguration";
复制代码

后续新建一个进程,真正的去运行目的程序

  pid = forkAndExec(launchMechanism.ordinal() + 1, //获取系统类型
                          helperpath, //对于java来说就是获取 java 命令地址
                          prog,
                          argBlock, argc,
                          envBlock, envc,
                          dir,
                          fds,
                          redirectErrorStream);
复制代码

XmlConfiguration启动

主要就是加载所有的xml文件,然后运行实现了LifeCycle接口的方法

List<Object> objects = new ArrayList<>(args.length);
for (int i = 0; i < args.length; i++)
{
    if (!args[i].toLowerCase(Locale.ENGLISH).endsWith(".properties") && (args[i].indexOf('=')<0))
    {
        XmlConfiguration configuration = new XmlConfiguration(Resource.newResource(args[i]).getURI().toURL());
        if (last != null)
            configuration.getIdMap().putAll(last.getIdMap());
        if (properties.size() > 0)
        {
            Map<String, String> props = new HashMap<>();
            for (Object key : properties.keySet())
            {
                props.put(key.toString(),String.valueOf(properties.get(key)));
            }
            configuration.getProperties().putAll(props);
        }

        Object obj = configuration.configure();
        if (obj!=null && !objects.contains(obj))
            objects.add(obj);
        last = configuration;
    }
}

// For all objects created by XmlConfigurations, start them if they are lifecycles.
for (Object obj : objects)
{
    if (obj instanceof LifeCycle)
    {
        LifeCycle lc = (LifeCycle)obj;
        if (!lc.isRunning())
            lc.start(); //运行
    }
}
复制代码

对应着jetty.xml中的配置,他就是Server的start方法

jetty.xml文件

它是默认的jetty配置文件,主要包括:

  1. 服务器的类和全局选项
  2. 连接池(最大最小线程数)
  3. 连接器(端口,超时时间,缓冲区,协议等)
  4. 处理器(handler structure,可用默认的处理器或者上下文处理搜集器contextHandlerCollections)
  5. 发布管理器(用来扫描要发布的webapp和上下文)
  6. 登录服务(做权限检查)
  7. 请求日志

jetty支持多配置文件,每一个配置文件中通过指定要初始化的服务器实例,ID来标识,每个ID都会在同一个JVM中创建一个新的服务,如果在多个配置文件中用同一个ID,这些所有的配置都会用到同一个服务上

配置文件一般样式

<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">

//<configure> 根元素,指定以下配置是给那个类,一般在jetty.xml中server,或者jetty-web.xml中的WebAppContext
<Configure id="foo" class="com.acme.Foo">

//<set> setter方法调用的标识。name属性用来标识setter的方法名,如果这个方法没有找到,就把name中值当做字段来使用。如果有属性class表明这个set方法是静态方法
  <Set name="name">demo</Set>
  <Set name="nested">
  
  //<new>初始化对象,class决定new对象的类型,需要写全路径类名,没有用<arg>则调用默认的构造函数
    <New id="bar" class="com.acme.Bar
  
    //<arg> 作为构造函数或者一个方法的参数,用于<call>和<new>
      <Arg>true</Arg>
      <Set name="wibble">10</Set>
      <Set name="wobble">xyz</Set>
      <Set name="parent"><Ref id="foo"/></Set>
  
      //<call>调用对象的某个方法,name属性表明确确调用的方法的名字
      <Call name="init">
         <Arg>false</Arg>
      </Call>
    </New>
  </Set>

//<ref>引用之前已经生成对象的id
  <Ref id="bar">
    <Set name="wibble">20</Set>
  
    //<get>调用当前对象的get方法,同set
    <Get name="parent">
      <Set name="name">demo2</Set>
    </Get>
  </Ref>
</Configure>
复制代码

它相当于java代码

com.acme.Foo foo = new com.acme.Foo();
foo.setName("demo");

com.acme.Bar bar = new com.acme.Bar(true);
bar.setWibble(10);
bar.setWobble("xyz");
bar.setParent(foo);
bar.init(false);

foo.setNested(bar);

bar.setWibble(20);
bar.getParent().setName("demo2");
复制代码

web项目中的一般配置

web服务指定的服务类一般为 org.eclipse.jetty.server.Server,然后构建对应的实例

  • ThreadPool。
  • Connector。
  • Handler。

这也是jetty整个架构的体现,Connector用来接收连接,Handler用来处理request和response

Jetty 官网架构

QueuedThreadPool

jetty的线程池默认使用的就是 QueuedThreadPool,它的构造函数如下

public QueuedThreadPool(@Name("maxThreads") int maxThreads, @Name("minThreads") int minThreads, @Name("idleTimeout") int idleTimeout, @Name("reservedThreads") int reservedThreads, @Name("queue") BlockingQueue<Runnable> queue, @Name("threadGroup") ThreadGroup threadGroup)
    {
        if (maxThreads < minThreads) {
            throw new IllegalArgumentException("max threads ("+maxThreads+") less than min threads ("
                    +minThreads+")");
        }

        setMinThreads(minThreads);
        setMaxThreads(maxThreads);
        setIdleTimeout(idleTimeout);
        setStopTimeout(5000);
        setReservedThreads(reservedThreads);
        if (queue==null)
        {
            int capacity=Math.max(_minThreads, 8);
            queue=new BlockingArrayQueue<>(capacity, capacity);
        }
        _jobs=queue;
        _threadGroup=threadGroup;
        setThreadPoolBudget(new ThreadPoolBudget(this));
    }
复制代码

本质上也是使用最大线程最小线程阻塞队列来实现

ServerConnector

  public ServerConnector(
        @Name("server") Server server,
        @Name("executor") Executor executor,
        @Name("scheduler") Scheduler scheduler,
        @Name("bufferPool") ByteBufferPool bufferPool,
        @Name("acceptors") int acceptors,
        @Name("selectors") int selectors,
        @Name("factories") ConnectionFactory... factories)
    {
        super(server,executor,scheduler,bufferPool,acceptors,factories);
        _manager = newSelectorManager(getExecutor(), getScheduler(),selectors);
        addBean(_manager, true);//在ServerConnector启动的过程中,会被启动
        setAcceptorPriorityDelta(-2);
    }
复制代码
  • factories:默认使用HttpConnectionFactory
  • acceptors:表示用来接收新的TCP/IP连接的线程个数
    int cores = ProcessorUtils.availableProcessors();
    if (acceptors < 0)
        acceptors=Math.max(1, Math.min(4,cores/8));
    if (acceptors > cores)
        LOG.warn("Acceptors should be <= availableProcessors: " + this);
    _acceptors = new Thread[acceptors];
    复制代码

WebAppContext

处理web请求使用的handler,一般使用默认的构造函数,通过set方法来实例化对应的属性。在jetty.xml中比如指定属性configurationClasses一般取值如下

<Array id="plusConfig" type="java.lang.String">
    <Item>org.eclipse.jetty.webapp.WebInfConfiguration</Item>
    <Item>org.eclipse.jetty.webapp.WebXmlConfiguration</Item>
    <Item>org.eclipse.jetty.webapp.MetaInfConfiguration</Item>
    <Item>org.eclipse.jetty.webapp.FragmentConfiguration</Item>
    <Item>org.eclipse.jetty.plus.webapp.EnvConfiguration</Item>
    <Item>org.eclipse.jetty.plus.webapp.PlusConfiguration</Item>
    <Item>org.eclipse.jetty.annotations.AnnotationConfiguration</Item>
    <Item>org.eclipse.jetty.webapp.JettyWebXmlConfiguration</Item>
    <Item>org.eclipse.jetty.webapp.TagLibConfiguration</Item>
</Array>
复制代码

比如web有两个WebInfConfiguration和WebXmlConfiguration,从名字可以感受到,WebInfConfiguration就是对应web项目中的WEB-INF目录,而WebXmlConfiguration就是对应着web.xml文件

Server启动

Server类是Jetty的HTTP Servlet服务器,它实现了LifeCycle接口。调用的start实现真正执行的就是Server自身的doStart

 //AbstractLifeCycle中
 @Override
public final void start() throws Exception
{
    synchronized (_lock)
    {
    ...
        doStart();
    ...
    }
}
复制代码

类似的后续的所有相关LifeCycle的start启动,其实就是调用实现了它的类的doStart()方法

Server本身启动

protected void doStart() throws Exception
{
    ...
    //1. 保证JVM自己挂掉的时候,对应的Jetty进程也会关掉
    ShutdownMonitor.register(this);
    ...
    //2. 按照Server自己添加的bean的顺序,来一个个的启动他们
        super.doStart();
    ...
    //3. 启动connector
    for (Connector connector : _connectors)
    {
        try
        {
            connector.start();
        }
        catch(Throwable e)
        {
            mex.add(e);
        }
    }
    ...
}
复制代码

bean启动

对于server来说,它的bean会在每次调用对应的set方法都会执行,包括ThreadPool和Handler

//ContainerLifeCycle中
public boolean addBean(Object o)
{
    if (o instanceof LifeCycle)
    {
        LifeCycle l = (LifeCycle)o;
        //尚未启动的统一为AUTO
        return addBean(o,l.isRunning()?Managed.UNMANAGED:Managed.AUTO);
    }

    return addBean(o,Managed.POJO);
}
复制代码

执行对应bean的启动

  //ContainerLifeCycle中
    protected void doStart() throws Exception
    {
        ...
        // start our managed and auto beans
        for (Bean b : _beans)
        {
            if (b._bean instanceof LifeCycle)
            {
                LifeCycle l = (LifeCycle)b._bean;
                switch(b._managed)
                {
                    case MANAGED:
                        if (!l.isRunning())
                            start(l);
                        break;
                        
                    case AUTO:
                        if (l.isRunning())
                            unmanage(b);
                        else
                        {
                            manage(b);
                            start(l);
                        }
                        break;
                        
                    default:
                        break;
                }
            }
        }

        super.doStart();
    }
复制代码

对于web来说,一定会配置一个handerWebAppContext来加载对应的web.xml文件

下面着重介绍 WebAppContext

QueuedThreadPool启动

@Override
protected void doStart() throws Exception
{
    _tryExecutor = new ReservedThreadExecutor(this,_reservedThreads);
    addBean(_tryExecutor);
    
    super.doStart();
    _threadsStarted.set(0);

    startThreads(_minThreads);  
}

 private boolean startThreads(int threadsToStart)
    {
        while (threadsToStart > 0 && isRunning())
        {
            ...
            Thread thread = newThread(_runnable);
            thread.setDaemon(isDaemon());
            thread.setPriority(getThreadsPriority());
            thread.setName(_name + "-" + thread.getId());
            _threads.add(thread);
            _lastShrink.set(System.nanoTime());
            thread.start();
            started = true;
            --threadsToStart;
           ...
        }
        return true;
    }
复制代码

可以看到它会直接调用去启动最小的线程数

org.eclipse.jetty.server.ServerConnector

Jetty9中它是主要的实现连接TCP/IP的类。可以在父类中找到对应dostart


//AbstractNetworkConnector
    protected void doStart() throws Exception
    {
        open();
        super.doStart();
    }
//ServerConnector    
public void open() throws IOException
{
    if (_acceptChannel == null)
    {
        _acceptChannel = openAcceptChannel();
        _acceptChannel.configureBlocking(true);//阻塞接收连接的channel
        _localPort = _acceptChannel.socket().getLocalPort();
        if (_localPort <= 0)
            throw new IOException("Server channel not bound");
        addBean(_acceptChannel);
    }
}
复制代码

再向父类执行

 //AbstractConnector
    protected void doStart() throws Exception
    {
        ...
       //1. 选择连接的类型
        _defaultConnectionFactory = getConnectionFactory(_defaultProtocol);
        ...
        SslConnectionFactory ssl = getConnectionFactory(SslConnectionFactory.class);
        ....
        //2. 启动自己的bean
        super.doStart();
        ...
        //3. 启动接收请求的线程
        for (int i = 0; i < _acceptors.length; i++)
        {
            Acceptor a = new Acceptor(i); 
            addBean(a);
            getExecutor().execute(a);
        }
        ... 
    }
复制代码

启动自己的bean

在构造函数执行的时候,bean中添加了SelectorManager,它的实例是一个ServerConnectorManager,执行的构造函数如下

protected SelectorManager(Executor executor, Scheduler scheduler, int selectors)
{
    if (selectors <= 0)
        selectors = defaultSelectors(executor);
    this.executor = executor;
    this.scheduler = scheduler;
    _selectors = new ManagedSelector[selectors];
    _selectorIndexUpdate = index -> (index+1)%_selectors.length;
}
复制代码
  • executor用来处理选中的EndPoint
  • scheduler处理与时间相关的事件
  • selectors实际就是包装了Java的Selector 启动过程其实就是去创建约定个数的ManagedSelector,它本身维护了一个Jave的Selector,一个Deque

    protected void doStart() throws Exception
    {
        ...
        for (int i = 0; i < _selectors.length; i++)
        {
            ManagedSelector selector = newSelector(i);
            _selectors[i] = selector;
            addBean(selector);
        }
        //执行自己的bean启动
        super.doStart();
    }
    //新建的ManagedSelector
       public ManagedSelector(SelectorManager selectorManager, int id)
    {
        _selectorManager = selectorManager;
        _id = id;
        SelectorProducer producer = new SelectorProducer();
        Executor executor = selectorManager.getExecutor();
        //producer就是SelectorProducer,后续用到
        _strategy = new EatWhatYouKill(producer,executor);
        addBean(_strategy,true);
        setStopTimeout(5000);
    }
复制代码

再次看到ManagedSelector的启动


    @Override
    protected void doStart() throws Exception
    {
        //1. EatWhatYouKill本身并没有做特别的doStart实现
        super.doStart();
        //2.获取一个JavaSelector
        _selector = _selectorManager.newSelector();

        // The producer used by the strategies will never
        // be idle (either produces a task or blocks).

        // The normal strategy obtains the produced task, schedules
        // a new thread to produce more, runs the task and then exits.
        //3.执行EatWhatYouKill的produce方法
        _selectorManager.execute(_strategy::produce);

        // Set started only if we really are started
        //4.往Deque中塞一个Start事件,实质就是运行起来就标志这Selector启动了
        Start start = new Start();
        submit(start);
        start._started.await();
    }

复制代码

这里的_selectorManager.execute(_strategy::produce);即去获取对应的连接建立后,处理连接事件

接收请求

Acceptor就是集成了Runnable,它的核心就是调用accept方法,对应就是ServerConnector的实现

@Override
public void accept(int acceptorID) throws IOException
{
    ServerSocketChannel serverChannel = _acceptChannel;
    if (serverChannel != null && serverChannel.isOpen())
    {
        SocketChannel channel = serverChannel.accept();//等待连接的到来
        accepted(channel);
    }
}
复制代码

网络的详细分析

WebAppContext

对于web项目来说,处理请求的一般使用WebAppContext。 WebAppContext是用来协助其它的handlers的构建和配置,以实现标准的web应用配置。它继承了ServletContextHandler,ServletContextHandler则支持标准的通过web.xml配置的session、security,listeners,filter,servlet和JSP

ServletContextHandler拥有 ServletHandler字段,并继承了ContextHandler WebAppContext同时也实现了LifeCycle类,它的doStart方法核心

protected void doStart() throws Exception{
    ...
    preConfigure();
    super.doStart();
    postConfigure();
    ...
}
复制代码

预加载

    public void preConfigure() throws Exception
    {
        // 加载所有的xml文件
        loadConfigurations();
        ...
        for (Configuration configuration : _configurations)
        {
            //对每个设置的要加载的配置进行处理,比如`WebInfConfiguration`和`WebXmlConfiguration`
            LOG.debug("preConfigure {} with {}",this,configuration);
            configuration.preConfigure(this);
        }
    }
复制代码

WebInfConfiguration预加载

 public void preConfigure(final WebAppContext context) throws Exception
{
    //1. 创建Temp目录
    resolveTempDirectory(context);

    //2. 进行一些解压缩的工作,比如对war进行解压缩
    unpack (context);
    //3. 找到容器下面classpath上的jar
    findAndFilterContainerPaths(context);
    //4. 找到没有在 /WEB-INF/lib下面的jar
    findAndFilterWebAppPaths(context);

    //No pattern to appy to classes, just add to metadata
    context.getMetaData().setWebInfClassesDirs(findClassDirs(context));
}
复制代码

它主要是处理了/WEB-INF 目录的相关工作

WebXmlConfiguration预加载

  @Override
    public void preConfigure (WebAppContext context) throws Exception
    {
        //parse webdefault.xml 这里就是获取默认的文件
        String defaultsDescriptor = context.getDefaultsDescriptor();
        if (defaultsDescriptor != null && defaultsDescriptor.length() > 0)
        {
            Resource dftResource = Resource.newSystemResource(defaultsDescriptor);
            if (dftResource == null) 
                dftResource = context.newResource(defaultsDescriptor);
            context.getMetaData().setDefaults (dftResource);
        }
        
        //parse, but don't process web.xml
        //查找web.xml文件
        Resource webxml = findWebXml(context);
        ...
    }
    
    protected Resource findWebXml(WebAppContext context) throws IOException, MalformedURLException
    {
        ...
        //获取web-inf目录
        Resource web_inf = context.getWebInf();
        if (web_inf != null && web_inf.isDirectory())
        {
            // do web.xml file
            Resource web = web_inf.addPath("web.xml");
            ...
        }
        return null;
    }
复制代码

这里就可以确确实实看到web.xml被加载了

调用父类的doStart

沿着路径网上可以看到,在处理了一些类加载器之后

//ContextHandler
protected void doStart() throws Exception
{
        ...
        startContext();
        ...
}
复制代码

在WebAppContext中对应实现如下

protected void startContext()
    throws Exception
{
    //1. 调用对应的配置类的配置方法
    configure();

    //2. 解析xml
    _metadata.resolve(this);
    
    //3. 文件加载结束启动web
    startWebapp();
}
复制代码

WebXmlConfiguration.configure

它的配置则是加载了一个标签处理器

 public void configure (WebAppContext context) throws Exception
{
    ...
    context.getMetaData().addDescriptorProcessor(new StandardDescriptorProcessor());
}

//StandardDescriptorProcessor构造函数如下
 public StandardDescriptorProcessor ()
    {
        try
        {
            registerVisitor("context-param", this.getClass().getMethod("visitContextParam", __signature));
            registerVisitor("display-name", this.getClass().getMethod("visitDisplayName", __signature));
            registerVisitor("servlet", this.getClass().getMethod("visitServlet",  __signature));
            registerVisitor("servlet-mapping", this.getClass().getMethod("visitServletMapping",  __signature));
            registerVisitor("session-config", this.getClass().getMethod("visitSessionConfig",  __signature));
            registerVisitor("mime-mapping", this.getClass().getMethod("visitMimeMapping",  __signature));
            registerVisitor("welcome-file-list", this.getClass().getMethod("visitWelcomeFileList",  __signature));
            registerVisitor("locale-encoding-mapping-list", this.getClass().getMethod("visitLocaleEncodingList",  __signature));
            registerVisitor("error-page", this.getClass().getMethod("visitErrorPage",  __signature));
            registerVisitor("taglib", this.getClass().getMethod("visitTagLib",  __signature));
            registerVisitor("jsp-config", this.getClass().getMethod("visitJspConfig",  __signature));
            registerVisitor("security-constraint", this.getClass().getMethod("visitSecurityConstraint",  __signature));
            registerVisitor("login-config", this.getClass().getMethod("visitLoginConfig",  __signature));
            registerVisitor("security-role", this.getClass().getMethod("visitSecurityRole",  __signature));
            registerVisitor("filter", this.getClass().getMethod("visitFilter",  __signature));
            registerVisitor("filter-mapping", this.getClass().getMethod("visitFilterMapping",  __signature));
            registerVisitor("listener", this.getClass().getMethod("visitListener",  __signature));
            registerVisitor("distributable", this.getClass().getMethod("visitDistributable",  __signature));
            registerVisitor("deny-uncovered-http-methods", this.getClass().getMethod("visitDenyUncoveredHttpMethods", __signature));
        }
        catch (Exception e)
        {
            throw new IllegalStateException(e);
        }
    }
复制代码

可以看到这些标签也就是平常写web.xml所用到的

解析web.xml

对应resolve则是获取描述符处理器一个个的去处理对应的处理器,以web.xml的处理器来说就是StandardDescriptorProcessor.process

//StandardDescriptorProcessor父类IterativeDescriptorProcessor中    
public void process(WebAppContext context, Descriptor descriptor)
throws Exception
{
    ...
    XmlParser.Node root = descriptor.getRoot();
    Iterator<?> iter = root.iterator();
    XmlParser.Node node = null;
    while (iter.hasNext())
    {
        Object o = iter.next();
        if (!(o instanceof XmlParser.Node)) continue;
        node = (XmlParser.Node) o;
        visit(context, descriptor, node);
    }
    ...
}
复制代码

它会一个节点的遍历并调用对应的visit方法,StandardDescriptorProcessor中对一个存在着相应的visit

<listener>为例

  public void visitListener(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
    {
        //读取配置的listerclass的名字
        String className = node.getString("listener-class", false, true);
        EventListener listener = null;
        try
        {
            if (className != null && className.length()> 0)
            {
                //Servlet Spec 3.0 p 74
                //存在重复的名字不会构建重复的实例
                for (ListenerHolder holder : context.getServletHandler().getListeners())
                {
                    if (holder.getClassName().equals(className))
                        return; 
                }

                ((WebDescriptor)descriptor).addClassName(className);
                //创建一个持有Listerner的类
                ListenerHolder h = context.getServletHandler().newListenerHolder(new Source (Source.Origin.DESCRIPTOR, descriptor.getResource().toString()));
                //设置持有的类名,即web.xml中配置的
                h.setClassName(className);
                //使得ServletHandler持有listener
                context.getServletHandler().addListener(h);
                context.getMetaData().setOrigin(className+".listener", descriptor);
            }
        }
        catch (Exception e)
        {
            LOG.warn("Could not instantiate listener " + className, e);
            return;
        }
    }
复制代码

类似的ServletHandler会持有 private ServletHolder[] _servlets=new ServletHolder[0]; private FilterHolder[] _filters=new FilterHolder[0];

启动Web

它会调用父类的startContext

    protected void startContext() throws Exception
    {
        ServletContainerInitializerCaller sciBean = getBean(ServletContainerInitializerCaller.class);
        if (sciBean!=null)
            //1. 调用实现了接口ServletContainerInitializerCaller的start方法
            sciBean.start();

        if (_servletHandler != null)
        {
            //Ensure listener instances are created, added to ContextHandler
            if(_servletHandler.getListeners() != null)
            {
                for (ListenerHolder holder:_servletHandler.getListeners())
                { 
                    //获取持有listener的holder,这里实际内部实现就通过反射去创建对应listener的class对象
                    holder.start();
                    //we need to pass in the context because the ServletHandler has not
                    //yet got a reference to the ServletContext (happens in super.startContext)
                    //对class对象进行初始化
                    holder.initialize(_scontext);
                    //将新建的Listener加入contextHandler的_eventListeners,做后续启动用
                    addEventListener(holder.getListener());
                }
            }
        }
        //调用父类的startContext
        super.startContext();

        // 启动ServletHandler中的filter,servlets,listiners
        if (_servletHandler != null)
            _servletHandler.initialize();
    }
复制代码

父类startContext最关键的在于

 protected void startContext() throws Exception
    {
      ...
        if (!_servletContextListeners.isEmpty())
        {
            ServletContextEvent event = new ServletContextEvent(_scontext);
            for (ServletContextListener listener : _servletContextListeners)
            {
                callContextInitialized(listener,event);//调用对应配置的listener的contextInitialized方法
                _destroySerletContextListeners.add(listener);
            }
        }
    }
复制代码

最后执行所有相关的servlet,filter的holder启动

  public void initialize()
        throws Exception
    {
        MultiException mx = new MultiException();

        Stream.concat(Stream.concat(
            Arrays.stream(_filters),
            Arrays.stream(_servlets).sorted()),
            Arrays.stream(_listeners))
            .forEach(h->{
                try
                {
                    if (!h.isStarted())
                    {
                        h.start();
                        h.initialize();
                    }
                }
                catch (Throwable e)
                {
                    LOG.debug(Log.EXCEPTION, e);
                    mx.add(e);
                }
            });

        mx.ifExceptionThrow();
    }
复制代码

以ServletHolder为例,它的实现类似于Listener,先通过反射获取对应的类,然后新建对象,最后调用servlet的init方法

 private synchronized void initServlet()
        throws ServletException
    {
        ...
        if (_servlet==null)
            _servlet=newInstance();
        ...
        // Handle configuring servlets that implement org.apache.jasper.servlet.JspServlet
        if (isJspServlet())
        {
            initJspServlet();
            detectJspContainer();
        }
        else if (_forcedPath != null)
            detectJspContainer();

        initMultiPart();
        ...
        //调用Servlet的init方法
        _servlet.init(_config);
        ...
    }
复制代码

后置加载

WebInfConfiguration和WebXmlConfiguration没有实现,什么都不做

附jetty8配合maven使用

pom中配置

<plugin>
  <groupId>org.mortbay.jetty</groupId>
  <artifactId>jetty-maven-plugin</artifactId>
  <version>jettyVersion</version>
  <configuration>
		<connectors>
		<!--可选。配置了jetty的监听接口,没有特别指定,默认使用org.eclipse.jetty.server.nio.SelectChannelConnector ,端口是8080 -->
			<connector implementation="org.eclipse.jetty.server.nio.SelectChannelConnector">
				<port>9999</port>
			</connector>
		</connectors>
		<stopPort>8888</stopPort>
		<stopKey>a</stopKey>
		<!--可选。监控web项目是否改变的时间设置【有改变就热启动,单位是秒】,默认是0,禁止扫描,任何大于0的数字都是启用【扫描的地方包括 pom.xml WEB-INF/lib WEB-INF/classes WEB-INF/web.xml】 -->
		<scanIntervalSeconds>10</scanIntervalSeconds>
		<webApp>
			<contextPath>/</contextPath>
		</webApp>
	</configuration>
</plugin>
复制代码

这样可以本地使用命令行 mvn jetty:run 运行jetty服务了