jetty、servlet以及spring的衔接源码分析

Servlet

对于一个请求来说,若是只是须要一个静态页面,能够直接在服务器上根据路径访问获得,可是若是请求的数据是一个动态页面,即只有在运行时从后台数据库获取,再拼装东西返回,而后生成一个对应的html文件。在Java中为了实现这个功能,使用的就是Servlet规范。html

Servlet:server component,运行在服务器上的java代码java

Servlet容器

Servlet并不处理任何的协议和链接等等动做,它只是约定了一个种处理request-response的模式。每一个功能实现Servlet接口的类均可以用来处理请求,好比加法用1个servlet,减法用一个Servlet,这样一但多起来,就须要知道,那些请求用那个Servlet处理,对应的配置产生物也就是web.xml,另外Servlet对象的构建、链接端口的请求,处理好对应的映射关系等等都须要有一个程序来负责,这个程序称做Servlet容器,好比Jetty,从Jetty的整体架构也就能够看出来它很好的实践了这些web

Connector负责链接,Handler则处理对应的请求,交给Servlet来处理spring

Servlet的生命周期

Servlet的生命周期是由发布它的容器控制的,好比Jetty,当要把请求映射到一个Servlet上时,容器通常会作以下的事情:数据库

  1. 若是Servlet不存在,就加载Servlet类,建立Servlet实例,而后调用Servlet的init方法
  2. 执行Servlet的service方法,传递request和response对象
  3. 若是容器要移除掉servlet,它就会调用Servlet的destroy方法

javax.servletjavax.servlet.http提供了要实现Servlet的全部接口和相关类,每个处理Servlet的类必须实现 Servlet.java接口,它的基本实现为GenericServlet,是与协议无关的一个实现,若是要实现本身的Servlet接口,能够继承它,仅须要实现对应的Service方法。对于处理HTTP请求则可使用HttpServlet更方便,它根据Http请求的类型,对service方法进行了细分,这样能够更好的去处理想要处理的请求bash

protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        String method = req.getMethod();

        if (method.equals(METHOD_GET)) {
            long lastModified = getLastModified(req);
            if (lastModified == -1) {
                // servlet doesn't support if-modified-since, no reason // to go through further expensive logic doGet(req, resp); } else { long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE); if (ifModifiedSince < lastModified) { // If the servlet mod time is later, call doGet() // Round down to the nearest second for a proper compare // A ifModifiedSince of -1 will always be less maybeSetLastModified(resp, lastModified); doGet(req, resp); } else { resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED); } } } else if (method.equals(METHOD_HEAD)) { long lastModified = getLastModified(req); maybeSetLastModified(resp, lastModified); doHead(req, resp); } else if (method.equals(METHOD_POST)) { doPost(req, resp); } else if (method.equals(METHOD_PUT)) { doPut(req, resp); } else if (method.equals(METHOD_DELETE)) { doDelete(req, resp); } else if (method.equals(METHOD_OPTIONS)) { doOptions(req,resp); } else if (method.equals(METHOD_TRACE)) { doTrace(req,resp); } else { // // Note that this means NO servlet supports whatever // method was requested, anywhere on this server. // String errMsg = lStrings.getString("http.method_not_implemented"); Object[] errArgs = new Object[1]; errArgs[0] = method; errMsg = MessageFormat.format(errMsg, errArgs); resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg); } } 复制代码

每次请求总会携带一些东西,返回也是,Servlet针对请求抽象了ServletRequest接口和ServletResponse接口,而特定于Http协议,则分别设计了HttpServletRequest接口和HttpServletResponse,每次请求Servlet容器负责实现对应的类提交给service方法服务器

web.xml 配置

web.xml常见配置以下网络

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:application-context.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <servlet>
        <servlet-name>mvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:web-mvc-dispatcher.xml</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>mvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

复制代码

Jetty启动web项目中的分析可知,会依次的去执行ContextLoaderListener的contextInitialized和DispatcherServlet的init方法,这里就是jetty容器、servlet和spring的衔接架构

Jetty处理网络请求

Jetty启动web项目分析可获得,网络请求分红两部分mvc

  1. 等待链接创建
  2. 处理链接请求

等待链接创建

Jetty中的ServerConnector接收到请求以后调用accepted

private void accepted(SocketChannel channel) throws IOException
{
    channel.configureBlocking(false); //将新的链接设置成非阻塞的
    Socket socket = channel.socket();
    configure(socket);
    _manager.accept(channel);
}
复制代码

_manager接收到请求以后,获取一个Selector,进而提交一个Accept,放到Deque队列中,Accept实现了SelectorUpdate

public void accept(SelectableChannel channel, Object attachment)
{
    final ManagedSelector selector = chooseSelector();
    selector.submit(selector.new Accept(channel, attachment));
}
复制代码

在从Deque中取值时,会执行它的 update方法

public void update(Selector selector)
    {
        ...
        //执行注册
        key = channel.register(selector, 0, attachment);
        //执行run方法
        execute(this);
       ...
    }
     
    public void run()
    {
        ...
        createEndPoint(channel, key);
        _selectorManager.onAccepted(channel);
        ...
    }
复制代码

建立的createEndPoint过程以下

private void createEndPoint(SelectableChannel channel, SelectionKey selectionKey) throws IOException
    {
        //注意_selectorManager自己在构造的时候是一个ServerConnectorManager
        //好比这里是一个SocketChannelEndPoint
        EndPoint endPoint = _selectorManager.newEndPoint(channel, this, selectionKey);
        //建立链接,好比默认的Http了链接
        Connection connection = _selectorManager.newConnection(channel, endPoint, selectionKey.attachment());
        endPoint.setConnection(connection);
        //创建链接的key是一个SocketChannelEndPoint
        selectionKey.attach(endPoint);
        endPoint.onOpen();
        _selectorManager.endPointOpened(endPoint);
        //实际做用是调用了httpConnection的onOpen方法
        _selectorManager.connectionOpened(connection);
        if (LOG.isDebugEnabled())
            LOG.debug("Created {}", endPoint);
    }
复制代码

ServerConnectorManager建立的newEndPoint为SocketChannelEndPoint

protected ChannelEndPoint newEndPoint(SocketChannel channel, ManagedSelector selectSet, SelectionKey key) throws IOException
    {
        SocketChannelEndPoint endpoint = new SocketChannelEndPoint(channel, selectSet, key, getScheduler());
        endpoint.setIdleTimeout(getIdleTimeout());
        return endpoint;
    }

复制代码

建立的链接则是根据链接工厂创建的,而默认的则是调用的HttpConnectionFactory

public Connection newConnection(SelectableChannel channel, EndPoint endpoint, Object attachment) throws IOException
{
    return getDefaultConnectionFactory().newConnection(ServerConnector.this, endpoint);
}
复制代码

建立一个HttpConnection对象

public HttpConnection(HttpConfiguration config, Connector connector, EndPoint endPoint, HttpCompliance compliance, boolean recordComplianceViolations)
    {
        //1. 父类构建了一个_readCallback回调,当成功的时候就会去执行子类的onFillable方法
        super(endPoint, connector.getExecutor());
        _config = config;
        _connector = connector;
        _bufferPool = _connector.getByteBufferPool();
        //用来构造HTTP的信息
        _generator = newHttpGenerator();
        //里头构建了一个Request对象,它实现了HttpServletRequest,同时构建了Response对象,实现了HttpServletRequest
        _channel = newHttpChannel();
        _input = _channel.getRequest().getHttpInput();
        _parser = newHttpParser(compliance);
        _recordHttpComplianceViolations = recordComplianceViolations;
        if (LOG.isDebugEnabled())
            LOG.debug("New HTTP Connection {}", this);
    }
复制代码

能够看到Request和Response是和channel绑定的,同一个TCP链接用的就是同一个Reqeust和Response,他们会循环的用

链接建立完成,调用open方法,它实际在将回调的_readCallback写入SocketChannelEndPoint的_fillInterest中

处理链接请求

Jetty中的EatWhatYouKill的produce方法,即用来处理请求,它核心是只要获取task并运行它

Runnable task = produceTask();
复制代码

这里的produceTask实际就是初始化的时候传入的SelectorProducer的方法

public Runnable produce()
    {
        while (true)
        {
            //若是_cursor中有值,也就是有链接过来了进行处理
            Runnable task = processSelected();
            if (task != null)
                return task;
            //遍历Deque,好比链接创建的时候会放入一个selector.new Accept,并执行update方法,具体分析见面下的Accept链接创建
            processUpdates();

            updateKeys();
            //执行Selector对应的select()方法,将放回的keys放入_cursor中存储
            if (!select())
                return null;
        }
    }
复制代码

当有请求过来的时候,也就是执行processSelected方法

//SelectorProducer
   private Runnable processSelected()
    {
    ...
        if (attachment instanceof Selectable)
        {
            // 当链接创建后会附上一个SocketChannelEndPoint,它实现了Selectable
            Runnable task = ((Selectable)attachment).onSelected();
            if (task != null)
                return task;
        }
    
    ...
    }
复制代码

对应的onSelect实如今ChannelEndPoint中

//ChannelEndPoint
 public Runnable onSelected()
    {
        int readyOps = _key.readyOps();
        ...
        //可读
        boolean fillable = (readyOps & SelectionKey.OP_READ) != 0;
        //可写
        boolean flushable = (readyOps & SelectionKey.OP_WRITE) != 0;
        ...
        //根据是可读仍是可写返回对应的任务
        Runnable task= fillable 
                ? (flushable 
                        ? _runCompleteWriteFillable 
                        : _runFillable)
                : (flushable 
                        ? _runCompleteWrite 
                        : null);
        ...
        return task;
    }
复制代码

到这里能够看到,EatWhatYouKill执行的实际上就是可读或者可写的一个channel任务。 以可读的为例,_runFillable的实现就是从getFillInterest获取的或吊执行它的fillable方法

public void run()
    {
        getFillInterest().fillable();
    }
复制代码

这对应到创建的HttpConnection,执行它的fillable方法,即调用这个链接的HttpChannel来处理

public void onFillable()
    {
            ....
            boolean suspended = !_channel.handle();
            ....
    }
复制代码

而处理的详情关键

public boolean handle()
{
 ...
  getServer().handle(this);
  ...
 }
复制代码

这里就对应处理到Server的handle实现

public void handle(HttpChannel channel) throws IOException, ServletException
    {
        final String target=channel.getRequest().getPathInfo();
        //获取channel上的Request
        final Request request=channel.getRequest();
        //获取channel上的Response
        final Response response=channel.getResponse();
        ...
        if (HttpMethod.OPTIONS.is(request.getMethod()) || "*".equals(target))
        {
            if (!HttpMethod.OPTIONS.is(request.getMethod()))
                response.sendError(HttpStatus.BAD_REQUEST_400);
            handleOptions(request,response);
            if (!request.isHandled())
                handle(target, request, request, response);
        }
        else
            handle(target, request, request, response);
       ...
    }

复制代码

对于web项目会有ServletHanlder,对应实现为

@Override
    public void doHandle(String target, Request baseRequest,HttpServletRequest request, HttpServletResponse response)
        throws IOException, ServletException
    {
        ....
        if (chain!=null)
            chain.doFilter(req, res);
        else
            servlet_holder.handle(baseRequest,req,res);
        ...
    }
复制代码

这里对应找到ServletHolder的handler方法

public void handle(Request baseRequest,
                       ServletRequest request,
                       ServletResponse response)
        throws ServletException,
               UnavailableException,
               IOException
    {
        //获取Servlet
        Servlet servlet = getServlet();
            if (baseRequest.isAsyncSupported() && !isAsyncSupported())
            {
                try
                {
                    baseRequest.setAsyncSupported(false,this.toString());
                    servlet.service(request,response);
                }
                finally
                {
                    baseRequest.setAsyncSupported(true,null);
                }
            }
            else
                //执行Servlet的service方法,并传入request和response
                servlet.service(request,response);
    }
复制代码

源码参考

至此对于Spring来讲已经连上了DispatcherServlet父类FrameWorkService的service方法,而后转置DispatcherServlet的doService方法。

总结

Jetty自己去链接了客户端,自身去实现了Servlet的规范,在每一个创建的channel上,本身实现了请求request和response,经由handler,对获取的web.xml配置中的servlet,关联上Spring的对应servlet的init和service方法来处理请求

相关文章
相关标签/搜索