Advertisement

Java 知识点整理(Spring boot) 手敲Springboot

阅读量:

服务端处理流程:

浏览器提交表单到服务端 -服务端解析请求-处理请求-发送响应

1.1新建一个maven项目,创建一个主启动对象(AppLication),添加构造方法启动服务端,添加start方法启动一个线程与客户端交互

main方法新建主启动对象调用start方法

复制代码
 package com.webserver.core;

    
  
    
 import java.io.IOException;
    
 import java.net.ServerSocket;
    
 import java.net.Socket;
    
  
    
 /** * 主启动类
    
  */
    
 public class BirdApplication {
    
     private ServerSocket serverSocket;
    
  
    
     public BirdApplication(){
    
     try {
    
         System.out.println("正在启动服务端...");
    
         serverSocket = new ServerSocket(8088);
    
         System.out.println("服务端启动完毕!");
    
     } catch (IOException e) {
    
         e.printStackTrace();
    
     }
    
     }
    
  
    
     public void start(){
    
     try {
    
         Socket socket = serverSocket.accept();
    
         //启动一个线程处理与该客户端的交互
    
         ClientHandler handler = new ClientHandler(socket);
    
         Thread t = new Thread(handler);
    
         t.start();
    
  
    
     } catch (IOException e) {
    
         e.printStackTrace();
    
     }
    
     }
    
  
    
     public static void main(String[] args) {
    
     BirdApplication application = new BirdApplication();
    
     application.start();
    
     }
    
 }
    
    
    
    

1.2创建一个客户端处理程序对象(ClientHandler) 实现Runable接口
添加构造方法,重写run方法 ,添加sock.getInputStream流读取浏览器发送请求(读取请求行)
测试

复制代码
 ** * 与指定的客户端完成一次HTTP交互
    
  * HTTP协议要求浏览器与服务端交互采取一问一答的原则,因此服务端这里处理一次交互由
    
  * 三步骤完成(参考【服务器处理流程示意图】):
    
  * 1:解析请求
    
  * 2:处理请求
    
  * 3:发送响应
    
  */
    
 public class ClientHandler implements Runnable{
    
     private Socket socket;
    
     public ClientHandler(Socket socket){
    
     this.socket = socket;
    
     }
    
     public void run() {
    
     try {
    
         InputStream in = socket.getInputStream();
    
         int d;
    
         StringBuilder builder = new StringBuilder();//拼接读取的字符
    
         char cur='a',pre='a';//cur表示本次读取的字符,pre表示上次读取的字符
    
         while((d = in.read())!=-1){
    
             cur = (char)d;//本次读取到的字符
    
             if(pre==13&&cur==10){//是否连续读取到了回车+换行(判断是否读取完一行了)
    
                 break;
    
             }
    
             builder.append(cur);//将本次读取的字符拼接到已经读取的字符串中
    
             pre = cur;//再下次读取字符前,将本次读取的字符记作上次读取的字符
    
         }
    
         String line = builder.toString().trim();
    
         System.out.println("请求行:"+line);
    
         String[] data = line.split("\ s");//[GET, /index.html, HTTP/1.1]
    
         String method = data[0];//请求方式
    
         String uri = data[1];//抽象路径
    
         String protocol = data[2];//协议版本
    
         System.out.println("method:"+method);
    
         System.out.println("uri:"+uri);
    
         System.out.println("protocol:"+protocol);
    
  
    
  
    
     } catch (IOException e) {
    
         e.printStackTrace();
    
     }
    
     }
    
 }
    
    
    
    

2.1添加readLine方法封装读取浏览器发送的请求(按行读取并返回)

2.2解析请求,通过正则表达式的方式拆分请求行(请求方式/抽象路径/协议版本)
解析消息头:添加map集 通过正则表达式的方式拆分并添加到map集中

