Servlet component

Table of contents

Why do we need Servlets?

1. Example of Servlet role in Web server

2 What is Servlet?

3 How to use Servlets?

3.1 Operation steps

3.2 Operation analysis (execution principle)

3.3 Servlet role summary

4 Servlet life cycle

4.1 Servlet life cycle overview

4.2 The main process of the Servlet life cycle

4.4 Servlet request process

5 Two interfaces of Servlet

5.1 ServletConfig interface

5.2 ServletContext interface

6 Servlet technology system

6.1 Servlet interface

6.2 Common implementation classes of Servlet interface

6.2.1 GenericServlet abstract class

6.2.2 HttpServlet abstract class

7 Two important interfaces for processing requests and responses

7.1 HttpServletRequest interface

7.1.1 Get request parameters★

7.1.2 Get url address parameters

7.1.3 Get request header information

7.1.4 Forwarding of requests

7.1.5 Saving data to the request field

7.2 HttpServletResponse interface

7.2.1 Use the PrintWriter object to output data to the browser

7.2.2 Setting response headers

7.2.3 Redirecting requests

8 Request Forwarding and Redirection

8.1 Forwarding of requests

8.2 Redirection of requests

8.3 Comparing request forwarding and redirection

9 Character encoding settings in requests and responses

9.2 Encoding and decoding

9.3 Solve the request garbled problem

9.3.1 GET request (Tomcat7 and below needs to be processed)

9.3.2 POST request

9.4 Solve the problem of garbled response

10 Web application path setting

10.1 The concept of url


Why do we need Servlets?

1. Example of Servlet role in Web server

  • Example 1: insert data

  • Example 2: query data

 Drive the server-side Java program through the web page. Display the data returned by the Java program on the web page.

2 What is Servlet?

If you compare a Web application to a restaurant, Servlet is the waiter in the restaurant -responsible for receiving customers, serving dishes, and checking out.

 

  • In a broad sense, the Servlet specification is a set of technical standards formulated by Sun, including a series of interfaces related to Web applications, and is a macro solution for the implementation of Web applications. The specific Servlet container is responsible for providing the standard implementation.

  • In a narrow sense, Servlet refers to the javax.servlet.Servlet interface and its sub-interfaces, and can also refer to the implementation class that implements the Servlet interface.

  • Servlet ( Server Applet ) is a component on the server side, and its original meaning is "a small program on the server side".

    • Servlet instance objects are created by the Servlet container;

    • Servlet methods are invoked by the container under certain circumstances;

    • The servlet container destroys the instance of the servlet object when the web application is unloaded.

3 How to use Servlets?

3.1 Operation steps

  • Ways to use the Servlet interface:

    ① Build a Web development environment

    ② Create a dynamic Web project

    ③ Create the implementation class of javax.servlet.Servlet interface: com.atguigu.servlet.MyFirstServlet

    ④ Write the following code in the service(ServletRequest, ServletResponse) method to output the response information:

@Override
	public void service(ServletRequest req, ServletResponse res)
			throws ServletException, IOException {
		//1.编写输出语句,证明当前方法被调用
		System.out.println("Servlet worked...");
		//2.通过PrintWriter对象向浏览器端发送响应信息
		PrintWriter writer = res.getWriter();
		writer.write("Servlet response");
		writer.close();
	}

⑤ Register MyFirstServlet     in the web.xml configuration file

<!-- 声明一个Servlet,配置的是Servlet的类信息 -->
<servlet>
	<!-- 这是Servlet的别名,一个名字对应一个Servlet。相当于变量名 -->
	<servlet-name>MyFirstServlet</servlet-name>
	<!-- Servlet的全类名,服务器会根据全类名找到这个Servlet -->
	<servlet-class>com.atguigu.servlet.MyFirstServlet</servlet-class>
</servlet>

<!-- 建立Servlet的请求映射信息 -->
<servlet-mapping>
	<!-- Servlet的别名,说明这个Servlet将会响应下面url-pattern的请求 -->
	<servlet-name>MyFirstServlet</servlet-name>
	<!-- Servlet响应的请求路径。如果访问这个路径,这个Servlet就会响应 -->
	<url-pattern>/MyFirstServlet</url-pattern>
</servlet-mapping>

