本文为读《深入剖析Tomcat》第2章做的笔记、写的代码、做的分析。作者是菜鸟,谨慎参考
实现流程图
HttpServer类:
public class HttpServer1 {
private static String SHUT_DOWN="shutdown";
public static void main(String[] args){
HttpServer1 server=new HttpServer1();
server.await();
}
private void await() {
ServerSocket server=null;
int port=806;
try {
server=new ServerSocket(port,1,InetAddress.getByName("127.0.0.1"));
} catch (Exception e) {
e.printStackTrace();
}
boolean shutdown=false;
while(!shutdown){
Socket socket;
InputStream input;
OutputStream out;
try {
socket = server.accept();
input=socket.getInputStream();
out=socket.getOutputStream();
Request req=new Request(input);
req.parse();
Response res=new Response(out);
res.setRequest(req);
if(req.getUri().startsWith("/servlet/")){
ServletProcessor1 processor=new ServletProcessor1();
processor.process(req,res);
}else{
StaticResourceProcessor processor=new StaticResourceProcessor();
processor.process(req, res);
}
socket.close();
shutdown=req.getUri().equals(SHUT_DOWN);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
3 . Response类的主要方法
public class Response implements ServletResponse {
private OutputStream out;
private Request req;
private static int BUFFER_SIZE = 1024;
public Response(OutputStream out) {
this.out = out;
}
public void setRequest(Request req) {
this.req = req;
}
public void sendStaticReSource() throws IOException {
System.out.println("访问的资源为"+Constants.WEB_ROOT + req.getUri());
File file = new File(Constants.WEB_ROOT + req.getUri());
FileInputStream fis = null;
byte[] buffer = new byte[1024];
int n = -1;
try {
if (file.exists()) {
System.out.println("该资源存在");
fis = new FileInputStream(file);
n = fis.read(buffer, 0, BUFFER_SIZE);
while (n != -1) {
out.write(buffer, 0, n);
n = fis.read(buffer, 0, BUFFER_SIZE);
}
} else {
System.out.println("该资源不存在");
String msg = "msg error! file does not exist.";
out.write(msg.getBytes());
}
} catch (IOException e) {
}finally{
if(fis!=null)
fis.close();
}
}
public PrintWriter getWriter() throws IOException{
//调用println会自动刷新,调用print不会自动刷新
PrintWriter writer =new PrintWriter(out,true);
return writer;
}
}
4 . Request的主要方法:
public class Request implements ServletRequest{
private final static int BUFFER_SIZE=2048;
private InputStream input;
private String uri;
public Request(InputStream input) {
this.input=input;
}
public void parse() {
byte[] buffers=new byte[BUFFER_SIZE];
int n;
try {
n = input.read(buffers, 0, BUFFER_SIZE);
} catch (IOException e) {
e.printStackTrace();
n=-1;
}
StringBuffer sb=new StringBuffer();
for(int i=0;i<n;i++){
sb.append((char)buffers[i]);
}
System.out.println("解析得到request:");
System.out.println(sb);
uri=parseUri(sb.toString());
System.out.println("解析request中的uri:"+uri);
}
private String parseUri(String str){
int index1,index2;
index1=str.indexOf(' ');
while(index1!=-1){
index2=str.indexOf(' ', index1+1);
if(index2-index1>0)
return str.substring(index1+1,index2);
}
return null;
}
public String getUri(){
return uri;
}
}
- ServletProcessor类:
public class ServletProcessor1 {
public void process(Request req,Response res){
String uri =req.getUri();
String servletName=uri.substring(uri.lastIndexOf('/')+1);
//URLCLassLoader为ClassLoader的直接子类
//用它的loadClass()方法来载入servlet类
URLClassLoader loader=null;
try {
URL[] urls=new URL[1];
URLStreamHandler streamHandler=null;
File classPath=new File(Constants.WEB_ROOT);
String repository=(new URL("file",null,classPath.getCanonicalPath()
+File.separator)).toString();
System.out.println("servlet的目录repository:"+repository);
//使用streamHandler的因为URL重载中,第3个参数不同
//streamHandler将其区分,repository为servlet的目录
urls[0]=new URL(null,repository,streamHandler);
//urls为URL对象数组,每个URL对象都指明了类载入器在哪里查找类
//若URL以“/”结尾,则表明其指向一个目录,否则为指向一个jar文件
//根据需要载入器会下载这个jar文件
//在servlet容器中,类载入器查找servlet类的目录称作仓库(repository)
loader=new URLClassLoader(urls);
} catch (IOException e) {
e.printStackTrace();
}
Class myclass=null;
try {
System.out.println("需要载入的 servletName:"+servletName);
myclass=loader.loadClass(servletName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Servlet servlet=null;
try {
servlet=(Servlet) myclass.newInstance();
servlet.service(req, res);
} catch (Exception e) {
e.printStackTrace();
}
}
}
6 . 需要注意的两点:
1)第一点需要注意的:在ServletProcessor类的process(Request req,Response res)中如下写是不好的,因为在service(ServletRequest req, ServletResponse res)方法中,可以将req下转型为Request型,从而req可以访问parse方法,可以将res下转型为Request型,从而res可以访问sendStaticResource()方法:
servlet=(Servlet) myclass.newInstance();
servlet.service(req, res);
2)针对上一点的改进方法,增加ResponseFacade类如下图:
3)将上1)步的代码改为如下,为什么这样能避免不好的情况,在下一篇文章中举例说明:
servlet=(Servlet) myclass.newInstance();
RequestFacade requestFacade=new RequestFacade(req);
ResponseFacade responseFacade=new ResponseFacade(res);
servlet.service(requestFacade, responseFacade);
4)第2个需要注意的问题,如下图,ServletResponse为接口,3)中servlet.service(requestFacade, responseFacade)会调用被访问servletName类的service(ServletRequest req, ServletResponse res)方法,例如尽管res是ServletResponse 实例,当使用res.getWriter(),实际上调用的是Response类中重写的getWriter():
- 两篇文章的源码:
[深入剖析Tomcat]一个简单的servlet容器实现源码