复制代码
 package com.webserver.core;

    
  
    
 import java.io.BufferedReader;
    
 import java.io.IOException;
    
 import java.io.InputStream;
    
 import java.net.Socket;
    
 import java.util.HashMap;
    
 import java.util.Map;
    
  
    
 /** * 与指定的客户端完成一次HTTP交互
    
  * HTTP协议要求浏览器与服务端交互采取一问一答的原则,因此服务端这里处理一次交互由
    
  * 三步骤完成(参考【服务器处理流程示意图】):
    
  * 1:解析请求
    
  * 2:处理请求
    
  * 3:发送响应
    
  */
    
 public class ClientHandler implements Runnable {
    
     private Socket socket;
    
  
    
     public ClientHandler(Socket socket) {
    
     this.socket = socket;
    
     }
    
  
    
     public void run() {
    
     try {
    
         //1解析请求
    
         //1.1:解析请求行
    
         String line = readLine();//读取请求中的第一行(请求行)
    
         System.out.println("请求行:" + line);
    
         String[] data = line.split("\ s");//[GET, /index.html, HTTP/1.1]
    
         String method = data[0];//请求方式
    
         String uri = data[1];//抽象路径
    
         String protocol = data[2];//协议版本
    
  
    
         //1.2:解析消息头
    
         Map<String,String> headers = new HashMap<>();
    
         while (true) {//从第二行开始循环读取后续的每一行(第二行开始是消息头)
    
             line = readLine();
    
             if (line.isEmpty()) {//读取到了空行(单独的回车+换行)则表示消息头部分读取完毕了
    
                 break;
    
             }
    
             System.out.println("消息头:" + line);
    
             //将每个消息头按照"冒号空格"拆分开,将消息头的名字作为key,值作为value存入headers
    
             data = line.split(":\ s");//[Host, localhost:8088]
    
             headers.put(data[0],data[1]);
    
         }
    
         System.out.println("headers:"+headers);
    
     } catch (IOException e) {
    
         e.printStackTrace();
    
     }
    
     }
    
  
    
     /** * 读取来自浏览器发送过来的一行字符串
    
      * * @return
    
      */
    
     private String readLine() throws IOException {
    
     //readLine方法会被调用多次,但是始终使用同一个socket调用getInputStream时
    
     //获取回来的输入流和输出流也始终是同一条流
    
     InputStream in = socket.getInputStream();
    
     int d;
    
     StringBuilder builder = new StringBuilder();//拼接读取的字符
    
     char cur = 'a', pre = 'a';//cur表示本次读取的字符,pre表示上次读取的字符
    
     while ((d = in.read()) != -1) {
    
         cur = (char) d;//本次读取到的字符
    
         if (pre == 13 && cur == 10) {//是否连续读取到了回车+换行(判断是否读取完一行了)
    
             break;
    
         }
    
         builder.append(cur);//将本次读取的字符拼接到已经读取的字符串中
    
         pre = cur;//再下次读取字符前,将本次读取的字符记作上次读取的字符
    
     }
    
     return builder.toString().trim();
    
     }
    
 }
    
    
    
    

3.1新建一个服务器请求对象(HttpServerrequest) 添加解析请求行/解析消息头/解析消息正文(HttpServerrequest) 三个方法封装,readLine也放进来,把请求行解析数据私有化,添加getter方法供其他类获取调用