illustrate:

  • <url-pattern>: Multiple url-patterns can be configured, which means that accessing these urls will trigger the Servlet to respond, run the browser, access the url path just configured, and the service method of the Servlet will be called.

  • The text content in <url-pattern> must start writing path with / or *. It is equivalent to mapping resources to the root directory of the project to form a virtual resource file.

  • <url-pattern> in <servlet-mapping> can declare more than one, and can be accessed through any one. But generally only one will be configured in development.

   ⑥ Create index.html in the WebContent directory

   ⑦ Add hyperlink <a href="MyFirstServlet">To Servlet</a> in index.html

   ⑧ Click on the hyperlink to test the Servlet

3.2 Operation analysis (execution principle)

  • index.html

  • web.xml

  • If the configuration file is modified, the server needs to be restarted to redeploy the web project.

3.3 Servlet role summary

  • Receive the request [parsing the data in the request message: request parameters]

  • Processing requests [DAO and database interaction]

  • Complete the response [Set response message]

4 Servlet life cycle

4.1 Servlet life cycle overview

  • The objects in the application not only have a hierarchical relationship in space, but also show different states and different behaviors in time because they are in different stages of the program running process-this is the life cycle of the object.

  • A simple description of the life cycle is the process of the object from being created to being destroyed in the container.

4.2 The main process of the Servlet life cycle

① Servlet object creation: constructor

  • By default, the Servlet container creates a corresponding Servlet object when it receives an HTTP request for the first time.

  • The reason why the container can do this is because we provided the full class name when registering the Servlet, and the container created the Servlet object using reflection technology.

② Servlet object initialization: init()

  • After the Servlet container creates the Servlet object, it calls the init(ServletConfig config) method.

  • Function: After the Servlet object is created, perform some initialization operations. For example, read some resource files, configuration files, or establish some kind of connection (such as: database connection)

  • The init() method is only executed once when the object is created, and it will not be executed when a request is received later

  • In the javax.servlet.Servlet interface, the public void init (ServletConfig config) method requires the container to pass in the instance object of ServletConfig, which is also the fundamental method for us to obtain the instance object of ServletConfig.

③ Processing request: service()

  • In the javax.servlet.Servlet interface, the service(ServletRequest req, ServletResponse res) method is defined to handle HTTP requests.

  • Executed after every request.

  • The role of Servlet mentioned in the previous section is mainly reflected in this method.

  • At the same time, the container is required to pass in the ServletRequest object and the ServletResponse object.

④ Servlet object destruction: destroy()

  • The Servlet object will be destroyed when the server is restarted, the server is stopped, or the web application is uninstalled, and the public void destroy() method will be called.

  • This method is used to perform operations such as releasing the cache, closing the connection, and saving memory data persistence before destruction.

4.4 Servlet request process

  • first request

    • call the constructor to create the object

    • Execute the init() method

    • Execute the service() method

  • later request

    • Execute the service() method

  • before object destruction

    • Execute the destroy() method

5 Two interfaces of Servlet

5.1 ServletConfig interface

  • The ServletConfig interface encapsulates the Servlet configuration information , which can be seen from the name of the interface.

  • Each Servlet has a unique corresponding ServletConfig object , which represents the configuration information of the current Servlet.

  • The object is created by the Servlet container and passed into the lifecycle method init(ServletConfig config). can be used directly.

  • The ServletContext object representing the current web application is also encapsulated into the ServletConfig object, making the ServletConfig object a bridge to obtain the ServletContext object.

  • The main functions of the ServletConfig object

    • Get the Servlet name: getServletName()

    • Get the global context ServletContext object: getServletContext()

    • Get Servlet initialization parameters: getInitParameter(String) / getInitParameterNames().

    • Use as follows:

5.2 ServletContext interface

  • When the web container starts, it will create a unique corresponding ServletContext object for each web application , which means the Servlet context and represents the current web application.

  • Since all Servlets in a Web application share the same ServletContext object , the ServletContext object is also called the application object (Web application object).

  • The object is created by the Servlet container when the project starts , and is obtained through the getServletContext() method of the ServletConfig object. Destroyed when the project is unloaded.

  • The main function of the ServletContext object

① Get the context path of the project (project name with /): getContextPath()  

