Servlet
对于一个请求来讲,如果只是需要一个静态页面,可以直接在服务器上根据路径访问得到,但是如果请求的数据是一个动态页面,即只有在运行时从后台数据库获取,再拼装东西返回,然后生成一个对应的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方法来处理请求