复制代码
 package com.webserver.http;

    
  
    
 import java.io.IOException;
    
 import java.io.InputStream;
    
 import java.net.Socket;
    
 import java.util.HashMap;
    
 import java.util.Map;
    
  
    
 /** * 请求对象
    
  * 该类的每一个实例用于表示浏览器发送过来的一个请求内容
    
  * 每个请求HTTP协议要求由三部分构成:
    
  * 请求行,消息头,消息正文(正文部分可以没有)
    
  */
    
 public class HttpServletRequest {
    
     private Socket socket;
    
     //请求行相关信息
    
     private String method;//请求方式
    
     private String uri;//抽象路径
    
     private String protocol;//协议版本
    
  
    
     //消息头相关信息
    
     private Map<String,String> headers = new HashMap<>();
    
  
    
     /** * 构造器,用于实例化请求对象
    
      * 实例化的过程就是解析请求的过程。
    
      */
    
     public HttpServletRequest(Socket socket) throws IOException {
    
     this.socket = socket;
    
     //1.1:解析请求行
    
     parseRequestLine();
    
     //1.2:解析消息头
    
     parseHeaders();
    
     //1.3:解析消息正文
    
     parseContent();
    
     }
    
     //解析请求行
    
     private void parseRequestLine() throws IOException {
    
     String line = readLine();//读取请求中的第一行(请求行)
    
     System.out.println("请求行:" + line);
    
     String[] data = line.split("\ s");//[GET, /index.html, HTTP/1.1]
    
     method = data[0];//请求方式
    
     uri = data[1];//抽象路径
    
     protocol = data[2];//协议版本
    
     }
    
     //解析消息头
    
     private void parseHeaders() throws IOException {
    
     while (true) {//从第二行开始循环读取后续的每一行(第二行开始是消息头)
    
         String line = readLine();
    
         if (line.isEmpty()) {//读取到了空行(单独的回车+换行)则表示消息头部分读取完毕了
    
             break;
    
         }
    
         System.out.println("消息头:" + line);
    
         //将每个消息头按照"冒号空格"拆分开,将消息头的名字作为key,值作为value存入headers
    
         String[] data = line.split(":\ s");//[Host, localhost:8088]
    
         headers.put(data[0],data[1]);
    
     }
    
     System.out.println("headers:"+headers);
    
     }
    
     //解析消息正文
    
     private void parseContent(){}
    
  
    
  
    
  
    
     /** * 读取来自浏览器发送过来的一行字符串
    
      * * @return
    
      */
    
     private String readLine() throws IOException {
    
     InputStream in = socket.getInputStream();
    
     int d;
    
     StringBuilder builder = new StringBuilder();//拼接读取的字符
    
     char cur = 'a', pre = 'a';//cur表示本次读取的字符,pre表示上次读取的字符
    
     while ((d = in.read()) != -1) {
    
         cur = (char) d;//本次读取到的字符
    
         if (pre == 13 && cur == 10) {//是否连续读取到了回车+换行(判断是否读取完一行了)
    
             break;
    
         }
    
         builder.append(cur);//将本次读取的字符拼接到已经读取的字符串中
    
         pre = cur;//再下次读取字符前,将本次读取的字符记作上次读取的字符
    
     }
    
     return builder.toString().trim();
    
     }
    
  
    
     public String getMethod() {
    
     return method;
    
     }
    
  
    
     public String getUri() {
    
     return uri;
    
     }
    
  
    
     public String getProtocol() {
    
     return protocol;
    
     }
    
  
    
     public String getHeader(String name) {
    
     return headers.get(name);
    
     }
    
 }
    
    
    
    

3.2ClientHandler调用

复制代码
 package com.webserver.core;

    
  
    
 import com.webserver.http.HttpServletRequest;
    
  
    
 import java.io.BufferedReader;
    
 import java.io.IOException;
    
 import java.io.InputStream;
    
 import java.net.Socket;
    
 import java.util.HashMap;
    
 import java.util.Map;
    
  
    
 /** * 与指定的客户端完成一次HTTP交互
    
  * HTTP协议要求浏览器与服务端交互采取一问一答的原则,因此服务端这里处理一次交互由
    
  * 三步骤完成(参考【服务器处理流程示意图】):
    
  * 1:解析请求
    
  * 2:处理请求
    
  * 3:发送响应
    
  */
    
 public class ClientHandler implements Runnable {
    
     private Socket socket;
    
  
    
     public ClientHandler(Socket socket) {
    
     this.socket = socket;
    
     }
    
  
    
     public void run() {
    
     try {
    
         //1解析请求
    
         HttpServletRequest request = new HttpServletRequest(socket);
    
         String path = request.getUri();//获取请求路径
    
         System.out.println("path:"+path);
    
         //2处理请求
    
         //3发送响应
    
     } catch (IOException e) {
    
         e.printStackTrace();
    
     } finally {
    
         try {
    
             socket.close();//协议要求一次交互后断开TCP连接
    
         } catch (IOException e) {
    
             e.printStackTrace();
    
         }
    
     }
    
     }
    
  
    
  
    
 }
    
    
    
    

