实现Tomcat——实现javax.servlet.Servlet接⼝
0. 环境配置
这⾥使⽤IntelliJ IDEA,Maven WebApp项⽬,不过这⾥我们不会使⽤/启动Tomcat服务器。
本⽂的⽬的就是使⽤Socket实现⼀个服务器;此服务器是⼀个Servlet容器,我们需要遵循Servlet接⼝规范,即javax.servlet.*。
这⾥由于我们使⽤的是Maven项⽬,所以这⾥引⼊servlet api 依赖,servlet api的版本为3.1
<!--  Servlet API  -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
1. Servlet接⼝规范
我们查看Servlet接⼝,发现其接⼝⽅法只有5个
public interface Servlet {
public void init(ServletConfig config)throws ServletException;
public void service(ServletRequest req, ServletResponse res)throws ServletException, IOException;
public void destroy();
public ServletConfig getServletConfig();
public String getServletInfo();
}
其中init,service,destroy是3个与Servlet⽣命周期相关的函数。getServletConfig(),getServletInfo从⽅法名可以看出,它们与Servlet的信息相关。
当实例化某个Servlet类后,servlet会调⽤其init⽅法来对servlet进⾏初始化,servlet容器只会调⽤该⽅法⼀次,调⽤后就可以执⾏service⽅
法来处理请求相应逻辑。在servlet接收任何请求之前,必须是经过正确初始化的,初始化在servlet的⽣命周期中只会执⾏⼀次,所以我们可以在init⽅法中做⼀些初始化操作,如初始化默认值,载⼊数据库驱动等。。⼀般情况,我们可以将init⽅法留空,什么也不做。
当客户端的请求到达时,servlet容器就会响应相应servlet的service⽅法,并传
⼊javax.servlet.ServletReqeust,javax.servlet.ServletResponse到service⽅法,其中ServletReqeust,ServletResponse分别包含HTTP请求响应的相关信息。service⽅法会在客户端每次请求时反复地被调⽤。
在Servlet实例服务移除前,servlet容器会调⽤servlet实例的destroy⽅法,⼀般当servlet容器关闭或者要释放内存时,才会将servlet实例移除。当且仅当servlet实例的service⽅法中所有线程都退出或执⾏超时后,才会调⽤destroy⽅法。
所以,现在我们总结以下,Servlet容器的处理流程:
1. 当客户端请求服务器时,第⼀次调⽤某个servlet时,要先加载该servlet类,并调⽤其init⽅法(仅此⼀
次)
2. 针对于每个请求,都会创建⼀个 javax.servlet.ServletReqeust实例和⼀个javax.servlet.ServletResponse实例
3. 调⽤该servlet的service⽅法,刚创建的ServletReqeust,ServletResponse作为service⽅法的参数。
4. 当关闭该servlet类时,调⽤其destroy⽅法,并卸载该servlet类。
所以现在我们可以简单地实现⼀个⾃⼰的Servlet类,我们称之为PrimitiveServlet,它继承⾃servlet接⼝,所以我们必须要重写servlet的5个⽅法:
public class PrimitiveServlet implements Servlet {
@Override
public void init(ServletConfig servletConfig)throws ServletException {
System.out.println("");
}
@Override
servlet和tomcat的关系public void service(ServletRequest request, ServletResponse response)throws ServletException, IOException {
System.out.println("From service");
PrintWriter out = Writer();
// 头部信息
out.write("HTTP/1.1 200 OK\r\n"+
"Content-Type: text/html\r\n"+
"\r\n");
out.println("<h1>Hello "+Class().getSimpleName()+" </h1>");
}
@Override
public void destroy(){
System.out.println("");
}
@Override
public ServletConfig getServletConfig(){
return null;
}
@Override
public String getServletInfo(){
return null;
}
}
我们看到,这⾥的servlet的service⽅法,我们简单地返回⼀个html⽂本,其内容是
Hello ${ServletName}。同时,我们暂时只对servlet的3个⽣命周期函数做⼀个简单实现。
2. 我们要实现的服务器的任务
我们的任务就是根据不同请求的URL地址,来做出不同的响应,这⾥我们做的很简单,假定⽤户只会按照下⾯的格式来访问我们的服务器。
localhost:8080/staticResource,此类Url访问,我们当作静态资源的请求
localhost:8080/servlet/servletName,此类Url访问,我们当作⼀个Servlet请求
localhost:8080/SHUTDOWN,关闭服务器。
3. HttpServer类
这⾥我们定义⼀个HTTP Server服务器的的主启动类,名为TomHttpServer1,它主要⽤来创建服务端Socket,并处理Socket请求。
public class TomHttpServer1 {
Logger logger = Logger(TomHttpServer1.class);
public static final String SHUTDOWN ="/SHUTDOWN";
public static final int PORT =8080;
private boolean isShutDown =false;
public static void main(String[] args){
TomHttpServer1 httpServer =new TomHttpServer1();
httpServer.await();
}
public void await(){
ServerSocket serverSocket = null;
try{
serverSocket =new ServerSocket(PORT,1, ByName("localhost"));
}catch(Exception e){
e.printStackTrace();
}
while(!isShutDown){
Socket socket = null;
InputStream input = null;
OutputStream output = null;
try{
socket = serverSocket.accept();
input = InputStream();
output = OutputStream();
// 创建ServletRequest,ServletResponse
TomServletRequest servletRequest =new TomServletRequest(input);
TomServletResponse servletResponse =new TomServletResponse(output, servletRequest);
// process
Uri()!= null && Uri().startsWith("/servlet/")){
// 如果是请求Servlet
ServletProcessor1 sp1 =new ServletProcessor1();
sp1.process(servletRequest, servletResponse);
}else{
// 否则,我们认为它是请求静态资源
StaticResourceProcessor srp =new StaticResourceProcessor();
srp.process(servletRequest, servletResponse);
}
socket.close();
isShutDown = Uri().equals(SHUTDOWN);
}catch(IOException e){
e.printStackTrace();
}
}
}
}
我们看到main函数,await⽅法为主要的逻辑所在。await⽅法⾸先初始化⼀个监听在8080端⼝的ServerSocket,然后根据Servlet 规范,我们创建ServletRequest,ServletResponse两个接⼝的实现类TomServletRequest,TomServletResponse实例,然后根据请求的URI来判断是请求静态资源还是请求servlet
如果是Servlet请求,我们就交由ServletProcessor1去处理,否则交由StaticResourceProcessor去处理。
这⾥的sp1.process(servletRequest, servletResponse);和srp.process(servletRequest, servletResponse);暂时可以简单地理解
为service(servletRequest,servletResponse)⽅法。
上⾯代码的4个类TomServletRequest,TomServletResponse,StaticResourceProcessor ,StaticResourceProcessor我们现在仍然没有实现,接下来,会⼀⼀实现。
4. ServletRequest类
public class TomServletRequest implements ServletRequest {
private Logger logger = Logger(TomServletRequest.class);
private InputStream input;
private String uri;
public TomServletRequest(InputStream input){
this.input = input;
this.parse();
}
private void parse(){
StringBuffer requestStringBuffer =new StringBuffer(2048);
int len =-1;
byte[] buffer =new byte[2048];
try{
len = ad(buffer);
}catch(Exception e){
e.printStackTrace();
}
for(int j =0; j < len ; j ++){
requestStringBuffer.append((char) buffer[j]);
}
logger.info("request string: \n{}", requestStringBuffer);
uri =String());
logger.info("parse uri: \n{}", uri);
}
public String getUri(){
return uri;
}
/**
* The first line of the http header is just like this :
* GET /servlet/tomservlet HTTP/1.1
* @param requestString
* @return
*/
private String parseUri(String requestString){
int index1, index2;
index1 = requestString.indexOf(' ');
if(index1 !=-1){
index2 = requestString.indexOf(' ', index1 +1);
if(index2 > index1){
return requestString.substring(index1 +1, index2);
}
}
return null;
}
/**
* 下⾯是部分重写的代码,代码实现为空
* 后⾯我们将⼀⼀实现,这⾥暂时省略,⼀⾯占篇幅
*/
Socket接收到请求后,就获取socket对应的InputStream,并传⼊TomServletRequest类的构造函数TomServletRequest(InputStream input)以创建⼀个ServletRequest类。
这⾥我们注意到⼀个parse()⽅法,它能够解析出HTTP头信息的请求URI地址,核⼼代码就是parseUri函数。parseUri函数很容易理解,我们可以参考下⾯的HTTP头信息的第⼀⾏理解⼀下:我们只需要到第⼀个空格和第⼆个空格的索引即可,两个索引之间的字符就是HTTP请求的URI地址。
GET /servlet/tomservlet HTTP/1.1
5. Constants类
定义常量类,定义当前maven web的根⽬录。
public class Constants {
public static final String WEB_ROOT =new File("").getAbsoluteFile().getPath()
+"\\src\\main\\webapp";
6. ServletResponse类

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。