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)
还没有任何评论哟~
