Servlet is a technology for implementing dynamic pages . It is a set of APIs provided by Tomcat to programmers to help programmers develop a
web app simply and efficiently.
Dynamic pages vs static pages:
- A static page is a page whose content is always fixed. Even if the user is different/the time is different/the input parameters are different , the page content will not
change (unless the website developer modifies the source code, the page content will always remain unchanged) - Correspondingly, dynamic pages refer to different users/different times/different input parameters , and the page content will change.
There are many technologies for building dynamic pages. Each language has some related libraries/frameworks to do this.
Servlet is a set of APIs provided by Tomcat, the HTTP server, to Java to complete the task of building dynamic pages.
The main work done by Servlet:
- Allow programmers to register a class and execute some code in this class when Tomcat receives a specific HTTP request.
- Help programmers parse HTTP requests from a string into an HttpRequest object
- Helps programmers construct HTTP responses. Programmers only need to fill in some attribute fields for the specified HttpResponse object. Servlet
- It will automatically install the HTTP protocol to construct an HTTP response string and write it back to the client through Socket.
In short, Servlet is a set of APIs provided by Tomcat, which allows programmers to write their own code to work well with Tomcat, making it easier to implement a web app without having to pay attention to Socket, HTTP protocol format, and multi-thread concurrency
. and other technical details, which lowers the threshold for web app development and improves development efficiency.
Hello World!
1. Create a maven project
2. Introduce dependencies
The Servlet API needs to be introduced into the code . This API is not built into the JDK, but is provided by a third party (provided by Tomcat)
. Tomcat is still a third party compared to Java officials.
It can be introduced directly with the help of maven. The first one to search for servlet is,
Use this version:
JDK, Tomcat, and Servlet versions must be matched . If the versions are not compatible, there may be some problems~~
(It is not that it cannot be used at all. There is a high probability that most functions are normal, but a few functions have bugs)
- JDK 8
- Tomcat 8.5
- Servlet 3.1
The location of maven's default warehouse:
3. Create directory structure
- Although maven has created some directories for us, it is not enough to support us in writing a Servlet project.
- Therefore, you need to manually create some directories and files ~~
There are also simpler methods, but I won’t introduce them here. Let’s use the simplest way to do it manually.
--Create a directory main
under webapp
. Tomcat can load multiple webapps at the same time, so the directory is webapps (each webapp is equivalent to a website), because here we are writing a webapp and there is no s
——Second directoryWEB-INF
--Create a fileweb.xml
Next, you need to web.xml
write some content (cannot be empty)
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
</web-app>
4. Write Servlet code
// 如果代码中找不到 HttpServlet 这个类 说明依赖没有被正确引入
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// super.doGet(req, resp);
}
}
do: Process
Get corresponding to the HTTP GET method. This method will be called by Tomcat (callback function) when
Tomcat receives an HTTP GET request.
HttpServletRequest
: Represents an HTTP request (Tomcat has received it and parsed it into an object )
HttpServletResponse
: Represents HTTP response (currently the resp here is an empty response object , and you need to set some properties for this object in the code)
What the doGet method does is to calculate and generate a response based on the request~~
The workflow of a server can be divided into three typical steps:
- Receive requests and parse
- Compute response based on request
- Construct response data and return to client
Among them, Tomcat has already done these two steps 1 3 for us. This is the logic that our programmers want to implement themselves, which is what doGet wants to implement~~
Imagine the server is like a restaurant~~
The code running on the server must handle requests one by one~~
When the boss received the request for "Japanese noodles," the chef took action to make the noodles according to the customer's request~
In the kitchen, there is a chef who is responsible for cutting vegetables, a chef who is responsible for ramen noodles, another chef who is responsible for frying sauce, and another chef who integrates the results here~~
The waiter can bring me this noodle~~
The concurrent work of these three masters is equivalent to three threads!!
@WebServlet("/hello")
// 如果代码中找不到 HttpServlet 这个类 说明依赖没有被正确引入
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 这个代码一定要干掉! 不能调用父类的 doGet!
// super.doGet(req, resp);
// 这是让服务器在自己的控制台里打印
System.out.println("hello world!");
// 在页面上也能打印,把 hello world! 字符串放到 http 响应的 body 中,浏览器就会把 body 的内容显示到页面上
resp.getWriter().write("hello word!" + System.currentTimeMillis());
}
}
resp is the response object
getWriter. In fact, it returns a Writer object (character stream object).
The Writer object here does not write to the file, but writes data to the body of the HTTP response.
write()
It is the real work and method of writing data.
1、 @WebServlet("/hello")
Associate the current HelloServlet class with requests such as /hello in the URL in the HTTP request .
- Tomcat may receive many kinds of requests
/123 http://123.123.123.123:8080/123
/aaa http://123.123.123.123:8080/aaa
/test http://123.123.123.123:8080/test- With so many requests, our code only handles one of them : /hello http://123.123.123.123:8080/hello.
Only then will Tomcat call the HelloServlet class for processing.
2. Ensure uniformity of methods
- /hello is associated with the HelloServlet class
- But if you want to execute doGet, you must ensure that the method is also a GET method~~
- If it is POST, /hello, still cannot execute doGet ~~
5. Packing
- The current code cannot be run alone (there is no main method)
- The current code needs to be packaged and then deployed to Tomcat, which will be called by Tomcat.
Preparation: Modification pom.xml
~~
jar
and war
are both compressed package formats used by Java publishing programs
war
. They are specially developed for Tomcat. It will not only include some .class
, but also include configuration files, dependent third-party jars, and html css...
Double-click this package to trigger packaging
The prompt BUILD SUCCESS indicates that the packaging was successful!!
6. Deployment
Copy the war package to the webapps directory of tomcat and start Tomcat
7. Verification procedure
When we access Tomcat, we need to specify two-level directories.
-
Context Path : The reason why our first-level path is called hello102 is because our
war
package name is hello102,war
and the path obtained after decompression is also hello102 -
Servlet Path :
@WebServlet("/hello")
This annotation matches the second-level path
It can be thought of like this:
multiple websites can be deployed on one Tomcat at the same time, and there are multiple pages on one website~~ The
first-level path in a request tells Tomcat which website to visit , and the second-level path is Tell Tomcat which page in this website the page you want to visit is
Visit : http://127.0.0.1:8080/hello102/hello
It was unsuccessful. At this time, in the tomcat console, I hit Enter and it came out. The hello world displayed is the body part of the returned HTTP response.
This situation is a big pitfall for cmd!!!
CMD has a "select text" mode like this
Selecting a piece of text with the mouse will trigger the selected text mode of CMD~~
When CMD selects the text, CMD will suspend the program running inside (paused and will not continue)
- Now the content of this page is generated through Java code , but what is the difference between this and you directly creating an HTML and writing hello world in it?
- The content on the page is fixed, static, and unchanging~~
- The content here is variable !!
- Depending on the user's input, different results can be obtained!!!
Modify the code : get timestamp
resp.getWriter().write("hello word!" + System.currentTimeMillis());
The above seven steps are for a new project~~
After the project is created, you do not need to repeat the first three steps if you subsequently modify the code. You can just proceed from 4 to 7~~
When redeploying, no need to repeat the steps. Be sure to restart tomcat (theoretically, there is no need to restart, but the Windows system is somewhat unsmooth~~, generally on Linux, this can be automatically reloaded smoothly~~)
If you see a prompt like this~~ it has been redeployed, deploy: deploy
Display results: hello word!1653073391039
smart tomcat
1 Introduction
The steps introduced above are actually the simplest operations~~
Therefore, there are some ways to improve the efficiency of the above process. You can use some third-party tools to simplify the operations of 5 and 6. After all, every time you modify the code, you need to Repackage and redeploy~~
We directly integrated Tomcat through the plug-in on IDEA ~~ We achieved "one-click" completion of packaging and deployment~~
Note: Tomcat and IDEA are two independent programs!!! Tomcat is not part of the IDEA functionality!!
Later development was mainly carried out by calling Tomcat through IDEA.
After using it for a long time, the students' impression of Tomcat began to become blurry~~
Smart tomcat is a plug-in of idea.
Although idea itself has many functions, it cannot cover everything!!
Therefore, idea also provides a series of extension capabilities, allowing third parties to develop some programs and let idea run them. It is equivalent to an expansion of the idea~~
smart tomcat is not really packaged~~
In fact, it is equivalent to setting the webapp directory as a parameter for tomcat startup, and letting tomcat load the webapp directly from this specified directory.
Tomcat loads war packages from webapps by default, and this is not the only way~~
So when you look at Tomcat's webapps directory, you will find that your war is not actually there~~ (unlike manual copying)
2. Installation
3. Use smart tomcat
In a project, when using it for the first time, you need to configure it briefly first , and you don’t need to configure it again~~
When using Smart Tomcat, the Context Path is not the war package name, but the name specified in the Smart Tomcat configuration page.
Click the triangle number here, and a series of operations such as packaging, deployment, and restarting tomcat will be automatically completed.
Error message : Caused by: java.net.BindException: Address already in use: bind
If a port has been bound by the server and you start a program to bind the same port again, this error will occur!!!
This problem currently exists because a Tomcat has already been started in the command line.
If you start another Tomcat in the idea, obviously the port cannot be occupied repeatedly.
There is no problem at this point. These words are garbled in cmd, but they are no longer garbled in the idea terminal.
org.apache.catalina.util.SessionIdGeneratorBase.createSecureRandom 使用[SHA1PRNG]创建会话ID生成的SecureRandom实例花费了[103]毫秒。
21-May-2022 03:36:58.905 信息 [main] org.apache.coyote.AbstractProtocol.start 开始协议处理句柄["http-nio-8080"]
21-May-2022 03:36:58.915 信息 [main] org.apache.catalina.startup.Catalina.start Server startup in 370 ms
http://localhost:8080/hello102
Summary of common errors
1、404
The resource you want to access does not exist on the server
Error 1: The resource path you requested is incorrect
Error 2: Although the path is correct, the server did not load the resource correctly~~
The first-level path Context Path is missing:
The second-level path Servlet Path is less written:
Error 3: The Servlet Path written does not match the URL
The requested path does not match the configuration on the server side:
Error 4: web.xml is written incorrectly
When there is a problem with web.xml, tomcat cannot be loaded into the webapp correctly~~
Tomcat finds that web.xml exists in this directory and the content is correct, then tomcat can load the webapp.
2、405 —— Method Not Allowed
Refers to the method in HTTP
Mistake 1: Change doGet to doPost:
For such user operation, the browser constructs a GET request, and the server writes doPost here and cannot handle the GET request~~
When does the browser send a GET request?
Directly in the address bar, enter the URL
Jump through a tag
Via img/link/script…
Through the form, the method is specified as GET
Through ajax, type is specified as GET
When does the browser send a POST request?
- Through the form form, the method is specified as POST
- Via ajax, type is specified as POST
Error 2: If you forget to comment out super.doGet(req, resp) in the code, 405 will also appear in this situation!!!
Entering HttpServlet
the source code, you can see that the doGet of the parent class here is to directly return a 405 response!!!
3、500
500 is also a very high-frequency error. Starting with 5, there is a problem with the server.
Generally, 500 means that an exception was thrown in the server code , and our code did not handle it. The exception was thrown to Tomcat~~
For example: if an exception occurs in the code, it can be caught by catch.
If catch is not caught (the type does not match, there is no catch at all), the exception will be passed up along the call stack.
If the code appears 500, just have fun. This situation is the best to solve!! Because it directly tells you where there is a problem!!!
The exception call stack (the exception call stack that appears in IDEA when writing code) tells us the exact location of the exception.
4. Blank page
If you do not set anything in the response object, a blank page will appear.
5. Unable to access this website
- If this is the case, it proves that the network is blocked (it is blocked at the TCP level)
- One of the great possibilities is that Tomcat did not start correctly (for example, the Tomcat port is occupied, the startup fails...)
Servlet operation principle
Servlet belongs to the superstructure, and the transport layer, network layer, and data link layer below belong to the economic foundation.
- When the browser sends a request to the server, Tomcat, as an HTTP server, can receive the request.
As an application layer protocol, the HTTP protocol requires an underlying protocol stack to support its work, as shown in the following figure:
Tomcat is actually an application. It is an ordinary process running in user mode (Tomcat is actually also a Java process).
The code written by the user (the response is calculated according to the request) interacts with Tomcat through Servlet~~
Tomcat further interacts with the browser. Network transmission still follows the same set of network principles: encapsulation and demultiplexing
For a more detailed interaction process, please refer to the figure below:
-
Receive request:
- The user enters a URL in the browser, and the browser constructs an HTTP request.
- This HTTP request will be encapsulated into a binary bit stream layer by layer through the network protocol stack, and finally converted into an optical signal/electrical signal through the physical layer hardware device for transmission.
- These optical/electrical signals carrying information pass through a series of network devices on the Internet and finally reach the target host (this process also requires the participation of the network layer and data link layer).
- When the server host receives these optical signals/electrical signals, they will be separated layer by layer through the network protocol stack, parsed layer by layer, and finally restored into HTTP requests. And handed over to the Tomcat process for processing (the process is determined based on the port number)
- Tomcat reads the request (a string) through Socket, parses the request according to the HTTP request format,
determines a webapp based on the Context Path in the request, and then determines a specific class through the Servlet Path. Then based on the current
request method (GET/POST/…), decide to call the doGet or doPost methods of this class. At this time, the first parameter HttpServletRequest of the doGet / doPost method in our code
contains the detailed information of this HTTP request.
-
Calculate the response based on the request:
- In our doGet / doPost method, our own code is executed. Our own code will
set some properties for the HttpServletResponse object based on some information in the request. For example, status code, header, body, etc.
- In our doGet / doPost method, our own code is executed. Our own code will
-
Return response:
- After our doGet / doPost is executed, Tomcat will automatically convert the HttpServletResponse object we just set into a string that conforms to the HTTP protocol, and send the response through Socket.
- At this time, the response data is encapsulated layer by layer on the server's host through the network protocol stack, and finally a binary bit stream is obtained, which is converted into an optical signal/electrical signal through the physical layer hardware device and transmitted out.
- These optical signals/electrical signals carrying information pass through a series of network devices on the Internet and finally reach the host where the browser is located (this process also requires the participation of the network layer and data link layer).
- The browser host receives these optical signals/electrical signals, and then separates them layer by layer through the network protocol stack, parses them layer by layer, and finally restores them to HTTP responses, which are then handed over to the browser for processing.
- The browser also reads the response (a string) through the Socket, parses the response according to the HTTP response format, and displays the data in the body on the browser interface in a certain format.
When data is exchanged between the browser and the server, does this process involve the TCP three-way handshake, confirmation response..., does it involve the sub-packetization of IP... Does it involve the MTU of Ethernet...
They all know it!!!
Tomcat pseudocode
- The following code describes the two core logics of Tomcat initialization/request processing in the form of "pseudocode"
- The so-called "pseudocode" is not code with strict grammar and complete functions. It is just a way to roughly express some logic.
1. Tomcat initialization process
- The main method is built into Tomcat's code. When we start Tomcat, execution starts from Tomcat's main method.
- Classes modified by the @WebServlet annotation will be obtained when Tomcat starts and managed centrally.
1). Let Tomcat first find all the Servlet classes to be loaded from the specified directory ~~
During the previous deployment, the Servlet code was compiled .class
, packaged war
, and copied to webapps. Tomcat will find the Servlet class corresponding to the .class from webapps and need to load it (of course, the loading here does not necessarily mean that it will be executed immediately as soon as Tomcat starts, it may also be "lazy loading".
But in the pseudo code here Just assume it loads immediately~~)
2). Based on the results of the class loading just now, create Servlet instances for these classes~~
// 这里要做的的是实例化出所有的 Servlet 对象出来;
for (Class<Servlet> cls : allServletClasses) {
// 这里是利用 java 中的反射特性做的
// 实际上还得涉及一个类的加载问题,因为我们的类字节码文件,是按照约定的
// 方式(全部在 WEB-INF/classes 文件夹下)存放的,所以 tomcat 内部是
// 实现了一个自定义的类加载器(ClassLoader)用来负责这部分工作。
Servlet ins = cls.newInstance(); // 反射
instanceList.add(ins);
}
3). After the instance is created, you can call init
the method of the current Servlet instance.
Servlet's own method, init does nothing by default.
When we inherit an HttpServlet, we can also rewrite init ourselves, which can help us do some initialization work at this stage.
// 调用每个 Servlet 对象的 init() 方法,这个方法在对象的生命中只会被调用这一次;
for (Servlet ins : instanceList) {
ins.init();
}
4). Create a TCP socket , listen to port 8080, and wait for a client to connect .
In order to respond to multiple HTTP requests at the same time, Tomcat adopts a multi-threaded approach. Therefore, the Servlet runs in a multi-threaded environment. Tomcat also uses the Socket API for network communication internally.
Every time there is a connection, accept
it will return, and then add a task doHttpRequest(socket) to the current thread pool
; this task will be responsible for processing the request.
The main loop here is essentially the same as the TCP echo server we talked about before.
Most of Tomcat's work is completed in this loop~~
// 利用我们之前学过的知识,启动一个 HTTP 服务器
// 并用线程池的方式分别处理每一个 Request
ServerSocket serverSocket = new ServerSocket(8080);
// 实际上 tomcat 不是用的固定线程池,这里只是为了说明情况
ExecuteService pool = Executors.newFixedThreadPool(100);
while (true) {
Socket socket = ServerSocket.accept();
// 每个请求都是用一个线程独立支持,这里体现了我们 Servlet 是运行在多线程环境下的
pool.execute(new Runnable() {
doHttpRequest(socket);
});
}
5). If the loop exits, Tomcat will also end, and the destroy method of each Servlet will be called in sequence.
This is the finishing work (things before Tomcat exits)
// 调用每个 Servlet 对象的 destroy() 方法,这个方法在对象的生命中只会被调用这一次;
for (Servlet ins : instanceList) {
ins.destroy();
}
Why can we still reach the following code without break?
That’s because what we are writing is a pseudo-code , which only contains the core logic and does not have too many implementation details~~
In fact, there will be some conditions in Tomcat to exit this main Loop
is similar to init. The destroy here does nothing by default . You can rewrite this destroy in user code ~~
Although there is 5) this link of calling destroy, this link is not necessarily reliable~~
If Tomcat exits through the "normal process", it can actively end the loop and call destroy here. If it exits through an "abnormal process", it will be too late
For example, Tomcat, port 8005 (management port, through which you can issue commands to this Tomcat), shutting down Tomcat in this way is a "normal process"
Abnormal process: end the process directly (this is the case in most cases)
class Tomcat {
// 用来存储所有的 Servlet 对象
private List<Servlet> instanceList = new ArrayList<>()
public void start() {
// 根据约定,读取 WEB-INF/web.xml 配置文件;
// 并解析被 @WebServlet 注解修饰的类
// 假定这个数组里就包含了我们解析到的所有被 @WebServlet 注解修饰的类.
Class<Servlet>[] allServletClasses = ...;
// 这里要做的的是实例化出所有的 Servlet 对象出来;
for (Class<Servlet> cls : allServletClasses) {
// 这里是利用 java 中的反射特性做的
// 实际上还得涉及一个类的加载问题,因为我们的类字节码文件,是按照约定的
// 方式(全部在 WEB-INF/classes 文件夹下)存放的,所以 tomcat 内部是
// 实现了一个自定义的类加载器(ClassLoader)用来负责这部分工作。
Servlet ins = cls.newInstance();
instanceList.add(ins);
}
// 调用每个 Servlet 对象的 init() 方法,这个方法在对象的生命中只会被调用这一次;
for (Servlet ins : instanceList) {
ins.init();
}
// 利用我们之前学过的知识,启动一个 HTTP 服务器
// 并用线程池的方式分别处理每一个 Request
ServerSocket serverSocket = new ServerSocket(8080);
// 实际上 tomcat 不是用的固定线程池,这里只是为了说明情况
ExecuteService pool = Executors.newFixedThreadPool(100);
while (true) {
Socket socket = ServerSocket.accept();
// 每个请求都是用一个线程独立支持,这里体现了我们 Servlet 是运行在多线程环境下的
pool.execute(new Runnable() {
doHttpRequest(socket);
});
}
// 调用每个 Servlet 对象的 destroy() 方法,这个方法在对象的生命中只会被调用这一次;
for (Servlet ins : instanceList) {
ins.destroy();
}
}
public static void main(String[] args) {
new Tomcat().start();
}
}
2. Tomcat processing request process
class Tomcat {
void doHttpRequest(Socket socket) {
// 参照我们之前学习的 HTTP 服务器类似的原理,进行 HTTP 协议的请求解析,和响应构建
HttpServletRequest req = HttpServletRequest.parse(socket); // 1)
HttpServletRequest resp = HttpServletRequest.build(socket);
// 判断 URL 对应的文件是否可以直接在我们的根路径上找到对应的文件,如果找到,就是静态内容
// 直接使用我们学习过的 IO 进行内容输出
if (file.exists()) {
// 2)
// 返回静态内容
return;
}
// 走到这里的逻辑都是动态内容了
// 根据我们在配置中说的,按照 URL -> servlet-name -> Servlet 对象的链条
// 最终找到要处理本次请求的 Servlet 对象
Servlet ins = findInstance(req.getURL()); // 3)
// 调用 Servlet 对象的 service 方法
// 这里就会最终调用到我们自己写的 HttpServlet 的子类里的方法了
try {
ins.service(req, resp); // 4)
} catch (Exception e) {
// 返回 500 页面,表示服务器内部错误
}
}
}
1). req : It reads the data in the socket and then parses it according to the request format of the HTTP protocol to construct an HttpServletRequest object.
resp : here is equivalent to new an empty object.
2). It is to determine whether the resource currently being requested is a static file .
If it is a static file, read the file content, construct the file content into the body of the resp object , and return the resp object.
For example, you can create a static file test.html in the webapp directory, start it, and visit: http://127.0.0.1:8080/hello102/test.html
3). According to the requested URL, get which class to use to process
the URL. There must be a two-level path in the URL:
-
First-level path: Context Path, determine a webapp
-
Second-level path: Servlet Path, determine a Servlet class . If no matching Servlet class is found, 404 will be returned.
4). Based on the Servlet object just found, call service
the method~~ Inside the service method, doGet / doPost will be further called
3. Implementation of the service method of Servlet
If an unhandled exception occurs during the execution of the Servlet's service method, 500 will be returned.
class Servlet {
public void service(HttpServletRequest req, HttpServletResponse resp) {
String method = req.getMethod();
if (method.equals("GET")) {
doGet(req, resp);
} else if (method.equals("POST")) {
doPost(req, resp);
} else if (method.equals("PUT")) {
doPut(req, resp);
} else if (method.equals("DELETE")) {
doDelete(req, resp);
}
......
}
}
During the discussion of the entire process above, the key methods of Servlet are involved, mainly three:
- init: Initialization phase. After the object is created, it will be executed. Users can override this method to execute some initialization logic.
- Service: Called during the request processing stage. Service must be called once for each request.
- destroy: exit the main loop, will be called before tomcat ends to release resources
Their calling timing is called the life cycle of the Servlet . The term life cycle is a very common term in computers: "When should something be done?"
The service method of the Servlet will internally decide to call one of the doXXX methods based on the current requested method.
When the doXXX method is called, the polymorphic mechanism will be triggered, thereby executing the doXXX method in the subclass we wrote ourselves.
Understand polymorphism here :
The HelloServlet class we wrote earlier inherits from the HttpServlet class. And HttpServlet inherits from Servlet. It is equivalent to HelloServlet being a subclass of Servlet.
Next, during the Tomcat startup phase, Tomcat has created an instance of HelloServlet according to the annotation description, and then placed this instance in the Servlet array.
Later, we obtained the HelloServlet instance from the array based on the requested URL, but we obtained the HelloServlet instance through a parent class reference such as Servlet ins.
Finally, when we call doGet through code like ins.doGet(), it is "the parent class reference points to the child class object". At this time, the polymorphic mechanism will be triggered, thus calling the doGet we implemented in HelloServlet before. method
Equivalent code:
Servlet ins = new HelloServlet(); ins.doGet(req, resp);