内容简介:对于一个请求来讲,如果只是需要一个静态页面,可以直接在服务器上根据路径访问得到,但是如果请求的数据是一个动态页面,即只有在运行时从后台数据库获取,再拼装东西返回,然后生成一个对应的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的总体架构也就可以看出来它很好的实践了这些
Connector负责连接,Handler则处理对应的请求,交给Servlet来处理
Servlet的生命周期
Servlet的生命周期是由发布它的容器控制的,比如Jetty,当要把请求映射到一个Servlet上时,容器一般会做如下的事情:
- 如果Servlet不存在,就加载Servlet类,创建Servlet实例,然后调用Servlet的init方法
- 执行Servlet的service方法,传递request和response对象
- 如果容器要移除掉servlet,它就会调用Servlet的destroy方法
javax.servlet
和 javax.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项目分析可得到,网络请求分成两部分
- 等待连接建立
- 处理连接请求
等待连接建立
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的衔接源码分析》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 漂亮~pandas可以无缝衔接Bokeh
- 【源码阅读】AndPermission源码阅读
- ReactNative源码解析-初识源码
- 【源码阅读】Gson源码阅读
- Spring源码系列:BeanDefinition源码解析
- istio 源码 – Citadel 源码分析 (原创)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
我是一只IT小小鸟
胡江堂、李成、唐雅薇、秦琴、蒋宇东、刘未鹏、居振梁、刘帅、温卫斌、张弦、张凯峰、庄表伟、宋劲杉、程露、黄小明、易晓东、简朝阳、林健、高昂、徐宥、辜新星 / 电子工业出版社 / 2009 / 29.80
一群IT小小鸟—— 来自十几所院校,或男生,或女生;或科班,或半路转行。 分布在不同的公司,或外企,或国企,或民企,老板有土有洋。 有失意,有快意;有泪水,有欢笑。在失望中追求希望,在迷茫中辨别方向。 他们用自己的成长故事,告诉在校的师弟师妹们: 青春太宝贵,千万别浪费;要想不浪费,万事早准备。一起来看看 《我是一只IT小小鸟》 这本书的介绍吧!