目录
Tomcat原理(3)------静&动态资源以及运行项目的基本流程-CSDN博客文章浏览阅读414次,点赞2次,收藏2次。Tomcat原理(2)------注解及注解的实现-CSDN博客(1)注解一般用于对程序的说明,就像注释一样,但是区别是注释是给人看的,但是注解是给程序看的。(2)让编译器进行编译检查的作用,比如下边这个@Override注解是重写的意思,子类重写了父类的方法,但是改动了方法名,所以报错。https://blog.csdn.net/2301_78566776/article/details/144508887?spm=1001.2014.3001.5502 我们在上一篇博客中已经了解到了静动态资源和tomcat的基本流程
一、什么是Servlet
1.servlet的定义
Servlet,全称为Java Servlet,是运行在Java服务器端的程序,它主要用于接收和响应来自客户端基于HTTP协议的请求。Servlet可以看作是在服务器上运行的小程序,用于生成动态Web内容。
2.servlet的结构
这是一个servlet的基本结构。我们观察可以发现:
- Servlet继承了一个HttpServlet抽象类
- servlet文件包含两个主要的方法:doGet和doPost
- 观察这两个方法的入参 :一个是request 一个是response。
二、实现servlet的流程图
1.我们首先需要构造一个Server服务器模拟,来接受HTTP请求,并且返回method(get/post)
2.创建request实体类和respons实体类,在request实体类中设置method和path变量
3.创建HttpServlet抽象类,在类里定义doGet和doPost两个抽象方法,并且判断所传method为什么类型,进行选择执行。
4.具体执行的doGet和doPost方法,要在具体的servlet中执行(找抽象类的具体实现方法)------即我们每一个servlet都要对doGet和doPost进行重写。
如图所示
三、具体实现代码
引入
我们尝试接收整个http请求的信息
javapackage com.qcby.tomcat.webservlet;/* * 服务器端 tomcat ------》接收信息 * */ import java.io.IOException; import java.io.InputStream; import java.net.ServerSocket; import java.net.Socket; public class SocketServer { public static void main(String[] args) throws IOException { run(); } public static void run() throws IOException { ServerSocket serverSocket= new ServerSocket(8080); //端口的范围:0~65535 while (true){//等待客户端连接 Socket socket = serverSocket.accept();//阻塞监听:程序会在这里卡住。只有监听到客户端的信息后才会向下执行 //输出客户端给我们发来的程序 InputStream inputStream=socket.getInputStream();//打开输入流:接收输入的信息 int count=0; while (count==0){ count=inputStream.available(); } byte[] bytes=new byte[count];//01010101010100001101010 用字节数组接收 inputStream.read(bytes); String context=new String(bytes); System.out.println(context); } } }
结果如图
我们可以看到这个请求的全部信息。第一行的前两个词为其method/path,在一会的server文件中我们只要前两个词。
1、server
这个类中我们获取到了http请求的method(get/post)和path(@WebServlet中的path)
获取的方法是对所有请求信息进行切割。并且在最后我们将method和path放入了request中
java
package com.qcby.tomcat.socket;
import com.qcby.tomcat.Request.Request;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
private static Request request=new Request();
public static void main(String[] args) throws Exception {
// 1.打开通信端口 tomcat:8080 3306 ---------》进行网络通信
ServerSocket serverSocket = new ServerSocket(8080);
System.out.println("****************server start.....");
//2.接受请求数据
while (true) {
Socket socket = serverSocket.accept(); //--------------------->注意:此时监听网卡的是:主线程
System.out.println("有客户进行了链接");
new Thread(() -> {
//处理数据---------》数据的处理在于读和写
try {
handler(socket);
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}
public static void handler(Socket socket) throws Exception {
//读取请求的数据
InputStream inputStream = socket.getInputStream();
requestContext(inputStream);
}
public static void requestContext(InputStream inputStream) throws IOException {
// 创建一个StringBuilder对象,用于构建请求的第一行
StringBuilder sb = new StringBuilder();
int context; // 用于存储每次从输入流中读取的单个字节
// 读取输入流直到遇到换行符(\n)或文件结束(-1)
while ((context = inputStream.read()) != -1) {
// 如果读取到换行符,则停止读取
if (context == '\n') {
break; // 遇到换行符,退出循环
}
// 将读取到的字节转换为字符,并添加到StringBuilder中
sb.append((char) context);
}
// 从StringBuilder中获取第一行字符串,并去除首尾空格
String firstLine = sb.toString().trim();
// 检查第一行是否为空
if (firstLine.isEmpty()) {
// 如果为空,则打印提示信息
System.out.println("你输入了一个空请求");
} else {
// 如果不为空,则按空格分割第一行字符串为单词数组
String[] words = firstLine.split("\\s+");
// 打印出请求方法和请求URI(通常是数组的前两个元素)
// 注意:这里没有检查数组长度,如果数组长度小于2,将会抛出ArrayIndexOutOfBoundsException
// 在实际应用中,应该添加适当的错误处理或验证逻辑
String method=words[0];
String path=words[2];
System.out.println(words[0] + " " + words[1]);
request.setMethod(method);
request.setPath(path);
}
}
}
@WebServlet接口
javapackage com.qcby.tomcat.webservlet; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(value = ElementType.TYPE) @Retention(value = RetentionPolicy.RUNTIME) public @interface WebServlet { String path() default ""; }
2.实体类request&response
生成了get和set方法
生成了全参和无参的构造函数
java
package com.qcby.tomcat.Request;
public class Request {
private String path;
private String method;
public Request(String path, String method) {
this.path = path;
this.method = method;
}
public Request() {
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
}
在这里 response我并没有实现
java
package com.qcby.tomcat.Response;
public class Response {
}
3.HttpServlet抽象类
抽象类中既可以有抽象方法也可以有具体方法。
java
package com.qcby.tomcat.HttpServlet;
import com.qcby.tomcat.Request.Request;
import com.qcby.tomcat.Response.Response;
public abstract class HttpServlet {
public void service(Request request, Response response){
if(request.getMethod().equals("GET")){
doGet(request,response);
}else if (request.getMethod().equals("POST")){
doPost(request,response);
}else if (request.getMethod().equals("")){
System.out.println("未获取到方法类型");
}
}
public abstract void doGet(Request request,Response response);
public abstract void doPost(Request request,Response response);
}
4.再定义三个servlet进行测试
注意!每个servlet都有继承HttpServlet,并且在每个servlet中都要重写doGet和doPost
java
package com.qcby.tomcat.MyWeb;
import com.qcby.tomcat.HttpServlet.HttpServlet;
import com.qcby.tomcat.Request.Request;
import com.qcby.tomcat.Response.Response;
import com.qcby.tomcat.webservlet.WebServlet;
@WebServlet(path ="myFirstServlet")
public class MyFirstServlet extends HttpServlet {
@Override
public void doGet(Request request, Response response) {
}
@Override
public void doPost(Request request, Response response) {
}
}
java
package com.qcby.tomcat.MyWeb;
import com.qcby.tomcat.HttpServlet.HttpServlet;
import com.qcby.tomcat.Request.Request;
import com.qcby.tomcat.Response.Response;
import com.qcby.tomcat.webservlet.WebServlet;
@WebServlet(path ="mySecondServlet")
public class MySecondServlet extends HttpServlet {
@Override
public void doGet(Request request, Response response) {
}
@Override
public void doPost(Request request, Response response) {
}
}
java
package com.qcby.tomcat.MyWeb;
import com.qcby.tomcat.HttpServlet.HttpServlet;
import com.qcby.tomcat.Request.Request;
import com.qcby.tomcat.Response.Response;
import com.qcby.tomcat.webservlet.WebServlet;
@WebServlet(path ="myThirdServlet")
public class MyThirdServlet extends HttpServlet {
@Override
public void doGet(Request request, Response response) {
}
@Override
public void doPost(Request request, Response response) {
}
}
我们会发现它已经很像Java的servlet文件模板了
补充:
如何读取一个软件包,遍历里面的servlet,返回其类名和注解的path值
javapackage com.qcby.tomcat; import com.qcby.tomcat.webservlet.WebServlet; import java.io.File; import java.net.URL; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; public class MyTomcat { public static void main(String[] args) { try { // 1. 扫描包路径 (com.wzh.tomcat.myweb) String packageName = "com.qcby.tomcat.MyWeb"; List<Class<?>> classes = getClasses(packageName); // 2. 遍历所有类,检查是否有@WebServlet注解 for (Class<?> clazz : classes) { if (clazz.isAnnotationPresent(WebServlet.class)) { // 3. 获取@WebServlet注解的值 WebServlet webServlet = clazz.getAnnotation(WebServlet.class); System.out.println("类名: " + clazz.getName() + " | URL路径: " + webServlet.path()); } } } catch (Exception e) { e.printStackTrace(); } } /** * 获取指定包下的所有类 * * @param packageName 包名,例如 "com.wzh.tomcat.myweb" * @return 类对象列表 * @throws Exception */ private static List<Class<?>> getClasses(String packageName) throws Exception { List<Class<?>> classes = new ArrayList<>(); String path = packageName.replace('.', '/'); // 将包名转换为文件路径 // 通过类加载器获取包的资源路径 ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); Enumeration<URL> resources = classLoader.getResources(path); while (resources.hasMoreElements()) { URL resource = resources.nextElement(); File directory = new File(resource.toURI()); // 扫描文件夹下的所有类文件 if (directory.exists()) { for (File file : directory.listFiles()) { if (file.getName().endsWith(".class")) { // 获取类的完整类名 String className = packageName + "." + file.getName().replace(".class", ""); classes.add(Class.forName(className)); } } } } return classes; } }