4.1ClientHandler封装println方法,调用客户端发送一行字符串,会自动在指定字符串后面加回车换行处理请求,发送响应,发送响应正文

复制代码
 package com.webserver.core;

    
  
    
 import com.webserver.http.HttpServletRequest;
    
  
    
 import java.io.*;
    
 import java.net.Socket;
    
 import java.net.URISyntaxException;
    
 import java.nio.charset.StandardCharsets;
    
 import java.util.HashMap;
    
 import java.util.Map;
    
  
    
 /** * 与指定的客户端完成一次HTTP交互
    
  * HTTP协议要求浏览器与服务端交互采取一问一答的原则,因此服务端这里处理一次交互由
    
  * 三步骤完成(参考【服务器处理流程示意图】):
    
  * 1:解析请求
    
  * 2:处理请求
    
  * 3:发送响应
    
  */
    
 public class ClientHandler implements Runnable {
    
     private Socket socket;
    
  
    
     public ClientHandler(Socket socket) {
    
     this.socket = socket;
    
     }
    
  
    
     public void run() {
    
     try {
    
         //1解析请求
    
         HttpServletRequest request = new HttpServletRequest(socket);
    
  
    
         //2处理请求
    
         File root = new File(ClientHandler.class.getClassLoader().getResource(".").toURI());
    
         File staticDir = new File(root,"static");
    
         //根据浏览器地址栏中抽象路径定位static下对应的文件
    
         String path = request.getUri();//获取请求路径
    
         System.out.println("path:"+path);
    
         File file = new File(staticDir,path);
    
  
    
         int statusCode;//状态代码
    
         String statusReason;//状态描述
    
         if(file.isFile()){//找到了用户请求的资源
    
             System.out.println("该资源存在");
    
             statusCode=200;
    
             statusReason="OK";
    
         }else{
    
             System.out.println("该资源不存在");
    
             statusCode=404;
    
             statusReason="NotFound";
    
             file = new File(staticDir,"404.html");
    
         }
    
         //3发送响应
    
         //3.1发送状态行
    
         println("HTTP/1.1"+" "+statusCode+" "+statusReason);
    
         //3.2发送响应头
    
         println("Content-Type: text/html");
    
         println("Content-Length: "+file.length());
    
         //单独发送回车+换行(空行)表达响应头发送完毕
    
         println("");
    
         //3.3发送响应正文
    
         OutputStream out = socket.getOutputStream();
    
         FileInputStream fis = new FileInputStream(file);
    
         byte[] buf = new byte[1024*10];
    
         int len;
    
         while((len=fis.read(buf))!=-1){
    
             out.write(buf,0,len);
    
         }
    
  
    
  
    
  
    
     } catch (IOException e) {
    
         e.printStackTrace();
    
     } catch (URISyntaxException e) {
    
         e.printStackTrace();
    
     } finally {
    
         try {
    
             socket.close();//协议要求一次交互后断开TCP连接
    
         } catch (IOException e) {
    
             e.printStackTrace();
    
         }
    
     }
    
     }
    
  
    
     /** * 向客户端发送一行字符串,会自动在指定字符串后面添加回车+换行(CRLF)
    
      */
    
     private void println(String line) throws IOException {
    
     OutputStream out = socket.getOutputStream();
    
     byte[] data = line.getBytes(StandardCharsets.ISO_8859_1);
    
     out.write(data);
    
     out.write(13);//发送回车符
    
     out.write(10);//发送换行符
    
     }
    
  
    
  
    
 }
    
    
    
    

5.1新建服务器响应对象(HttpServerResponse) 封装响应

