本章的主要内容是:第一个描述的是最基本的web容器,但是他只能处理静态的请求,不能处理servlet代码,即动态的网页。所以这一章就给出了最基础版本的可以处理静态和动态的servlet容器。
下面给出主要的代码
1.PrimitiveServlet
这里首先给出对应的servlet的代码。这里的servlet代码实现了Servlet接口
注意:所有的自定义的servlet类都必须实现javax.servlet.servlet接口或者继承实现了该接口的类。为什么呢?因为在容器中一般是根据你自己定义的servlet的类名来获取对应的对象,每个人写的类名都不一样,如果不给出一个共有的接口的话,那么tomcat怎么知道要读取到对应的类对象以后,调用他的哪个方法, 传什么参数进去呢?所以这里就做了上述的约定。必须继承上述的接口或者实现该接口的类。
package servlet;
import javax.servlet.*;
import java.io.IOException;
import java.io.PrintWriter;
/**
* 原始的servlet
*/
public class PrimitiveServlet implements Servlet {
public void init(ServletConfig config) throws ServletException {
System.out.println("执行init()方法....");
}
public ServletConfig getServletConfig() {
return null;
}
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
System.out.println("执行service");
//这里自己定义了相应头部,作者源代码中没有写,会导致对应的
String s="HTTP/1.1 200 OK\r\n\r\n";
//获取输出流工具类
PrintWriter outputStream = res.getWriter();
outputStream.println(s+"hello rose are red....");
// outputStream.print("vio are blue");
// outputStream.close();
}
public String getServletInfo() {
return null;
}
public void destroy() {
System.out.println("执行destory....");
}
}
注意上述的outputStream,前提要加上相应头部!不然前端无法看到对应的输出信息。
2.Request
这里和第一张的功能是一样的,Request主要是来提取请求的信息,在本章主要是用来提取URI的。下面给出他的具体的代码
package demon2;
import javax.servlet.*;
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;
/**
* 为什么继承ServletRequest,因为我们自己编写的servlet类的service方法都需要传入ServletRequest
* 所以这里让我们自定义的Request继承对应的ServletRequest
*/
public class Request implements ServletRequest {
/**
* 输入流
*/
private InputStream input;
/**
* 请求的资源
*/
private String uri;
public Request(InputStream input) {
this.input = input;
}
/**
* 解析报文方法
*/
public void parse(){
StringBuilder stringBuilder = new StringBuilder(2048);
int readSum=0;
byte[] buffer=new byte[2048];
try {
readSum= input.read(buffer);
} catch (IOException e) {
e.printStackTrace();
}
for(int j=0;j<readSum;j++){
stringBuilder.append((char) buffer[j]);
}
//解析请求资源
uri=parseUri(stringBuilder.toString());
}
private String parseUri(String requestString){
int index1,index2=0;
index1=requestString.indexOf(' ');
if(index1!=-1){
index2=requestString.indexOf(' ',index1+1);
}
if(index2>index1){
return requestString.substring(index1+1,index2);
}
return null;
}
public String getUri(){
return uri;
}
public Object getAttribute(String name) {
return null;
}
public Enumeration<String> getAttributeNames() {
return null;
}
public String getCharacterEncoding() {
return null;
}
public void setCharacterEncoding(String env) throws UnsupportedEncodingException {
}
public int getContentLength() {
return 0;
}
public long getContentLengthLong() {
return 0;
}
public String getContentType() {
return null;
}
public ServletInputStream getInputStream() throws IOException {
return null;
}
public String getParameter(String name) {
return null;
}
public Enumeration<String> getParameterNames() {
return null;
}
public String[] getParameterValues(String name) {
return new String[0];
}
public Map<String, String[]> getParameterMap() {
return null;
}
public String getProtocol() {
return null;
}
public String getScheme() {
return null;
}
public String getServerName() {
return null;
}
public int getServerPort() {
return 0;
}
public BufferedReader getReader() throws IOException {
return null;
}
public String getRemoteAddr() {
return null;
}
public String getRemoteHost() {
return null;
}
public void setAttribute(String name, Object o) {
}
public void removeAttribute(String name) {
}
public Locale getLocale() {
return null;
}
public Enumeration<Locale> getLocales() {
return null;
}
public boolean isSecure() {
return false;
}
public RequestDispatcher getRequestDispatcher(String path) {
return null;
}
public String getRealPath(String path) {
return null;
}
public int getRemotePort() {
return 0;
}
public String getLocalName() {
return null;
}
public String getLocalAddr() {
return null;
}
public int getLocalPort() {
return 0;
}
public ServletContext getServletContext() {
return null;
}
public AsyncContext startAsync() throws IllegalStateException {
return null;
}
public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException {
return null;
}
public boolean isAsyncStarted() {
return false;
}
public boolean isAsyncSupported() {
return false;
}
public AsyncContext getAsyncContext() {
return null;
}
public DispatcherType getDispatcherType() {
return null;
}
}
这里的自己写的Request方法实现了ServletRequest接口,为什么必须实现呢,和上述的servlet一样,因为规定的servlet的service方法中只认这ServletRequest类型,但是为了方便,这里只简单的实现的实现了几个方法。
3.Response
这里的主要的功能就是获取对应的输出流,将对应的数据返回给用户。(在这里静态方法的返回写在这里了),同样他也继承了ServletResponse,原因和Request一样的。
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletResponse;
import java.io.*;
import java.util.Locale;
public class Response implements ServletResponse {
private Request request;
private OutputStream output;
public Response(OutputStream output) {
this.output = output;
}
public void setRequest(Request request){
this.request=request;
}
/**
* 发送静态资源给浏览器
*/
public void sendStaticResource() throws IOException {
byte[] bytes=new byte[1024];
FileInputStream fis=null;
try{
System.out.println(demon1.PropertiesConst.WEB_ROOT);
File file=new File(demon1.PropertiesConst.WEB_ROOT,request.getUri());
if(file.exists()){
fis = new FileInputStream(file);
int ch=fis.read(bytes,0,1024);
output.write("HTTP/1.1 200 OK\r\n\r\n".getBytes());
while(ch!=-1){
output.write(bytes,0,ch);
ch=fis.read(bytes,0,1024);
}
}else {
String errorMessge = "HTTP/1.1 404 FILE NOT Found\r\n" +
"Content-Type:text/html\r\n" +
"Content-Length:23\r\n" +
"\r\n" +
"<h1>file not found</h1>";
output.write(errorMessge.getBytes());
}
}catch (Exception e){
System.out.println(e.toString());
}finally {
if(fis!=null){
fis.close();
}
}
}
public String getCharacterEncoding() {
return null;
}
public String getContentType() {
return null;
}
public ServletOutputStream getOutputStream() throws IOException {
return null;
}
/**
* 这里使用的true,表示使用 printWriter.println();的时候会自动刷新输出到浏览器
* 而使用 printWriter.print();不会自动刷新
* @return
* @throws IOException
*/
public PrintWriter getWriter() throws IOException {
//使用最原始的输出流outputStream,创建一个给servlet使用的输出流
return new PrintWriter(output, true);
}
public void setCharacterEncoding(String charset) {
}
public void setContentLength(int len) {
}
public void setContentLengthLong(long len) {
}
public void setContentType(String type) {
}
public void setBufferSize(int size) {
}
public int getBufferSize() {
return 0;
}
public void flushBuffer() throws IOException {
}
public void resetBuffer() {
}
public boolean isCommitted() {
return false;
}
public void reset() {
}
public void setLocale(Locale loc) {
}
public Locale getLocale() {
return null;
}
}
4.下面给出静态资源处理器和servlet资源处理器。
a)静态资源处理器
package demon2;
import java.io.IOException;
/**
* 处理静态请求的类
*/
public class StaticResourceProcessor {
public void process(Request request,Response response){
try {
response.sendStaticResource();
} catch (IOException e) {
e.printStackTrace();
}
}
}
b)servlet处理器
package demon2;
import demon1.PropertiesConst;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLStreamHandler;
/**
* 处理动态请求的方法,在这个里面调用service
*/
public class ServletProcessor {
public void process(Request request,Response response){
//获取资源
String uri = request.getUri();
//提取对应的servlet类名
String servletName=uri.substring(uri.lastIndexOf("/")+1);
URLClassLoader loader=null;
try {
URL[] urls=new URL[1];
URLStreamHandler urlStreamHandler=null;
File classPath=new File(PropertiesConst.WEB_ROOT);
String repository=new URL("file",null,classPath.getCanonicalPath()+File.separator).toString();
urls[0]=new URL(null,repository,urlStreamHandler);
loader=new URLClassLoader(urls);
} catch (IOException e) {
e.printStackTrace();
}
Class myClass=null;
try {
myClass=loader.loadClass(servletName);
// myClass=Class.forName(servletName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Servlet servlet;
try {
servlet= (Servlet) myClass.newInstance();
servlet.service(request,response);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ServletException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
这里的主要功能就是根据servlet的类名获取对应的servlet对象,并调用其service方法。
最后给出对应的HttpServer类
他的主要的功能,
1.建立socket链接
2.提取对应的请求流和输出流InputStream和OutputStream
3.根据请求类型判断你是静态的请求还是servlet请求,分别调用对应不同的处理器。
package demon2;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
public class HttpServer {
private static final String SHUTDOWN_COMMAND="/shutdown";
private boolean shutdown=false;
private static final int PORT=8080;
private static final String IP="127.0.0.1";
public void await(){
ServerSocket serverSocket=null;
try {
serverSocket=new ServerSocket(PORT,1, InetAddress.getByName(IP));
} catch (IOException e) {
e.printStackTrace();
}
while(!shutdown){
try {
assert serverSocket != null;
//阻塞监听端口
Socket socket = serverSocket.accept();
//输入流,包含请求报文
InputStream inputStream = socket.getInputStream();
Request request=new Request(inputStream);
request.parse();
//输出流,向浏览器写入输出报文
OutputStream outputStream = socket.getOutputStream();
Response response=new Response(outputStream);
response.setRequest(request);
/**
* 判断对应的是servlet还是静态资源
*/
if(request.getUri().startsWith("/servlet/")){
//
ServletProcessor servletProcessor=new ServletProcessor();
servletProcessor.process(request,response);
}else {
StaticResourceProcessor staticResourceProcessor=new StaticResourceProcessor();
staticResourceProcessor.process(request,response);
}
socket.close();
shutdown=request.getUri().equals(SHUTDOWN_COMMAND);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
上述就是本节内容的主要内容。