仿寫(xiě)之前,我們要搞清楚都要用到哪些技術(shù)
- 自定義注解,比如Tomcat使用的是@Servlet,我們可以定義一個(gè)自己的@MyServlet
- 構(gòu)造請(qǐng)求體和返回體,比如tomcat使用HttpRequest,我們可以自己定義myHttpRequest
- java去遍歷一個(gè)指定目錄,然后獲取到.java文件,再獲取到帶有@MyServlet注解的類
- 然后將這個(gè)注解里的path和這個(gè)類本身映射成map
- 通過(guò)反射去調(diào)用該類的方法(doGet、doPost)
- 還需要用到socket來(lái)監(jiān)聽(tīng)消息,并且對(duì)監(jiān)聽(tīng)到的消息進(jìn)行處理
第一步:自定義注解
@Target(ElementType.TYPE)
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface MyServlet {
String path() default "";
}
第二步:定義HttpRequest以及HttpResponse、
public class MyHttpRequest {
//定義一個(gè)map,用來(lái)存放請(qǐng)求體中的參數(shù),key是參數(shù)名稱,value是參數(shù)值
public Map<String,String> map = new HashMap<>();
public String getParameter(String key){
return map.get(key);
}
}
public class MyHttpResponse {
public OutputStream outputStream;
public static final String responsebody = "HTTP/1.1 200+\r\n" + "Content-Type:text/html+\r\n"
+ "\r\n";
public MyHttpResponse(OutputStream outputStream) {
this.outputStream = outputStream;
}
}
第三步:遍歷整個(gè)目錄,把Java文件放入list中
private static void func(File file){
File[] files = file.listFiles();
String s;
for (File file1 : files) {
if (file1.isDirectory()){
func(file1);
}
if (file1.isFile()){
//取src之后的名字
s = file1.toString().split("src")[1];
//去掉src后邊的第一個(gè)\,得到全類名
s = s.substring(1);
//判斷是不是以.java結(jié)尾的文件
if (s.length() >=5 && s.substring(s.length() - 5).equals(".java")){
//把全類名中的\替換成.
s = s.replace('\\','.');
//去掉后綴名.java
s = s.substring(0,s.length()-5);
//把類名加入到list中
javaclasses.add(s);
}
}
}
}
第四步:找出帶有Servlet注解的Java文件,并把注解中的path,類對(duì)象放入到map中
public static void getServlet() throws ClassNotFoundException {
for (int i = 0; i < javaclasses.size(); i++) {
String path = javaclasses.get(i);
Class<?> cl = Class.forName(path);
if (cl.isAnnotationPresent(MyServlet.class)){
servletMap.put(cl.getAnnotation(MyServlet.class).path(),cl);
}
}
}
第五步:創(chuàng)建socket連接
InetAddress localHost = InetAddress.getLocalHost();
System.out.println("localhost" + localHost);
ServerSocket serverSocket = new ServerSocket(8080, 10, localHost);
System.out.println("等待建立連接");
Socket server = serverSocket.accept();
System.out.println("連接已建立");
第六步:定義線程接收?qǐng)?bào)文
HttpAcceptThread httpAcceptThread = new HttpAcceptThread(server);
Thread accept = new Thread(httpAcceptThread);
accept.start();
accept.join();
HttpAcceptThread類內(nèi)容如下:
class HttpAcceptThread implements Runnable{
private Socket socket;
ArrayList<String> strings = new ArrayList<>();
public HttpAcceptThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
System.out.println("開(kāi)始接收http");
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String s;
while ((s = reader.readLine()).length() != 0){
try {
strings.add(s);
System.out.println(s);
} catch (Exception e){
System.out.println("接收Http進(jìn)程結(jié)束");
break;
}
}
System.out.println("接收http進(jìn)程結(jié)束");
} catch (IOException e) {
e.printStackTrace();
}
}
}
第七步:處理httprequest,也就是通過(guò)反射去調(diào)用doGet和doPost方法
這一步有些復(fù)雜,尤其是對(duì)url切割時(shí),但我給每一步都加了注釋,方便理解
? ? ? ? ? ? ?GET /address1?a=111&b=222
private static void requestHttp(Socket socket,String http) throws IOException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
//GET /address1?a=111&b=222(拿獲取到的這個(gè)url舉例)
//先通過(guò)空格判斷是GET還是POST
String requestStyle = http.split(" ")[0];
if (requestStyle.equals("GET")){
//如果是GET,取空格后面部分,即/address1?a=111&b=222
String httpPathAndParameter = http.split(" ")[1];
//定義httpPath
String httpPath;
//創(chuàng)建httpRequest對(duì)象
MyHttpRequest myHttpRequest = new MyHttpRequest();
//通過(guò)索引位置判斷url里邊有沒(méi)有帶?
if (httpPathAndParameter.indexOf("?") != -1){
//如果有,由于有個(gè)/,因此我們要先拿到address1?a=111&b=222這部分
httpPath = httpPathAndParameter.substring(1);
//獲取問(wèn)號(hào)前面部分,即address1,\\作為轉(zhuǎn)義字符使用
httpPath = httpPath.split("\\?")[0];
System.out.println(httpPath);
//獲取問(wèn)號(hào)后面部分的所有參數(shù)
String parameterString = httpPathAndParameter.split("\\?")[1];
//使用&分開(kāi)
String[] parameters = parameterString.split("&");
for (int i = 0; i < parameters.length; i++) {
//把參數(shù)及其值仿佛request的map中
myHttpRequest.map.put(parameters[i].split("=")[0],parameters[i].split("=")[1]);
}
} else {
//如果不存在?,也就說(shuō)明不存在參數(shù),我們只需要獲取httpPath
httpPath = httpPathAndParameter.substring(1);
System.out.println(httpPath);
}
//創(chuàng)建HttpResponse對(duì)象
OutputStream outputStream = socket.getOutputStream();
MyHttpResponse myHttpResponse = new MyHttpResponse(outputStream);
//反射調(diào)用doGet
Class servletClass = servletMap.get(httpPath);
Method doGet = servletClass.getMethod("doGet", MyHttpRequest.class, MyHttpResponse.class);
doGet.invoke(servletClass.newInstance(),myHttpRequest,myHttpResponse);
} else {
//如果不是Get請(qǐng)求,也按照同樣的步驟,先取出/address1
String httpPath = http.split(" ")[1];
//去掉/,只留下address1
httpPath = httpPath.substring(1);
System.out.println(httpPath);
MyHttpRequest myHttpRequest = new MyHttpRequest();
OutputStream outputStream = socket.getOutputStream();
MyHttpResponse myHttpResponse = new MyHttpResponse(outputStream);
//根據(jù)httpPath取出類信息
Class servletClass = servletMap.get(httpPath);
//獲取doPost方法
Method doPost = servletClass.getMethod("doPost", MyHttpRequest.class, MyHttpResponse.class);
//調(diào)用doPost方法
doPost.invoke(servletClass.newInstance(),myHttpRequest,myHttpResponse);
}
}
最后一步:把上面這些方法整合起來(lái),在主方法中調(diào)用,同時(shí)定義好全局變量
public class MyTomcat {
//用于存放Java類的全類名
public static ArrayList<String> javaclasses = new ArrayList<>();
//用于存放Servlet的類對(duì)象,其中key是Servlet的url,value是servlet的類對(duì)象
public static HashMap<String,Class> servletMap = new HashMap<>();
public static void main(String[] args) throws ClassNotFoundException, IOException, InterruptedException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
String inputPath = "D:\\JavaProject\\practice\\src\\tomcat";
File file = new File(inputPath);
//獲取.java后綴文件,并獲取全類名
func(file);
System.out.println(javaclasses);
//獲取帶有servlet注解的類對(duì)象,并放到map中。
getServlet();
System.out.println(servletMap);
InetAddress localHost = InetAddress.getLocalHost();
System.out.println("localhost" + localHost);
ServerSocket serverSocket = new ServerSocket(8080, 10, localHost);
System.out.println("等待建立連接");
Socket server = serverSocket.accept();
System.out.println("連接已建立");
//定義線程接收http報(bào)文
HttpAcceptThread httpAcceptThread = new HttpAcceptThread(server);
Thread accept = new Thread(httpAcceptThread);
accept.start();
accept.join();
//處理請(qǐng)求
requestHttp(server,httpAcceptThread.strings.get(0));
}
然后就可以進(jìn)行測(cè)試了,在測(cè)試類上方加上我們已經(jīng)定義好的@MyServlet注解
@MyServlet(path = "address1")
public class Servlet1 {
public void doGet(MyHttpRequest request, MyHttpResponse response) throws IOException {
System.out.println("address1 GET響應(yīng):");
System.out.println("a=" + request.getParameter("a"));
System.out.println("\n響應(yīng)的http如下:");
String resp = MyHttpResponse.responsebody + "<!DOCTYPE html>\n" +
"<html>\n" +
"<head>\n" +
" <meta charset=\"utf-8\" />\n" +
"</head>\n" +
"<body>\n" +
" \n" +
" <form name=\"my_form\" method=\"POST\">\n" +
" <input type=\"button\" value=\"按下\" onclick=\"alert('你按下了按鈕')\">\n" +
" </form>\n" +
" \n" +
"</body>\n" +
"</html>";
System.out.println(resp);
response.outputStream.write(resp.getBytes());
response.outputStream.flush();
response.outputStream.close();
}
public void doPost(MyHttpRequest request, MyHttpResponse response) throws IOException {
System.out.println("\n響應(yīng)的http如下:");
String resp = MyHttpResponse.responsebody +
"{\"sorry\":\"we only respond to method GET now\"},\r\n" +
"";
System.out.println(resp);
response.outputStream.write(resp.getBytes());
response.outputStream.flush();
response.outputStream.close();
}
}
然后啟動(dòng)項(xiàng)目
?可以看到本機(jī)ip地址,然后通過(guò)瀏覽器地址欄訪問(wèn)
文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-662587.html
?這樣就實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的tomcat文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-662587.html
到了這里,關(guān)于如何仿寫(xiě)簡(jiǎn)易tomcat 實(shí)現(xiàn)思路+代碼詳細(xì)講解的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!