《看透SpringMVC源碼分析與實踐》
引用 https://yq.aliyun.com/articles/20177
Connector用于接收請求并将請求封裝成Request和Response來具體處理,最底層是使用Socket協定來進行連接配接的,在封裝完成之後交給Container進行處理(即Servlet容器),Container處理完畢之後傳回給Connector,最終Connector使用Socket将處理結果傳回給用戶端。這樣整個請求就處理完成了。
Connector中具體是用
ProtocolHandler
來處理請求的,不同的ProtocolHandler代表不同的連接配接類型:
三種HTTP協定,用戶端與Tomcat伺服器直接連接配接。
-
: BIO,支援http1.1協定Http11Protocol
-
: NIO,支援http1.1協定Http11NioProtocol
-
: ARP(Apache portable runtime),支援http1.1協定 Tomcat 7.0.30版本開始,預設使用此方式。Http11AprProtocol
以及三種帶AJP13是定向包協定.
-
: BIO,支援AJP協定AjpProtocol
-
,NIO,支援AJP協定AjpNioProtocol
-
,Apr ,支援AJP協定AjpAprProtocol
什麼是AJP
AJPv13協定是面向資料包的。WEB伺服器和Servlet容器通過TCP連接配接來互動;為了節省SOCKET建立的昂貴代價,WEB伺服器會嘗試維護一個
到servlet容器,并且在多個請求和響應周期過程會重用連接配接。
永久TCP連接配接(長連接配接)
AJP就是Apache和Tomcat之間的通訊基本有三種方式之一,另外常見的還有`Mod_Jk、HTTP_Proxy`協定. ![這裡寫圖檔描述](https://img-blog.csdn.net/20180815215028689?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2l0X2ZyZXNobWFu/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70) 引用:http://blog.chinaunix.net/uid-20662363-id-3012760.html
- Apache是世界排名第一的Web伺服器,它支援處理靜态頁面,Apache可以支援PHP。
- Tomcat支援動态頁面(servlet, JSP),對靜态頁面的處理不太理想。
- Tomcat能夠承擔性能要求較低的WEB應用。
- 通常會使用Apache + Tomcat的配置下,Apache處理靜态頁面,對于JSP則轉發交給Tomcat來處理。
ProtocolHandler有三個非常重要的元件:Endpoint,Processor和Adapter.
-
: 用于處理底層Socket的網絡協定。 Endpoint的抽象實作類Endpoint
中定義了Acceptor,AsyncTimeout兩個内部類和一個接口HandlerAbstractEndpoint
-
用于監聽請求。Acceptor:
-
用于異步檢查request逾時。AsyncTimeout:
-
用于處理接收到的Socket,在内部調用了Handler:
Processor
-
-
: 用于将Endpoint接收到的Socket封裝成RequestProcessor
-
: 用于将封裝好的Request交給Containner進行具體處理。Adapter
Connector處理請求過程
如下圖:
源碼分析
執行個體化##
Connector執行個體化
Connector是在Catalina的load方法中,執行
createStartDigester()
解析
server.xml
配置檔案的配置資訊所建立的。
server.xml
-part
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000" redirectPort="8443" />
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
java建立-流程
//org.apache.catalina.startup.Catalina.createStartDigester()
digester.addRule("Server/Service/Connector", new ConnectorCreateRule());
//org.apache.catalina.startup.ConnectorCreateRule.begin()
Connector con = new Connector(attributes.getValue("protocol"));
可見最終Connector的建立流程,執行到了Connector的構造函數中。
//org.apache.catalina.Connector
public Connector(String protocol) {
// 将server.xml中配置的protocol “HTTP/1.1”,“AJP/1.3” 轉換為上文中的6種protocol;
setProtocol(protocol);
Class<?> clazz = Class.forName(protocolHandlerClassName);
//調用具體protocol的無參構造函數,執行個體化。
this.protocolHandler = (ProtocolHandler) clazz.newInstance();
}
public void setProtocol(String protocol) {
//若支援apr預設為:xxxAprProtocol
if (AprLifecycleListener.isAprAvailable()) {
if ("HTTP/1.1".equals(protocol)) {
setProtocolHandlerClassName
("org.apache.coyote.http11.Http11AprProtocol");
} else if ("AJP/1.3".equals(protocol)) {
setProtocolHandlerClassName
("org.apache.coyote.ajp.AjpAprProtocol");
} else if (protocol != null) {
setProtocolHandlerClassName(protocol);
} else {
setProtocolHandlerClassName
("org.apache.coyote.http11.Http11AprProtocol");
}
} else {
//否則預設為:Http11Protocol 或者 AjpProtocol
if ("HTTP/1.1".equals(protocol)) {
setProtocolHandlerClassName
("org.apache.coyote.http11.Http11Protocol");
} else if ("AJP/1.3".equals(protocol)) {
setProtocolHandlerClassName
("org.apache.coyote.ajp.AjpProtocol");
} else if (protocol != null) {
setProtocolHandlerClassName(protocol);
}
}
}
在構造函數中,最主要的就是指定ProtocolHandler的類型,并通過執行newInstance方法,建立具體的ProtocolHandler執行個體。
ProtocolHandler執行個體化
ProtocolHandler類圖,如下:
從上圖可以看到,ProtocolHandler有一個抽象實作類
AbstractProtocol
,AbstractProtocol又有
AbstractAjpProtocol
和
AbstractHttp11Protocol
抽象子類,以及本文開局提到的最終的六種ProtocolHandler子類。
本文以**
Http11NioProtocol
**為例,分析ProtocolHandler。
//org.apache.coyote.http11.Http11NioProtocol
public Http11NioProtocol() {
// 建立NioEndpoint
endpoint=new NioEndpoint();
//建立Http11ConnectionHandler
cHandler = new Http11ConnectionHandler(this);
//指定NioEndpoint的handler,
((NioEndpoint) endpoint).setHandler(cHandler);
setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
}
構造函數中,最主要是NioEndpoint類型的Endpoint,Http11ConnectionHandler類型的Handler并将其設定到Endpoint中。
Endpoint
Endpoint用于處理具體的連接配接和傳輸資料,
AbstractEndpoint
是所有Endpoint的父類,類圖如下:
其中NioEndpoint中處理請求的具體流程如下,
Poller和SocketProcessor
是NioEndpoint的内部類,後續會講到。
初始化##
整個Connector的初始化過程流程見下圖:
Connector初始化
根據之前的經驗,Connector的生命周期方法是在Service中調用的,首先調用生命周期LifecycleBase.init(),然後再調用Connector.initInternal()。
//org.apache.catalina.Connector
protected void initInternal() throws LifecycleException {
super.initInternal();
//建立新的Adapter,并設定到protocolHandler,
//Adapter的主要作用是通過自身的service方法調用Container管道中的invoke方法來處理強求
adapter = new CoyoteAdapter(this);
//将Adapter設定到protocolHandler中
protocolHandler.setAdapter(adapter);
//省略部分代碼....
//初始化protocolHandler
protocolHandler.init();
//初始化mapperListener
mapperListener.init();
}
ProtocolHandler初始化
本文的ProtocolHandler預設使用
Http11NioProtocol
,Http11NioProtocol的初始化邏輯是調用
AbstractProtocol.init() --> Endpoint.init()
:
Endpoint初始化
Http11NioProtocol中的Endpoint類型為:
NioEndpoint
,在ProtocolHandler的初始化過程中會調用NioEndpoint初始化方法, 而NioEndpoint的初始化邏輯是調用
AbstractEndpoint.init() -->NioEndpoint.bind():
//org.apache.tomcat.util.net.NioEndpoint
public void bind() throws Exception {
//初始化ServerSocketChannel
serverSock = ServerSocketChannel.open();
socketProperties.setProperties(serverSock.socket());
InetSocketAddress addr = (getAddress()!=null?new InetSocketAddress(getAddress(),getPort()):new InetSocketAddress(getPort()));
serverSock.socket().bind(addr,getBacklog());
serverSock.configureBlocking(true); //mimic APR behavior
serverSock.socket().setSoTimeout(getSocketProperties().getSoTimeout());
//需要啟動acceptor線程的個數,如果是0,則設定為1
if (acceptorThreadCount == 0) {
acceptorThreadCount = 1;
}
//需要設定poller線程個數
if (pollerThreadCount <= 0) {
pollerThreadCount = 1;
}
//省略ssl....
if (oomParachute>0) reclaimParachute(true);
//建立一個NioBlockingSelector
selectorPool.open();
}
啟動
啟動流程如下圖:
Endpoint啟動
//org.apache.tomcat.util.net.NioEndpoint
public void startInternal() throws Exception {
if (!running) {
running = true;
paused = false;
// 建立工作線程池
if ( getExecutor() == null ) {
createExecutor();
}
initializeConnectionLatch();
// 啟動Poller線程,用來處理Acceptor傳遞來的socket請求。
pollers = new Poller[getPollerThreadCount()];
for (int i=0; i<pollers.length; i++) {
pollers[i] = new Poller();
Thread pollerThread = new Thread(pollers[i], getName() + "-ClientPoller-"+i);
pollerThread.setPriority(threadPriority);
pollerThread.setDaemon(true);
pollerThread.start();
}
//啟動Acceptor線程,用來監聽接收請求;
startAcceptorThreads();
}
}
這裡首先初始化了一些屬性,然後啟動了Poller和Acceptor線程,通過這些線程或線程組用來監聽請求并處理請求。
MapperListener啟動
//org.apache.catalina.connector.MapperListener
public void startInternal() throws LifecycleException {
Engine engine = (Engine) connector.getService().getContainer();
addListeners(engine);
Container[] conHosts = engine.findChildren();
for (Container conHost : conHosts) {
Host host = (Host) conHost;
if (!LifecycleState.NEW.equals(host.getState())) {
//注冊host至mapper,同時遞歸的将所有的context,wrapper注冊
registerHost(host);
}
}
}
MapperListener啟動,将所有的Context,以及Wrapper都注冊值mapper屬性中,供後續的請求uri比對。
HTTP請求處理
1.Acceptor
org.apache.tomcat.util.net.NioEndpoint.Acceptor
是NioEndpoint的一個内部類,它用來接收請求:
//org.apache.tomcat.util.net.NioEndpoint.Acceptor
protected class Acceptor extends AbstractEndpoint.Acceptor {
@Override
public void run() {
int errorDelay = 0;
while (running) {
//省略其他判斷.....
SocketChannel socket = serverSock.accept();
//将socket注冊到Poller中的selector中。
if (!setSocketOptions(socket)) {
countDownConnection();
closeSocket(socket);
}
}
}
}
setSocketOptions()
// org.apache.tomcat.util.net.NioEndpoint
protected boolean setSocketOptions(SocketChannel socket) {
//設定socket非阻塞。
socket.configureBlocking(false);
Socket sock = socket.socket();
// 設定socket熟悉,如keepAlive,timeout 等資訊
socketProperties.setProperties(sock);
// channel 包含
NioChannel channel = nioChannels.poll();
NioBufferHandler bufhandler = new NioBufferHandler(
socketProperties.getAppReadBufSize(),
socketProperties.getAppWriteBufSize(),
socketProperties.getDirectBuffer());
channel = new NioChannel(socket, bufhandler);
channel.setIOChannel(socket);
channel.reset();
// 将chanel注冊到Poller線程
getPoller0().register(channel);
}
2.Poller
org.apache.tomcat.util.net.NioEndpoint.Poller
也是NioEndpoint的一個内部類:
//org.apache.tomcat.util.net.NioEndpoint.Poller
public class Poller implements Runnable {
public Poller() throws IOException {
synchronized (Selector.class) {
//擷取目前線程的selector,不是bind方法中selectorPool.open();的selector
this.selector = Selector.open();
}
}
public void register(final NioChannel socket) {
socket.setPoller(this);
KeyAttachment key = keyCache.poll();
/*
* 建立一個新的KeyAttachment,入參是socket
* KeyAttachment的類結構繼承SocketWrapper<NioChannel>
*/
final KeyAttachment ka = key!=null?key:new KeyAttachment(socket);
ka.reset(this,socket,getSocketProperties().getSoTimeout());
//關注read事件
ka.interestOps(SelectionKey.OP_READ);
PollerEvent r = eventCache.poll();
if ( r==null) {
//PollerEvent作用:将SocketChannel注冊到selector中
r = new PollerEvent(socket,ka,OP_REGISTER);
} else {
r.reset(socket,ka,OP_REGISTER);
}
//添加event
addEvent(r);
}
@Override
public void run() {
//省略其他.....
keyCount = selector.selectNow();
if ( keyCount == 0 ) hasEvents = (hasEvents | events());
//輪詢準備就緒的Key
Iterator<SelectionKey> iterator = keyCount > 0 ? selector.selectedKeys().iterator() : null;
while (iterator != null && iterator.hasNext()) {
SelectionKey sk = iterator.next(); //阻塞...
KeyAttachment attachment = (KeyAttachment)sk.attachment();
if (attachment == null) {
iterator.remove();
} else {
attachment.access();
iterator.remove();
//調用processKey 處理socket
processKey(sk, attachment);
}
}
}
//處理SelectionKey,使用read,write方法等。
protected boolean processKey(SelectionKey sk, KeyAttachment attachment) {
unreg(sk, attachment, sk.readyOps());
boolean closeSocket = false;
if (sk.isReadable()) {
//調用processSocket處理socket
if (!processSocket(channel, SocketStatus.OPEN_READ, true)) {
closeSocket = true;
}
}
if (!closeSocket && sk.isWritable()) {
if (!processSocket(channel, SocketStatus.OPEN_WRITE, true)) {
closeSocket = true;
}
}
if (closeSocket) {
cancelledKey(sk,SocketStatus.DISCONNECT,false);
}
}
}
,上述代碼的作用
- 處理Acceptor接收到NioChannel并注冊到
selector
- 然後調用後續的
方法,該方法用來處理NioChannel的read,write方法等。processKey()
- 上述方法内部又調用了
來處理,在本方法内部主要是将請求轉交給Endpoint.processSocket()
SocketProcessor
//org.apache.tomcat.util.net.NioEndpoint
public boolean processSocket(NioChannel socket, SocketStatus status, boolean dispatch) {
KeyAttachment attachment = (KeyAttachment)socket.getAttachment(false);
SocketProcessor sc = processorCache.poll();
//構造SocketProcessor
if ( sc == null ) {
sc = new SocketProcessor(socket,status);
} else {
sc.reset(socket,status);
}
if ( dispatch && getExecutor()!=null ) {
//通過線程池處理SocketProcessor
getExecutor().execute(sc);
}else {
//調用SocketProcessor.run()處理請求
sc.run();
}
}
4.SocketProcessor
protected class SocketProcessor implements Runnable {
protected NioChannel socket = null;
protected SocketStatus status = null;
public SocketProcessor(NioChannel socket, SocketStatus status) {
reset(socket,status);
}
public void reset(NioChannel socket, SocketStatus status) {
this.socket = socket;
this.status = status;
}
public void run() {
SelectionKey key = socket.getIOChannel().keyFor(
socket.getPoller().getSelector());
KeyAttachment ka = (KeyAttachment)key.attachment();
//省略其他....
doRun(key, ka);
}
private void doRun(SelectionKey key, KeyAttachment ka) {
SocketState state = SocketState.OPEN;
/*
* 調用handler處理請求,
* 此時的handler為在Http11NioProtocol構造函數中設定的Http11ConnectionHandler(this)
*/
state = handler.process(ka, SocketStatus.OPEN_READ);
}
}
SocketProcessor處理請求,将請求交給
Http11ConnectionHandler
處理。
5.Handler
Hander的實際類型為Http11ConnectionHandler,它的類圖如下:
process()
方法最終在
AbstractProtocol.AbstractConnectionHandler
中實作。
//org.apache.coyote.AbstractProtocol.AbstractConnectionHandler
public SocketState process(SocketWrapper<S> wrapper,
SocketStatus status) {
S socket = wrapper.getSocket();
Processor<S> processor = connections.get(socket);
if (processor == null) {
//擷取processor執行個體
processor = createProcessor();
}
//調用processor.process()
SocketState state = processor.process(wrapper);
//省略其他...
return SocketState.CLOSED;
}
,方法的主要邏輯是
- 擷取建立Processor,此時示例類型是
Http11NioProcessor
- 然後調用Processor處理請求
6.Processor
通過
Http11NioProtocol.createProcessor()
擷取的Processor,代碼如下:
//org.apache.coyote.http11.Http11NioProtocol
public Http11NioProcessor createProcessor() {
Http11NioProcessor processor = new Http11NioProcessor(
proto.getMaxHttpHeaderSize(), (NioEndpoint)proto.endpoint,
proto.getMaxTrailerSize(), proto.getMaxExtensionSize());
processor.setAdapter(proto.adapter);
processor.setMaxKeepAliveRequests(proto.getMaxKeepAliveRequests());
processor.setKeepAliveTimeout(proto.getKeepAliveTimeout());
//省略其他一些屬性配置......
register(processor);
return processor;
}
,由上述代碼可知Processor的具體類型是
Http11NioProcessor
,
processor.process()
方法處理請求邏輯來自它的父類
AbstractHttp11Proccessor
:
//org.apache.coyote.http11.AbstractHttp11Processor
public SocketState process(SocketWrapper<S> socketWrapper)
throws IOException {
RequestInfo rp = request.getRequestProcessor();
rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
// Setting up the I/O
setSocketWrapper(socketWrapper);
getInputBuffer().init(socketWrapper, endpoint);
getOutputBuffer().init(socketWrapper, endpoint);
if (!error) {
rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
// 預處理,設定解析請求頭,URI,METHOD等等....
prepareRequest();
}
if (!error) {
rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
//調用Adapter執行請求
adapter.service(request, response);
}
}
在這個方法裡面,最終請求交給Adapter處理,
注意可以看到此時請求的參數是org.apache.coyote.Request,org.apache.coyote.Response
7.Adapter
Tomcat7中Adapter隻有一個實作類
CoyoteAdapter
,處理邏輯如下:
//org.apache.catalina.connector.CoyoteAdapter
public void service(org.apache.coyote.Request req,
org.apache.coyote.Response res){
/*
* 将org.apache.coyote.Request , Response
* 轉換為org.apache.catalina.connector.Request , Response
*/
Request request = (Request) req.getNote(ADAPTER_NOTES);
Response response = (Response) res.getNote(ADAPTER_NOTES);
// postParseRequest:設定scheme,serverName,port,sessionId等
boolean postParseSuccess = postParseRequest(req, request, res, response);
if (postParseSuccess) {
//交給container處理請求
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
}
//判斷目前請求類型
AsyncContextImpl asyncConImpl = (AsyncContextImpl)request.getAsyncContext();
if (asyncConImpl != null) {
async = true;
} else if (!comet) {
// 如果是非異步請求,則結束請求
request.finishRequest();
response.finishResponse();
if (postParseSuccess && request.getMappingData().context != null) {
((Context) request.getMappingData().context).logAccess( request, response,System.currentTimeMillis() - req.getStartTime(), false);
}
req.action(ActionCode.POST_REQUEST , null);
}
}
service方法的作用,主要是完成如下2個任務:
- 預處理請求,通過request URI的資訊找到屬于自己的Context和Wrapper
- 調用container處理請求。
1).postParseRequest()
a.解析,host,context,wrapper,session等資訊。
//org.apache.catalina.connector.CoyoteAdapter
protected boolean postParseRequest(org.apache.coyote.Request req,
Request request,
org.apache.coyote.Response res,
Response response) {
String proxyName = connector.getProxyName();
int proxyPort = connector.getProxyPort();
if (proxyPort != 0) {
req.setServerPort(proxyPort);
}
if (proxyName != null) {
req.serverName().setString(proxyName);
}
//該方法主要作用是解析url中解析出來參數,并放置到request參數清單裡面。
parsePathParameters(req, request);
//略...
boolean mapRequired = true;
while (mapRequired) {
// 将host,context,wrapper等屬性解析存放至 request.getMappingData()
connector.getMapper().map(serverName, decodedURI, version,request.getMappingData());
//設定比對的context,wrapper
request.setContext((Context) request.getMappingData().context);
request.setWrapper((Wrapper) request.getMappingData().wrapper);
//略....
String sessionID = null;
if (request.getServletContext().getEffectiveSessionTrackingModes().contains(SessionTrackingMode.URL)) {
//從url參數中設定的sessionId
//形如:protocol://host:port/path/servlet.do;jsessionid=xxxxx?username=trump 的url中包含sessionId
//url參數清單中session的key為 "jsessionid",定義在org.apache.catalina.util.SessionConfig.DEFAULT_SESSION_PARAMETER_NAME
sessionID = request.getPathParameter(SessionConfig.getSessionUriParamName(request.getContext()));
if (sessionID != null) {
//設定sessionId,且來自URL
request.setRequestedSessionId(sessionID);
request.setRequestedSessionURL(true);
}
}
//從Cookie中尋找SessionId,可能會覆寫浏覽器中的sessionId
parseSessionCookiesId(req, request);
parseSessionSslId(request);
//略.....
}
}
//從cookie中解析SessionId
protected void parseSessionCookiesId(org.apache.coyote.Request req, Request request) {
//如果已禁用目前通過cookie的會話跟蹤上下文,不要在cookie中查找cookie中的SessionID
Context context = (Context) request.getMappingData().context;
if (context != null && !context.getServletContext().getEffectiveSessionTrackingModes().contains(SessionTrackingMode.COOKIE)) {
return;
}
// Parse session id from cookies
Cookies serverCookies = req.getCookies();
int count = serverCookies.getCookieCount();
if (count <= 0) {
return;
}
//預設值為 "JSESSIONID"
String sessionCookieName = SessionConfig.getSessionCookieName(context);
for (int i = 0; i < count; i++) {
ServerCookie scookie = serverCookies.getCookie(i);
//周遊cookie
if (scookie.getName().equals(sessionCookieName)) {
//“requestedSessionId”當為空時進入if,否則進入else
if (!request.isRequestedSessionIdFromCookie()) {
//僅接受第一個來自cookie的SessionID
convertMB(scookie.getValue());
//設定SessionId,内部邏輯為給“requestedSessionId”指派
request.setRequestedSessionId (scookie.getValue().toString());
//session來自cookie設定true, 來自url設為false
request.setRequestedSessionCookie(true);
request.setRequestedSessionURL(false);
} else {
//判斷session是否過期
if (!request.isRequestedSessionIdValid()) {
//替換sessionId,直到sessionId有效為止
convertMB(scookie.getValue());
request.setRequestedSessionId(scookie.getValue().toString());
}
}
}
}
}
b.servlet擷取session的過程
參考Tomcat中session的管理機制
2).Container處理請求
Containner處理請求的流程如下圖:
-
:尋找host,并交由host.getPipeline()處理請求StandardEngineValve.invoke()
-
:設定context,并設定StandardHostValve.invoke()
,最終交由context.getPipeline()處理請求context.getLoader().getClassLoader()
- `StandardContextValve.invoke()``:尋找wrapper,并交由wrapper.getPipeline()處理請求
-
:通過wrapper定位初&始化、StandardWrapperValve.invoke()
,并執行FilterChain執行過程中調用javax.servlet.Servlet
servlet.service()方法
//org.apache.catalina.core.StandardWrapperValve
public final void invoke(Request request, Response response)
throws IOException, ServletException {
StandardWrapper wrapper = (StandardWrapper) getContainer();
Servlet servlet = null;
Context context = (Context) wrapper.getParent();
//定位Servlet,并執行servlet.init()
servlet = wrapper.allocate();
//建立本次請求的filterChain
ApplicationFilterFactory factory = ApplicationFilterFactory.getInstance();
ApplicationFilterChain filterChain = factory.createFilterChain(request, wrapper, servlet);
//省略其他....
//調用filterChain,并在此方法内部執行service.service()
filterChain.doFilter(request.getRequest(), response.getResponse());
}
//org.apache.catalina.core.ApplicationFilterChain
private void internalDoFilter(ServletRequest request,
ServletResponse response)
throws IOException, ServletException {
ApplicationFilterConfig filterConfig = filters[pos++];
Filter filter = null;
filter = filterConfig.getFilter();
// 省略其他.....
servlet.service(request, response);
}