@Override
public void init(ServletConfig config) throws ServletException {
	ServletContext application = config.getServletContext();
	System.out.println("全局上下文对象:"+application);
	String path = application.getContextPath();
	System.out.println("全局上下文路径:"+path);// /06_Web_Servlet
}

② Get the local real path mapped by the virtual path: getRealPath(String path)

  • Virtual path: the path used by the browser to access resources in the web application.

  • Local Path: The actual save path of the resource in the file system.

  • Function: Write the files uploaded by users to the hard disk of the server through streaming.

@Override
public void init(ServletConfig config) throws ServletException {
	//1.获取ServletContext对象
	ServletContext context = config.getServletContext();
	//2.获取index.html的本地路径
	//index.html的虚拟路径是“/index.html”,其中“/”表示当前Web应用的根目录,
	//即WebContent目录
	String realPath = context.getRealPath("/index.html");
	//realPath=D:\DevWorkSpace\MyWorkSpace\.metadata\.plugins\
	//org.eclipse.wst.server.core\tmp0\wtpwebapps\MyServlet\index.html
	System.out.println("realPath="+realPath);
}

③ Obtain the global initialization parameters of the WEB application (basically not used)

  • The way to set the initialization parameters of the web application is to add the following code under the root tag of web.xml

<web-app>
	<!-- Web应用初始化参数 -->
	<context-param>
		<param-name>ParamName</param-name>
		<param-value>ParamValue</param-value>
	</context-param>
</web-app>

 Get the initialization parameters of the web application

@Override
public void init(ServletConfig config) throws ServletException {
	//1.获取ServletContext对象
	ServletContext application = config.getServletContext();
	//2.获取Web应用初始化参数
	String paramValue = application.getInitParameter("ParamName");
	System.out.println("全局初始化参数paramValue="+paramValue);
}

④ Sharing data as domain objects

  • Share data across different web resources across the project as the largest domain object.

in,

  • setAttribute(key,value): It can be taken out and used at any position in the future

  • getAttribute(key): Take out the set value

6 Servlet technology system

6.1 Servlet interface

 

6.2 Common implementation classes of Servlet interface

 

6.2.1 GenericServlet abstract class

  • GenericServlet encapsulates and perfects the Servlet function, and rewrites the init (ServletConfig config) method to obtain the ServletConfig object. At this time, if the subclass of GenericServlet (usually a custom Servlet) rewrites the init(ServletConfig config) method, the ServletConfig object may not be obtained, so the subclass should not rewrite the init() method with parameters.

  • If you want to perform an initialization operation, you can override the parameterless init() method provided by GenericServlet, so that it will not affect the acquisition of the ServletConfig object.

  • Keep service (ServletRequest req, ServletResponse res) as an abstract method, so that users only care about business implementation.

6.2.2 HttpServlet abstract class

  • A Servlet dedicated to handling Http requests.

  • To further encapsulate and expand GenericServlet, in the service(ServletRequest req, ServletResponse res) method, convert ServletRequest and ServletResponse into HttpServletRequest and HttpServletResponse, and call special methods for processing according to different HTTP request types.

  • In the future, in actual use, just inherit the HttpServlet abstract class and create your own Servlet implementation class. Rewrite the doGet(HttpServletRequest req, HttpServletResponse resp) and doPost(HttpServletRequest req, HttpServletResponse resp) methods to implement request processing, no longer need to rewrite the service(ServletRequest req, ServletResponse res) method.

  • And because the processing methods of get and post in our business are the same, we only need to write one method, and use another method to call the doXXX method we wrote. The web.xml configuration is the same as before.

//处理浏览器的get请求
doGet(HttpServletRequest request, HttpServletResponse response){
	//业务代码
}
//处理浏览器的post请求
doPost(HttpServletRequest request, HttpServletResponse response){
    doGet(request, response);
}

7 Two important interfaces for processing requests and responses

7.1 HttpServletRequest interface

  • This interface is a sub-interface of the ServletRequest interface, which encapsulates information about HTTP requests.

  • When the browser requests the server, it will encapsulate the request message and send it to the server. After receiving the request, the server will parse the request message to generate a request object.

  • The Servlet container creates its implementation class object and passes it into the service(HttpServletRequest req, HttpServletResponse res) method.

  • The HttpServletRequest object we refer to below refers to the HttpServletRequest implementation class object provided by the container.

The main functions of the HttpServletRequest object are:

