Servlet规范定义了一个API标准,这一标准的实现通常称为Servlet容器,比如开源的Tomcat、JBoss。容器提供一些基本的服务,使得开发者专注于业务领域。当一个请求到达时,监听在相应端口的容器会接受该请求,将请求的信息转化成一个ServletRequest对象,同时创建一个ServletResponse对象,我们在开发Servlet应用时,就可以直接使用这两个对象(容器传递到service()方法的参数)。因此,所谓容器(服务器、中间件等),就是提供一些底层的、业务无关的基本功能,为真正的Servlet提供服务。
例如,在Http请求到达的时候,请求是一个标准的Http请求,包括请求方法、URI、协议、客户端环境参数、请求参数等等。容器通过Socket接收这些信息,并将其转化成特定于Servlet的ServletRequest对象。我们无需关注底层的网络Socket细节就可以直接使用。同样,容器也创建好一个ServletResponse,它帮我们设置了一些返回操作的基本内容,比如用于写入的OutputStream,容器已经把这个输出流定向到客户端的机器上,因此我们操作ServletResponse对象时,是在容器提供的服务至上的。
容器还负责根据请求的信息找到对应的Servlet,传递Request和Response参数,调用Servlet的service方法,完成请求的响应。
接下来我们事先一个简单的、最基本的容器:
时序图如下:
下面给出源码:
SimpleContainer:
package simpleserver.simplecontainer;
import static simpleserver.simplecontainer.Constant.HOSTNAME;
import static simpleserver.simplecontainer.Constant.LOG_BAKC;
import static simpleserver.simplecontainer.Constant.PORT;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class SimpleContainer {
private boolean shutdown = false;
private Log log = LogFactory.getLog(this.getClass());
public static void main(String[] args) {
SimpleContainer server = new SimpleContainer();
server.start();
}
private void start() {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(PORT, LOG_BAKC,
InetAddress.getByName(HOSTNAME));
} catch (IOException e) {
System.out.println("Server starts failed");
e.printStackTrace();
System.exit(1);
}
log.info("Server starts successfully.");
service(serverSocket);
}
private void service(ServerSocket serverSocket) {
while (!shutdown) {
try {
processRequest(serverSocket);
} catch (Exception e) {
e.printStackTrace();
}
}
}
private void processRequest(ServerSocket serverSocket) throws IOException {
log.info("waitting for incoming request ... ");
Socket socket = serverSocket.accept();
log.info("receive a request from "
+ socket.getRemoteSocketAddress().toString());
InputStream in = socket.getInputStream();
BaseRequest request = new BaseRequest(in);
log.info("Request Object ready!");
OutputStream out = socket.getOutputStream();
BaseResponse response = new BaseResponse(out);
ServletProcessor.processServletRequest(request, response);
socket.close();
}
}
BaseRequest:只给出部分实现,其他都是ServletRequest的空实现
package simpleserver.simplecontainer;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Map;
import javax.servlet.AsyncContext;
import javax.servlet.DispatcherType;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class BaseRequest implements ServletRequest {
private InputStream inputStream;
private String uri;
private StringBuffer requestContentBuffer = new StringBuffer(2048);
public BaseRequest(InputStream in) {
this.inputStream = in;
prepareContent();
parseAndSetUri();
}
private void prepareContent() {
byte[] buffer = new byte[2048];
int i = -1;
try {
i = inputStream.read(buffer);
} catch (IOException e) {
e.printStackTrace();
}
for (int k = 0; k < i; k++) {
requestContentBuffer.append((char) buffer[k]);
}
System.out.println(requestContentBuffer.toString());
}
private void parseAndSetUri() {
String requestString = requestContentBuffer.toString();
int index1 = requestString.indexOf(' ');
int index2 = -1;
if (index1 != -1) {
index2 = requestString.indexOf(' ', index1 + 1);
}
this.uri = (index2 > index1) ? requestString.substring(index1 + 1,
index2) : null;
}
public String getUri() {
return this.uri;
}
......
}
BaseResponse:
public class BaseResponse implements ServletResponse {
private OutputStream outputStream;
public BaseResponse(OutputStream out) {
this.outputStream = out;
}
......
}
ServletProcessor:
package simpleserver.simplecontainer;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLStreamHandler;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class ServletProcessor {
private static Log log = LogFactory.getLog(ServletProcessor.class);
public static void processServletRequest(BaseRequest request,
BaseResponse response) {
String uri = request.getUri();
String servletName = MappingHelper.resolve(uri);
log.info("Processing servlet: " + servletName);
Servlet servlet = loadServlet(servletName);
callService(servlet, request, response);
}
private static URLClassLoader createUrlClassLoader() {
URLClassLoader loader = null;
try {
URL[] urls = new URL[1];
URLStreamHandler streamHandler = null;
File classPath = new File(Constant.RESOURCE_ROOT);
// org.apache.catalina.startup.ClassLoaderFactory
String repository = (new URL("file", null,
classPath.getCanonicalPath() + File.separator)).toString();
urls[0] = new URL(null, repository, streamHandler);
loader = new URLClassLoader(urls);
} catch (IOException e) {
e.printStackTrace();
}
return loader;
}
private static Servlet loadServlet(String servletName) {
URLClassLoader loader = createUrlClassLoader();
Servlet servlet = null;
try {
@SuppressWarnings("unchecked")
Class<Servlet> servletClass = (Class<Servlet>) loader
.loadClass(servletName);
servlet = (Servlet) servletClass.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return servlet;
}
private static void callService(Servlet servlet, ServletRequest request,
ServletResponse response) {
try {
servlet.service(request, response);
} catch (ServletException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
MappingHeler:
package simpleserver.simplecontainer;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
public class MappingHelper {
public static Properties requestMapping;
static {
requestMapping = new Properties();
try {
requestMapping.load(new FileInputStream(Constant.MAPPING_FILE));
} catch (IOException e) {
e.printStackTrace();
}
}
public static String resolve(String requestPath) {
return requestMapping.getProperty(requestPath);
}
}
该类负责请求URL到Servlet的映射,读入一个属性文件,如下:
/servlet/PrimitiveServlet=simpleserver.simplecontainer.webapp.PrimitiveServlet
/servlet/Test=simpleserver.simplecontainer.webapp.TestServlet
另外还有一个帮助类定义常量。
欢迎交流。