常用的web基本都是用xml配置的,而在tomcat解析web.xml的过程中知道,如果类实现了ServletContainerInitializer接口的话,就可以不用在xml里面配置了.
现在对下面两种方式进行说明:
首先是xml配置,web.xml的代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<servlet>
<servlet-name>testservlet</servlet-name>
<servlet-class>com.yluo.TestServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>testservlet</servlet-name>
<url-pattern>/testservlet</url-pattern>
</servlet-mapping>
</web-app>
这个配置大家都很熟悉的吧.
下面看到实现了ServletContainerInitializer接口的配置,代码如下:
package com.yluo.config;
import com.yluo.servlet.TestServlet;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import java.util.Set;
/**
* Created by 樱天寻 on 2016/7/13.
*/
public class WebConfig implements ServletContainerInitializer {
@Override
public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
ServletRegistration.Dynamic dynamic = servletContext.addServlet("testServler", TestServlet.class);
dynamic.setLoadOnStartup(1);
dynamic.addMapping("/testServler");
}
}
不过在这里有点不一样的就是要添加下面这么一个文件
WEB-INF/classes/META-INF/services/javax.servlet.ServletContainerInitializer文件的内容如下:
com.yluo.config.WebConfig
就是把实现ServletContainerInitializer接口的类填上.这样就不用配置xml了,是不是很方便呢.
下面分析异步servlet.示例代码如下:
@Override
protected void doGet(HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
final AsyncContext asyncContext = req.startAsync();
new Thread(new Runnable() {
@Override
public void run() {
try {
resp.getWriter().write("hello world!");
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
异步的话一般都是在新的线程的进行的,这样才不会把任务阻塞在tomcat内部的哪个线程池里面.开启异步之后改方法就返回了,tomcat做了哪些工作呢.在上一章中有说到,请求路径的匹配是在org.apache.coyote.http11.Http11Processor类的service里面的,那么看到该方法,部分代码如下:
@Override
public SocketState service(SocketWrapperBase<?> socketWrapper)
throws IOException {
......
getAdapter().service(request, response); //
......
if (!isAsync()) {
// 不开启异步的正常关闭
endRequest();
}
......
if (getErrorState().isError() || endpoint.isPaused()) {
return SocketState.CLOSED;
} else if (isAsync()) {
// 异步就返回SocketState.LONG;
return SocketState.LONG;
}
......
}
在servlet的doGet方法返回之后,就是回到该方法判是不是异步了,接着如果是异步的话service函数就返回 SocketState.LONG 继续往前看,看到org.apache.coyote.AbstractProtocol类的process方法,部分代码如下:
@Override
public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
......
state = processor.process(wrapper, status);
try{
if (state == SocketState.LONG) {
longPoll(wrapper, processor); // 第一次连接的时候就是在这里处理了
if (processor.isAsync()) { // 返回的还是这里异步的
// 添加到定时器里面定时检测是偶过期
getProtocol().addWaitingProcessor(processor);
}
}
......
return state; // 返回LONG
} catch(java.net.SocketException e) {
......
}
......
}
processor.process发挥的state值为SocketState.LONG,在这里会判断是否开启了异步,如果开始了执行addWaitingProcessor方法,部分代码如下:
public void addWaitingProcessor(Processor processor) {
waitingProcessors.add(processor);
}
而waitingProcessors是一个Set集合
private final Set<Processor> waitingProcessors =
Collections.newSetFromMap(new ConcurrentHashMap<Processor, Boolean>());
加进来有什么用呢,看到AbstractProtocol类的子类AsyncTimeout的run方法的代码如下:
@Override
public void run() {
while (asyncTimeoutRunning) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
long now = System.currentTimeMillis();
for (Processor processor : waitingProcessors) {
processor.timeoutAsync(now);
}
while (endpoint.isPaused() && asyncTimeoutRunning) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// Ignore
}
}
}
}
从该方法可以知道,tomcat会一个子线程里面每隔一秒检测异步请求有没有结束或者超时.
O啦,tomcat的源码就分析到这里了. 第一次写博客分析源码,有很多不足之处,希望大家见谅吧. 后面就是分析netty的源码了. 如果大家有什么问题可以给我留言哈.