7.1.1 Get request parameters★

  • What are request parameters?

    • The request parameter is the data submitted by the browser to the server.

  • How does the browser send data to the server?

    ① Attached to the url (consistent with the get request, the form of splicing is enough to bind the request data), such as:

    http://localhost:8080/MyServlet/MyHttpServlet?userId=20

    ② Submit via the form

<form action="MyHttpServlet" method="post">
	你喜欢的足球队<br /><br />
	巴西<input type="checkbox" name="soccerTeam" value="Brazil" />
	德国<input type="checkbox" name="soccerTeam" value="German" />
	荷兰<input type="checkbox" name="soccerTeam" value="Holland" />
	<input type="submit" value="提交" />
</form>
  • Use the HttpServletRequest object to get request parameters
//一个name对应一个值
String userId = request.getParameter("userId");
//一个name对应一组值
String[] soccerTeams = request.getParameterValues("soccerTeam");
for(int i = 0; i < soccerTeams.length; i++){
	System.out.println("team "+i+"="+soccerTeams[i]);
}

7.1.2 Get url address parameters

String path = request.getContextPath();//重要
System.out.println("上下文路径:"+path);
System.out.println("端口号:"+request.getServerPort());
System.out.println("主机名:"+request.getServerName());
System.out.println("协议:"+request.getScheme());

7.1.3 Get request header information

String header = request.getHeader("User-Agent");
System.out.println("user-agent:"+header);
String referer = request.getHeader("Referer");
System.out.println("上个页面的地址:"+referer);//登录失败,返回登录页面让用户继续登录

7.1.4 Forwarding of requests

//获取请求转发对象
RequestDispatcher dispatcher = request.getRequestDispatcher("success.html");
dispatcher.forward(request, response);//发起转发

7.1.5 Saving data to the request field

//将数据保存到request对象的属性域中
request.setAttribute("attrName", "attrValueInRequest");
//两个Servlet要想共享request对象中的数据,必须是转发的关系
request.getRequestDispatcher("/ReceiveServlet").forward(request, response);
//从request属性域中获取数据
Object attribute = request.getAttribute("attrName");
System.out.println("attrValue="+attribute);

7.2 HttpServletResponse interface

  • This interface is a sub-interface of the ServletResponse interface, which encapsulates the relevant information of the server for the HTTP response. (Temporarily only server configuration information, no specific content related to the response body)

  • The Servlet container creates its implementation class object and passes it into the service(HttpServletRequest req, HttpServletResponse res) method.

  • The HttpServletResponse object we refer to later refers to the HttpServletResponse implementation class object provided by the container.

The main functions of the HttpServletResponse object are:

7.2.1 Use the PrintWriter object to output data to the browser

//通过PrintWriter对象向浏览器端发送响应信息
PrintWriter writer = res.getWriter();
writer.write("Servlet response");
writer.close();
  • The data written out can be pages, page fragments, strings, etc.

  • When the written data contains Chinese, the response data received by the browser may have garbled characters. In order to avoid garbled characters, you can use the Response object to set the response header before outputting data to the browser.

7.2.2 Setting response headers

  • The response header is the configuration for the browser to parse the page. For example: tell the browser which encoding and file format to use to parse the response body content

response.setHeader("Content-Type", "text/html;charset=UTF-8");
  • After setting, you will see the information in the set response header in the response message of the browser.

7.2.3 Redirecting requests

  • For request redirection, see Chapter 8 - Request Forwarding and Redirection.

  • Example: The user submits login request data from the login.html page to LoginServlet for processing. If the account password is correct, the user needs to jump to the success page. It is too complicated to write the success page to the response body through the servlet. The address of the success page is given to the browser through redirection and the response status code is set to 302. The browser will Jump automatically.

//注意路径问题,加上/会失败,会以主机地址为起始,重定向一般需要加上项目名
response.sendRedirect(“success.html”);

8 Request Forwarding and Redirection

Request forwarding and redirection are the main means of web application page jumps, and are widely used in web applications.

