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

栏目: Java · 发布时间: 6年前

内容简介:对于一个请求来讲,如果只是需要一个静态页面,可以直接在服务器上根据路径访问得到,但是如果请求的数据是一个动态页面,即只有在运行时从后台数据库获取,再拼装东西返回,然后生成一个对应的html文件。在Java中为了实现这个功能,使用的就是Servlet规范。Servlet:server component,运行在服务器上的java代码Servlet并不处理任何的协议和连接等等动作,它只是约定了一个种处理request-response的模式。每个功能实现Servlet接口的类都可以用来处理请求,比如加法用1个

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

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

Servlet容器

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

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

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

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方法进行了细分,这样可以更好的去处理想要处理的请求

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项目分析可得到,网络请求分成两部分

  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方法来处理请求


以上所述就是小编给大家介绍的《jetty、servlet以及spring的衔接源码分析》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

我是一只IT小小鸟

我是一只IT小小鸟

胡江堂、李成、唐雅薇、秦琴、蒋宇东、刘未鹏、居振梁、刘帅、温卫斌、张弦、张凯峰、庄表伟、宋劲杉、程露、黄小明、易晓东、简朝阳、林健、高昂、徐宥、辜新星 / 电子工业出版社 / 2009 / 29.80

一群IT小小鸟—— 来自十几所院校,或男生,或女生;或科班,或半路转行。 分布在不同的公司,或外企,或国企,或民企,老板有土有洋。 有失意,有快意;有泪水,有欢笑。在失望中追求希望,在迷茫中辨别方向。 他们用自己的成长故事,告诉在校的师弟师妹们: 青春太宝贵,千万别浪费;要想不浪费,万事早准备。一起来看看 《我是一只IT小小鸟》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码