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

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

内容简介:对于一个请求来讲,如果只是需要一个静态页面,可以直接在服务器上根据路径访问得到,但是如果请求的数据是一个动态页面,即只有在运行时从后台数据库获取,再拼装东西返回,然后生成一个对应的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的衔接源码分析》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

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

PHP for the World Wide Web, Second Edition (Visual QuickStart Gu

PHP for the World Wide Web, Second Edition (Visual QuickStart Gu

Larry Ullman / Peachpit Press / 2004-02-02 / USD 29.99

So you know HTML, even JavaScript, but the idea of learning an actual programming language like PHP terrifies you? Well, stop quaking and get going with this easy task-based guide! Aimed at beginning ......一起来看看 《PHP for the World Wide Web, Second Edition (Visual QuickStart Gu》 这本书的介绍吧!

SHA 加密
SHA 加密

SHA 加密工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具