8.1 Forwarding of requests

  • The first Servlet received the request from the browser, performed some processing, and then did not respond to the request immediately, but "handed over the request to the next Servlet" to continue processing. After the next Servlet processed, the browser was processed response. "Handing over" the request to other components inside the server for further processing is the forwarding of the request. For the browser, a total of only one request is sent, and the browser cannot feel the "forwarding" performed inside the server, and the address in the browser address bar will not become the virtual path of the "next Servlet".

  • HttpServletRequest represents an HTTP request, and the object is created by the Servlet container. In the case of forwarding, two Servlets can share the data stored in the same Request object.

  • When the data obtained in the background needs to be transmitted to the JSP for display, the data can be stored in the Request object first, and then forwarded to the JSP to obtain from the attribute field. At this time, because it is "forwarding", they both share the data in the Request object.

  • In the case of forwarding, resources under WEB-INF can be accessed.

  • Forwarding starts with "/" to indicate the root path of the project, and redirection starts with "/" to indicate the host address.

  • Function:

    • Get request parameters

    • Obtain information about the request path, that is, the URL address

    • Store data in the request field

    • forward request

  • code example

protected void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
	//1.使用RequestDispatcher对象封装目标资源的虚拟路径
	RequestDispatcher dispatcher = request.getRequestDispatcher("/index.html");
	//2.调用RequestDispatcher对象的forward()方法“前往”目标资源
	//[注意:传入的参数必须是传递给当前Servlet的service方法的
	//那两个ServletRequest和ServletResponse对象]
	dispatcher.forward(request, response);
}

8.2 Redirection of requests

  • The first Servlet receives the request from the browser, performs certain processing, and then sends a special response message to the browser. This special response message will notify the browser to access another resource. This action is automatically performed by the server and the browser. Completed. During the whole process, the browser will send two requests , and the address change can be seen in the address bar of the browser , changing to the address of the next resource.

  • In the case of redirection, the request field data cannot be shared between the original Servlet and the target resource.

  • HttpServletResponse represents the HTTP response, and the object is created by the Servlet container.

  • Function:

    • output data to the browser

    • redirect request

  • The header of the redirected response message

HTTP/1.1 302 Found
Location: success.html

  • application:

    • The user submits the login request data from the login.html page to the LoginServlet for processing.

      If the account password is correct, the user needs to jump to the success page. It is too complicated to write the success page to the response body through the servlet. The address of the success page is given to the browser through redirection and the response status code is set to 302. The browser will Jump automatically

  • Code example:

protected void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
	//1.调用HttpServletResponse对象的sendRedirect()方法
	//2.传入的参数是目标资源的虚拟路径
	response.sendRedirect("index.html");
}

8.3 Comparing request forwarding and redirection

Forward redirect
browser awareness Completed inside the server, the browser does not perceive The server notifies the browser to access the new address with a 302 status code, and the browser is aware of it
browser address bar do not change Change
The number of requests sent during the entire process once twice
Can the request object data be shared able no
Resources under WEB-INF able to access Can not access
target resource Must be a resource in the current web application Not limited to current web applications

Note 1: By default, the browser cannot access the resources under the server web-inf, but the server can.

Note 2: The default absolute path of the browser: http://localhost:8080/

Absolute path in the code of the server project: http://localhost:8080/ projectname/  

9 Character encoding settings in requests and responses

  • When our web program receives and processes requests, if we do not pay attention to the encoding format and decoding format, it is easy to cause Chinese garbled characters. What is the cause of this problem? How to solve? We will discuss this issue in this subsection.

  • Speaking of this issue, let's talk about character sets first.

    • What is a character set is a collection of various characters, including Chinese characters, English, punctuation marks and so on. Different countries have different characters and symbols. A collection of these text symbols is called a character set.

    • Existing character sets ASCII, GB2312, BIG5, GB18030, Unicode, UTF-8, ISO-8859-1, etc.

  • These character sets are a collection of many characters. However, if the characters are to be stored in the computer in binary form, we need to encode them and store the encoded binary. When we take it out, we have to decode it, and decode the binary into our previous characters. At this time, we need to formulate a set of encoding and decoding standards. Otherwise, it will lead to confusion, that is, our garbled characters.

9.2 Encoding and decoding

  • Encoding: converting characters to binary numbers

Chinese character Encoding coding binary
'middle' GB2312 D6D0 1101 0110-1101 0000
'middle' UTF-16 4E2D 0100 1110-0010 1101
'middle' UTF-8 E4B8AD 1110 0100- 1011 1000-1010 1101
  • Decoding: converting binary numbers to characters