复制代码
 package com.webserver.http;

    
  
    
 import java.io.File;
    
 import java.io.FileInputStream;
    
 import java.io.IOException;
    
 import java.io.OutputStream;
    
 import java.net.Socket;
    
 import java.nio.charset.StandardCharsets;
    
  
    
 /** * 响应对象
    
  * 该类的每一个实例用于表示服务端给浏览器回复的HTTP响应
    
  * HTTP协议要求的响应格式由三部分构成:
    
  * 状态行,响应头,响应正文
    
  * */
    
 public class HttpServletResponse {
    
     private Socket socket;
    
     //状态行相关信息
    
     private int statusCode = 200;//状态代码,默认值为200
    
     private String statusReason = "OK";//状态描述,默认值为"OK"
    
  
    
     //响应头相关信息
    
  
    
     //响应正文相关信息
    
     private File entity;//响应正文对应的实体文件
    
  
    
  
    
     public HttpServletResponse(Socket socket){
    
     this.socket = socket;
    
     }
    
  
    
     /** *  将当前响应对象的内容以标准的响应格式发送给浏览器
    
      */
    
     public void response() throws IOException {
    
     //3.1发送状态行
    
     println("HTTP/1.1"+" "+statusCode+" "+statusReason);
    
     //3.2发送响应头
    
     println("Content-Type: text/html");
    
     println("Content-Length: "+entity.length());
    
     //单独发送回车+换行(空行)表达响应头发送完毕
    
     println("");
    
     //3.3发送响应正文
    
     OutputStream out = socket.getOutputStream();
    
     FileInputStream fis = new FileInputStream(entity);
    
     byte[] buf = new byte[1024*10];
    
     int len;
    
     while((len=fis.read(buf))!=-1){
    
         out.write(buf,0,len);
    
     }
    
     }
    
  
    
  
    
     /** * 向客户端发送一行字符串,会自动在指定字符串后面添加回车+换行(CRLF)
    
      */
    
     private void println(String line) throws IOException {
    
     OutputStream out = socket.getOutputStream();
    
     byte[] data = line.getBytes(StandardCharsets.ISO_8859_1);
    
     out.write(data);
    
     out.write(13);//发送回车符
    
     out.write(10);//发送换行符
    
     }
    
  
    
     public int getStatusCode() {
    
     return statusCode;
    
     }
    
  
    
     public void setStatusCode(int statusCode) {
    
     this.statusCode = statusCode;
    
     }
    
  
    
     public String getStatusReason() {
    
     return statusReason;
    
     }
    
  
    
     public void setStatusReason(String statusReason) {
    
     this.statusReason = statusReason;
    
     }
    
  
    
     public File getEntity() {
    
     return entity;
    
     }
    
  
    
     public void setEntity(File entity) {
    
     this.entity = entity;
    
     }
    
 }
    
  
    
  
    
  
    
    
    
    

ClientHandler调用

复制代码
 package com.webserver.core;

    
  
    
 import com.webserver.http.HttpServletRequest;
    
 import com.webserver.http.HttpServletResponse;
    
  
    
 import java.io.*;
    
 import java.net.Socket;
    
 import java.net.URISyntaxException;
    
 import java.nio.charset.StandardCharsets;
    
 import java.util.HashMap;
    
 import java.util.Map;
    
  
    
 /** * 与指定的客户端完成一次HTTP交互
    
  * HTTP协议要求浏览器与服务端交互采取一问一答的原则,因此服务端这里处理一次交互由
    
  * 三步骤完成(参考【服务器处理流程示意图】):
    
  * 1:解析请求
    
  * 2:处理请求
    
  * 3:发送响应
    
  */
    
 public class ClientHandler implements Runnable {
    
     private Socket socket;
    
  
    
     public ClientHandler(Socket socket) {
    
     this.socket = socket;
    
     }
    
  
    
     public void run() {
    
     try {
    
         //1解析请求
    
         HttpServletRequest request = new HttpServletRequest(socket);
    
         HttpServletResponse response = new HttpServletResponse(socket);
    
  
    
         //2处理请求
    
         File root = new File(ClientHandler.class.getClassLoader().getResource(".").toURI());
    
         File staticDir = new File(root,"static");
    
         //根据浏览器地址栏中抽象路径定位static下对应的文件
    
         String path = request.getUri();//获取请求路径
    
         System.out.println("path:"+path);
    
         File file = new File(staticDir,path);
    
         if(file.isFile()){//找到了用户请求的资源
    
             System.out.println("该资源存在");
    
             response.setEntity(file);
    
         }else{
    
             System.out.println("该资源不存在");
    
             response.setStatusCode(404);
    
             response.setStatusReason("NotFound");
    
             file = new File(staticDir,"404.html");
    
             response.setEntity(file);
    
         }
    
  
    
         //3发送响应
    
         response.response();
    
  
    
  
    
  
    
     } catch (IOException e) {
    
         e.printStackTrace();
    
     } catch (URISyntaxException e) {
    
         e.printStackTrace();
    
     } finally {
    
         try {
    
             socket.close();//协议要求一次交互后断开TCP连接
    
         } catch (IOException e) {
    
             e.printStackTrace();
    
         }
    
     }
    
     }
    
  
    
  
    
  
    
  
    
 }
    
    
    
    

