一.理论知识回顾

如图,首先客户端的请求先要打在网卡上,网卡进行注册端口的操作。
注册完端口请求就会发送给Socket,Socket要打开输入流InputStream,根据输入流解析其中的数据,得到请求地址和请求类型之后,把地址发送给Servlet容器。
Servlet容器根据这个地址找到了对应的Servlet对象之后,找到对应的Servlet对象的地址,解析对象的代码,把对象里的请求类型发送给对象。
对象里的doGet方法此时用request接收,然后根据用户在方法内写下的具体行为,生成response,把response打回到Socket当中,Socket此时打开输出流OutputStream。
然后输出内容,把响应返回给客户端。

如图,这是Servlet执行流程,我们要先让普通类继承HttpServlet这个抽象类,其实,HttpServlet还继承了GenericServlet这个抽象类,而GenericServlet又实现了Servlet接口。
二.具体代码
1.文件结构
首先利用Maven创建一个项目。
在“java”文件夹(自带)下创建一个com包,在包下模仿真正的Tomcat的结构,首先直接在com包下创建MyTomcat作为启动器;然后创建config文件夹,里面创建ServletConfigMapp类,用来实现Servlet容器;创建lib文件夹,里面创建Servlet接口,GenericServlet抽象类,HttpServlet抽象类,ServeltRequest接口,HttpServeltRequest类,ServletResponse接口,HttpServletResponse类,WebServlet注解;创建utils文件夹用来放一些工具类;创建webapps.myweb包,下面存放具体实现功能的动态资源(普通类),比如MyFirstServlet类。
在resources文件夹(自带)下创建静态资源,比如login.html。
2.代码
Servlet接口:
public interface Servlet {
public void init();
public void service(ServletRequest request,ServletResponse response) throws IOException;
public void destroy();
}
仿照真实的Sevlet接口,定义好init,service,destroy三个方法。
GenericServlet抽象类:
public abstract class GenericServlet implements Servlet{
@Override
public void init() {
}
public abstract void service(ServletRequest request, ServletResponse response) throws IOException;
@Override
public void destroy() {
}
}
实现除service之外的方法,不实现的方法要写上abstract关键字。
HttpServlet抽象类:
public abstract class HttpServlet extends GenericServlet{
public abstract void doGet(ServletRequest request, ServletResponse response) throws IOException;
public abstract void doPost(ServletRequest request, ServletResponse response);
@Override
public void service(ServletRequest request, ServletResponse response) throws IOException {
if(request.getMethod().equals("GET")){
doGet(request,response);
}else if(request.getMethod().equals("POST")){
doPost(request,response);
}
}
}
实现service方法,service方法的作用在于根据传来的请求类型,来决定调用doGet还是doPost方法。这里要先把doGet和doPost方法定义好。
ServeltRequest接口:
public interface ServletRequest {
public String getMethod();
public String getPath();
public String setMethod(String method);
public String setPath(String path);
}
定义好getter和setter方法,用于传来和查询请求类型和请求地址。
HttpServeltRequest类:
public class HttpServletRequest implements ServletRequest{
private String method;
private String path;
@Override
public String getMethod() {
return method;
}
@Override
public String getPath() {
return path;
}
@Override
public String setMethod(String method) {
return this.method = method;
}
@Override
public String setPath(String path) {
return this.path = path;
}
}
实现ServeltRequest接口,实现对应的方法。
ServletResponse接口:
public interface ServletResponse {
public void append(String context) throws IOException;
}
定义好接下来要具体实现的方法。
HttpServletResponse类:
public class HttpServletResponse implements ServletResponse{
private OutputStream outputStream;
public void setOutputStream(OutputStream outputStream){
this.outputStream = outputStream;
}
@Override
public void append(String context) throws IOException {
outputStream.write(context.getBytes());
}
public void returnStatic(String path) throws Exception {
String resource = FileUtil.getResoucePath(path);
File file = new File(resource);
if (file.exists()){
FileUtil.writeFile(file,outputStream);
}else {
System.out.println("404无法找到。。。");
}
}
}
实现ServletResponse接口,append方法用于把数据写进输出流形成响应返回到客户端。returnnStatic用于针对静态资源把数据写进输出流形成响应返回到客户端。
WebServlet注解:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface WebServlet {
String value();
}
注解定义要注意设置运行时生效和针对类生效。
Servlet容器ServletConfigMap:
public class ServletConfigMapping {
public static Map<String, HttpServlet> classMapping = new HashMap<>();
static {
try {
List<String> paths = SearchClassUtil.searchClass("com.webapps.myweb");
for (String path : paths) {
try {
Class clazz = Class.forName(path);
WebServlet webServlet = (WebServlet) clazz.getDeclaredAnnotation(WebServlet.class);
HttpServlet servlet = (HttpServlet) clazz.getDeclaredConstructor().newInstance();
classMapping.put(webServlet.value(), servlet);
} catch (Exception e) {
e.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
定义Servlet容器,利用了集合,反射的相关知识。
Tomcat启动器MyTomcat:
public class MyTomcat {
private static final Integer PORT = 7789;
private static HttpServletRequest httpServletRequest = new HttpServletRequest();
private static HttpServletResponse httpServletResponse = new HttpServletResponse();
public static void start() throws Exception {
ServerSocket serverSocket = new ServerSocket(PORT);
while (true){
Socket socket = serverSocket.accept();
InputStream stream = socket.getInputStream();
httpServletResponse.setOutputStream(socket.getOutputStream());
handler(stream);
}
}
public static void handler(InputStream stream) throws Exception {
int count = 0;
while (count == 0){
count = stream.available();
}
byte[] bytes = new byte[count];
int read = stream.read(bytes);
String request = new String(bytes,0,read);
if (request.equals("")){
System.out.println("请求为空");
}else {
String firstLine = request.split("\n")[0];
String method = firstLine.split("\\s")[0];
String path = firstLine.split("\\s")[1];
httpServletRequest.setMethod(method);
httpServletRequest.setPath(path);
if(path.equals("")){
httpServletResponse.append(ResponseUtil.responseHeader404);
}else if (ServletConfigMapping.classMapping.get(path) != null){
HttpServlet servlet = ServletConfigMapping.classMapping.get(path);
servlet.service(httpServletRequest,httpServletResponse);
}else {
httpServletResponse.returnStatic(path);
}
}
}
public static void main(String[] args) throws Exception {
start();
}
}
利用Socekt相关知识实现启动器功能。
MyFirstServlet类:
@WebServlet("/first")
public class MyFirstServlet extends HttpServlet {
@Override
public void doGet(ServletRequest request, ServletResponse response) throws IOException {
System.out.println("first地址");
response.append(ResponseUtil.getResponseHeader200("<h1>这是第一个Servlet</h1>"));
}
@Override
public void doPost(ServletRequest request, ServletResponse response) {
}
}
继承HttpServlet类,写上WebServlet注解,并实现doGet或doPost方法。
login.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录页面</title>
</head>
<body>
<h1>登录页面</h1>
</body>
</html>
三.效果展示
(这里有一个小bug,输出流输出中文会产生乱码)



1279

被折叠的 条评论
为什么被折叠?