1110 0100-1011 1000-1010 1101 → E4B8AD → 'Medium'

  • Garbled characters: A piece of text, encoded with character set A and decoded with character set B, will produce garbled characters. So the fundamental way to solve the problem of garbled characters is to unify the encoding and decoding character sets.

9.3 Solve the request garbled problem

The solution to garbled characters: Unicode.

9.3.1 GET request (Tomcat7 and below needs to be processed)

  • GET request parameters are after the address. We need to modify the configuration file of tomcat. You need to modify the Connector tag in the server.xml file and add the URIEncoding="utf-8" attribute.

  • Once configured, it can solve the garbled problem of all GET requests in the current workspace.

9.3.2 POST request

  • The post request submits the request body in Chinese, and there is a problem with the server parsing.

  • Solution: Before obtaining the parameter value, set the decoding format of the request to be consistent with the page.

    request.setCharacterEncoding("utf-8");
  • The solution to the problem of garbled characters in POST requests is only applicable to the class where the current operation is located. It cannot be resolved uniformly like a GET request. Because the request body may upload files. Not necessarily all Chinese characters.

9.4 Solve the problem of garbled response

  • When sending a response to the browser, tell the browser which character set I use, and the browser will decode it in this way. How to tell the browser the character encoding scheme of the response content. very simple.

  • Solution one:

    response.setHeader("Content-Type", "text/html;charset=utf-8");
  • Solution two

    response.setContentType("text/html;charset=utf-8");

    Explanation: Some people may think of using response.setCharacterEncoding("utf-8") to set the encoding of the response object to write UTF-8 strings into the response message as UTF-8. It is not enough to do this alone, and you must manually set the character set used by the browser for parsing in the browser.

10 Web application path setting

10.1 The concept of url

url is uniform Resource Locaterthe abbreviation of url, translated into Chinese 统一资源定位符, it is the only access address of a certain Internet resource, and the client can access specific Internet resources through url

The complete url structure is as follows:

relative path and absolute path

Relative path: If the virtual path does not start with "/", it is a relative path . The browser will analyze the relative path based on the virtual path where the current resource is located to generate the final access path. At this time, if you enter other directories through forwarding, and then use relative paths to access resources, an error will occur. Therefore, in order to prevent path errors, we often convert relative paths into absolute paths for requests.

Absolute path: The virtual path starts with "/", which is an absolute path. ① On the server side : "/" at the beginning of the virtual path indicates the root directory of the current web application. As long as it is an absolute path parsed by the server, it starts with the web root directory. The path parsed by the server includes: <1> web.xml configuration path, <2> request forwarding path. ② On the browser side : the "/" at the beginning of the virtual path indicates the current host address. For example: the link address "/Path/dir/b.html" after being parsed by the browser is: equivalent to http://localhost:8989/Path/dir/b.html The paths parsed by the browser include: <1> Heavy Orientation operation: response.sendRedirect("/xxx") <2> All HTML tags: <a href="/xxx">, <form action="/xxx">, link, img, script, etc. These final access paths Both are http://localhost:8989/xxx

So we can see that if it is the path parsed by the browser, we must add the project name to correctly point to the resource. http://localhost:8989 /Path /xxx

<1> Redirect operation:

response.sendRedirect(request.getContextPath()+"/xxx");

<2>All HTML tags: <a href="/project name/xxx">; <form action="/project name/xxx">

  • On the browser side, in addition to using absolute paths, we can also use base tags + relative paths to determine that resource access is valid.

  • The base tag affects all relative paths in the current page and does not affect absolute paths. It is equivalent to setting a base address for the relative path.

  • It is customary to declare in the <head> tag of html:

<!-- 给页面中的相对路径设置基准地址 -->
<base href="http://localhost:8080/Test_Path/"/>

 Then the path in html can be accessed using a relative path. for example:

<h4> base+相对路径</h4>
<!-- <base href="http://localhost:8080/Test_Path/"/> -->
<a href="1.html">1.html</a><br/>
<a href="a/3.html">a/3.html</a><br/>
<!-- servlet映射到了项目根目录下,可以直接访问 -->
<a href="PathServlet">PathServlet</a><br/>

Guess you like

Origin blog.csdn.net/rbx508780/article/details/127440941