6.添加调度服务类(ClientHandler)封装处理请求

复制代码
 package com.webserver.core;

    
  
    
 import com.webserver.http.HttpServletRequest;
    
 import com.webserver.http.HttpServletResponse;
    
  
    
 import java.io.File;
    
 import java.net.URISyntaxException;
    
  
    
 /** * 该类实际上是SpringMVC框架提供的一个核心类,用于和Tomcat整合并接收处理请求工作的类。
    
  * 原生的Tomcat开发,处理业务的类都需要定义对应的一个Servlet类并重写service方法。然后在
    
  * Tomcat中进行XML对应的配置。
    
  */
    
 public class DispatcherServlet {
    
     private static File root;
    
     private static File staticDir;
    
     static{
    
     try {
    
         root = new File(ClientHandler.class.getClassLoader().getResource(".").toURI());
    
         staticDir = new File(root,"static");
    
     } catch (URISyntaxException e) {
    
         e.printStackTrace();
    
     }
    
     }
    
  
    
     public void service(HttpServletRequest request, HttpServletResponse response){
    
     //根据浏览器地址栏中抽象路径定位static下对应的文件
    
     String path = request.getUri();//获取请求路径
    
     System.out.println("path:"+path);
    
     File file = new File(staticDir,path);
    
     if(file.isFile()){//找到了用户请求的资源
    
         System.out.println("该资源存在");
    
         response.setEntity(file);
    
     }else{
    
         System.out.println("该资源不存在");
    
         response.setStatusCode(404);
    
         response.setStatusReason("NotFound");
    
         file = new File(staticDir,"404.html");
    
         response.setEntity(file);
    
     }
    
     }
    
 }
    
    
    
    

ClientHandler调用

复制代码
 package com.webserver.core;

    
  
    
 import com.webserver.http.HttpServletRequest;
    
 import com.webserver.http.HttpServletResponse;
    
  
    
 import java.io.*;
    
 import java.net.Socket;
    
 import java.net.URISyntaxException;
    
 import java.nio.charset.StandardCharsets;
    
 import java.util.HashMap;
    
 import java.util.Map;
    
  
    
 /** * 与指定的客户端完成一次HTTP交互
    
  * HTTP协议要求浏览器与服务端交互采取一问一答的原则,因此服务端这里处理一次交互由
    
  * 三步骤完成(参考【服务器处理流程示意图】):
    
  * 1:解析请求
    
  * 2:处理请求
    
  * 3:发送响应
    
  */
    
 public class ClientHandler implements Runnable {
    
     private Socket socket;
    
  
    
     public ClientHandler(Socket socket) {
    
     this.socket = socket;
    
     }
    
  
    
     public void run() {
    
     try {
    
         //1解析请求
    
         HttpServletRequest request = new HttpServletRequest(socket);
    
         HttpServletResponse response = new HttpServletResponse(socket);
    
  
    
         //2处理请求
    
         DispatcherServlet servlet = new DispatcherServlet();
    
         servlet.service(request,response);
    
  
    
         //3发送响应
    
         response.response();
    
  
    
  
    
  
    
     } catch (IOException e) {
    
         e.printStackTrace();
    
     } finally {
    
         try {
    
             socket.close();//协议要求一次交互后断开TCP连接
    
         } catch (IOException e) {
    
             e.printStackTrace();
    
         }
    
     }
    
     }
    
  
    
  
    
  
    
  
    
 }
    
    
    
    

全部评论 (0)

还没有任何评论哟~