Servlets are Java classes that comply with the Java Servlet API, and these Java classes can respond to requests. Although Servlets can respond to any type of request, they are most widely used in response to web requests. Servlets must be deployed in Java servlet containers before they can be used. Although many developers use it Java Server Pages(JSP) and Java Server Faces(JSF) Wait for the Servlet framework, but all these technologies need to compile pages into Java Servlets behind the scenes through the Servlet container. That is to say, it is useful for any Java web developer to understand the basic knowledge of Java Servlet technology.
In this tutorial, we will take a comprehensive look at Java Servlet technology through the following topics.
Catalog
- Write your first Servlet
- Servlet lifecycle approach
- Developing Servlet with @WebServlet annotation
- Packing and deploying servlets to Tomcat servers
- Writing dynamic Servlet response content
- Processing Servlet requests and responses
- Listening for Servlet container events
- Pass Servlet initialization parameters
- Adding Servlet filters for specific URL requests
- Download binary files using Servlet
- Forwarding requests to another Servlet using RequestDispatcher.forward()
- Use HttpServletResponse.sendRedirect() to redirect requests to another Servlet
- Read and write Cookie s using Servlets
Let's learn Servlet step by step.
Write your first Servlet
Our first Servlet was a simple Servlet with only a small amount of code, designed to make you focus only on its behavior.
package com.howtodoinjava.servlets; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class MyFirstServlet extends HttpServlet { private static final long serialVersionUID = -1915463532411657451L; @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); try { // Write some content out.println("<html>"); out.println("<head>"); out.println("<title>MyFirstServlet</title>"); out.println("</head>"); out.println("<body>"); out.println("<h2>Servlet MyFirstServlet at " + request.getContextPath() + "</h2>"); out.println("</body>"); out.println("</html>"); } finally { out.close(); } } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //Do some other work } @Override public String getServletInfo() { return "MyFirstServlet"; } }
To register the Servlet above in the web container, you need to create a web.xml entry file for your application.
<?xml version="1.0"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <welcome-file-list> <welcome-file>/MyFirstServlet</welcome-file> </welcome-file-list> <servlet> <servlet-name>MyFirstServlet</servlet-name> <servlet-class>com.howtodoinjava.servlets.MyFirstServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>MyFirstServlet</servlet-name> <url-pattern>/MyFirstServlet</url-pattern> </servlet-mapping> </web-app>
The Servlet above does something important that you might want to know.
- The MyFirstServlet class inherits the HttpServlet. This inheritance is necessary because all servlets must either inherit the common Servlet of javax.servlet.GenericServlet or inherit the HTTP Servlet of javax.servlet.http.HttpServlet.
- Re-doGet() and doPost() methods. Both methods have been defined in the HttpServlet class. When a GET or POST request arrives, it is mapped to the corresponding method. For example, if you send an HTTP GET request to the servlet, the doGet() method will be called.
- There are also some other useful ways. You can rewrite them to control applications at runtime. For example, getServletInfo().
- HttpServletRequest and HttpServletResponse are default parameters for all doXXX() methods. We'll look at these objects in detail in later chapters.
All of the above about simple servlets is what you need to know.
Servlet lifecycle approach
When your application loads and uses a servlet, a series of events occur between initialization and destruction of the servlet. These events are called Servlet life cycle events (or methods). Let's get to know them better.
The three core methods of the Servlet life cycle are init (), service () and destroy(). Each servlet implements these methods and invokes them at a specific run time.
1) In the initial stage of the Servlet life cycle, the web container initializes the Servlet instance by calling init() method, and can pass an object to it that implements the javax.servlet.ServletConfig interface. This configuration object enables the Servlet to read the initial name-value parameter defined in the web.xml file of the web application. This method is called only once in the lifetime of the Servlet instance.
The init method definition is similar to this:
public void init() throws ServletException { //custom initialization code }
2) After initialization, the Servlet instance can process client requests. The web container calls the service() method of the servlet to process each request. The service() method defines the types of requests that can be processed and invokes appropriate methods to process these requests. Developers writing servlets must provide implementations for these methods. If a request is made that is not implemented by a servlet, the method of the parent class is called and an error message is usually returned to the requester.
Usually, we don't need to override this method.
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getMethod(); if (method.equals(METHOD_GET)) { long lastModified = getLastModified(req); if (lastModified == -1) { // servlet doesn't support if-modified-since, no reason // to go through further expensive logic doGet(req, resp); } else { long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE); if (ifModifiedSince < (lastModified / 1000 * 1000)) { // If the servlet mod time is later, call doGet() // Round down to the nearest second for a proper compare // A ifModifiedSince of -1 will always be less maybeSetLastModified(resp, lastModified); doGet(req, resp); } else { resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED); } } } else if (method.equals(METHOD_HEAD)) { long lastModified = getLastModified(req); maybeSetLastModified(resp, lastModified); doHead(req, resp); } else if (method.equals(METHOD_POST)) { doPost(req, resp); } else if (method.equals(METHOD_PUT)) { doPut(req, resp); } else if (method.equals(METHOD_DELETE)) { doDelete(req, resp); } else if (method.equals(METHOD_OPTIONS)) { doOptions(req,resp); } else if (method.equals(METHOD_TRACE)) { doTrace(req,resp); } else { // // Note that this means NO servlet supports whatever // method was requested, anywhere on this server. // String errMsg = lStrings.getString("http.method_not_implemented"); Object[] errArgs = new Object[1]; errArgs[0] = method; errMsg = MessageFormat.format(errMsg, errArgs); resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg); } }
3) Finally, the web container calls destroy() method to terminate the servlet. If you want to shut down or destroy some file system or network resources within the lifetime of Servlet, you can call this method. The destroy() method, like the init() method, can only be called once in the Servlet lifecycle.
public void destroy() { // }
In most cases, you don't usually need to rewrite these methods in your Servlet.
Expanding reading: How does a web server work?
Developing Servlets with the @WebServlet annotation
It doesn't matter if you prefer annotations instead of xml configuration. The Servlets API also provides some annotation interfaces for you. You can use it as an example below. @WebServlet Annotate and do not need to register any information for the Servlet in web.xml. The container will automatically register your servlet to the running environment and process it as usual.
package com.howtodoinjava.servlets; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet(name = "MyFirstServlet", urlPatterns = {"/MyFirstServlet"}) public class MyFirstServlet extends HttpServlet { private static final long serialVersionUID = -1915463532411657451L; @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //Do some work } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //Do some other work } }
Packing and deploying servlets to Tomcat servers
If you are using IDE (e.g. eclipse), packaging and deploying your application requires only a simple step. Right-click Project > Run As > Run As Server. If you haven't configured the server, configure the server first, and then you're ready to go.
If you're not using IDE, you need to do some extra work. For example, use command prompts to compile applications, use ANT to generate war files, and so on. But I believe that today's developers are using IDE to develop. So I don't waste time on this.
When you deploy our first Servlet to tomcat and type "http://localhost:8080/servletexamples/MyFirstServlet" in the browser, you will get the following response.
Writing dynamic Servlet response content
One of the reasons Java Servlets are so useful is that they can dynamically display web content. This content can be obtained from the server itself, another website, or many other resources accessible by the network. Servlets are not static web pages, they are dynamic. It can be said that this is their greatest advantage.
Let's take a Servlet example, which shows the current date and time to the user and displays the user name and some custom information. Let's write code for this function.
package com.howtodoinjava.servlets; import java.io.IOException; import java.io.PrintWriter; import java.util.Date; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet(name = "CalendarServlet", urlPatterns = {"/CalendarServlet"}) public class CalendarServlet extends HttpServlet { private static final long serialVersionUID = -1915463532411657451L; @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Map<String,String> data = getData(); response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); try { // Write some content out.println("<html>"); out.println("<head>"); out.println("<title>CalendarServlet</title>"); out.println("</head>"); out.println("<body>"); out.println("<h2>Hello " + data.get("username") + ", " + data.get("message") + "</h2>"); out.println("<h2>The time right now is : " + new Date() + "</h2>"); out.println("</body>"); out.println("</html>"); } finally { out.close(); } } //This method will access some external system as database to get user name, and his personalized message private Map<String, String> getData() { Map<String, String> data = new HashMap<String, String>(); data.put("username", "Guest"); data.put("message", "Welcome to my world !!"); return data; } }
When you run the Servlet above in tomcat and type "http://localhost:8080/servletexamples/Calendar Servlet" in the browser, you will get the following response.
Processing Servlet requests and responses
Servlet can easily create a web application based on the request and response lifecycle. They provide HTTP responses and can use the same piece of code to process business logic. The ability to process business logic makes Servlets more powerful than standard HTML code.
In real-world applications, an HTML Web form contains parameters to be sent to the Servlet. Servlet will process these parameters in some way and return a response that the client can recognize. In the case of HttpServlet, the client is a web browser and the response is a web page. The action attribute of < form > specifies which servlet to use to process the parameter values in the form.
To get the request parameters, you need to call the getParameter() method of the HttpServletRequest object and pass the id of the input parameters you want to get to the method.
String value1 = req.getParameter("param1"); String value1 = req.getParameter("param2");
Once parameter values are obtained, they are processed as needed. The response to the client is the same as we discussed in the previous section. We use the HttpServletResponse object to send a response to the client.
The basic use of request and response processing can be as follows:
@Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); String username = request.getParameter("username"); String password = request.getParameter("password"); boolean success = validateUser(username, password); try { // Write some content out.println("<html>"); out.println("<head>"); out.println("<title>LoginServlet</title>"); out.println("</head>"); out.println("<body>"); if(success) { out.println("<h2>Welcome Friend</h2>"); }else{ out.println("<h2>Validate your self again.</h2>"); } out.println("</body>"); out.println("</html>"); } finally { out.close(); } }
In order to send content to the client, you need to use the PrintWriter object retrieved from the HttpServletResponse. Anything written to this object is written into the output stream and sent back to the client.
Listening for Servlet container events
Sometimes it's useful to know when certain events occur in the application server container. This concept applies to many situations, but it is usually used to initialize applications when they are open or to clean applications when they are closed. You can register a listener in the application to show when the application is turned on or off. Therefore, by listening for these events, the Servlet can perform the corresponding actions when some events occur.
To create a listener that performs actions based on container events, you must create a class that implements the ServletContextListener interface. The methods that this class must implement are contextInitialized() and contextDestroyed(). Both methods require ServletContextEvent as a parameter and are automatically invoked each time the Servlet container is initialized or closed.
To register listeners in containers, you can use one of the following methods:
1) Use the @WebListener annotation.
2) Register the listener in the web.xml application deployment file.
3) Use the addListener() method defined in ServletContext
Note that ServletContextListener is not the only listener in the Servlet API. Here are some other monitors, such as:
javax.servlet.ServletRequestListener javax.servlet.ServletRequestAttrbiteListener javax.servlet.ServletContextListener javax.servlet.ServletContextAttributeListener javax.servlet.HttpSessionListener javax.servlet.HttpSessionAttributeListener
Select them to implement your listener class based on the events you want to monitor. For example, whenever a user session is created or destroyed, the HttpSessionListener notifies.
Pass Servlet initialization parameters
Most applications today need to set configuration parameters that can be passed on when the application/controller is started. Servlet s can also accept initialization parameters and use them to build configuration parameters before processing the first request.
Obviously, you can also hard-code configuration values in the Servlet. But in doing so, you need to recompile the entire application when the Servlet changes. Nobody likes it.
<web-app> <servlet> <servlet-name>SimpleServlet</servlet-name> <servlet-class>com.howtodoinjava.servlets.SimpleServlet</servlet-class> <!-- Servlet init param --> <init-param> <param-name>name</param-name> <param-value>value</param-value> </init-param> </servlet> </web-app>
Once set, you can call getServletConfig.getInitializationParameter() in the code and pass the parameter name to the method to use the parameter. Like the code shown below:
String value = getServletConfig().getInitParameter("name");
Adding Servlet filters for specific URL requests
Web filters are useful for preprocessing requests and calling corresponding functions when a given URL is accessed. A filter containing the same URL pattern is called before a Servlet call, as opposed to calling a Servlet directly for a given URL request. This is useful in many cases. Perhaps the biggest use is to execute logs, validation, or other back-end services that do not need to interact with users.
The filter must implement the javax.servlet.Filter interface. This interface includes init(), descriptor(), and doFilter(). init() and destroy() methods are called by the container. The doFilter() method is used to implement logical tasks in the filter class. If you want to make up a chain filter or have multiple filters that match a given URL pattern, they will be invoked according to the configuration order in web.xml.
To configure filters in web.xml, you need to use <filter> and <filter-mapping> XML elements and related sub-element tags.
<filter> <filter-name>LoggingFilter</filter-name> <filter-class>LoggingFilter</filter-class> </filter> <filter-mapping> <filter-name>LogingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> If you want to use annotations to configure filters for specific servlet s, you can use the @WebFilter annotation.
Download binary files using Servlet
Almost all web applications must have the function of downloading files. To download a file, a Servlet must provide a response type that matches the download file type. Similarly, it must be pointed out in the response header that the response contains attachments. Like the code below.
String mimeType = context.getMimeType( fileToDownload ); response.setContentType( mimeType != null ? mimeType : "text/plain" ); response.setHeader( "Content-Disposition", "attachment; filename="" + fileToDownload + """ );
By calling the ServletContext.getResourceAsStream() method and passing the file path to the method, you can get a reference to the file you want to download (the file is saved on the file system). This method returns an InputStream object, which we can use to read the contents of the file. When reading a file, we create a byte buffer to retrieve data blocks from the file. The final task is to read the contents of the files and copy them to the output stream. We use the while loop to read the file, which does not jump out of the loop until all the contents of the file are read. We use loops to read in data blocks and write them into the output stream. After all the data is written into the output stream, the flush method of the ServletOutputStream object is invoked, emptying the content and releasing the resources.
Look at this simple code:
private void downloadFile(HttpServletRequest request, HttpServletResponse response, String fileToDownload) throws IOException { final int BYTES = 1024; int length = 0; ServletOutputStream outStream = response.getOutputStream(); ServletContext context = getServletConfig().getServletContext(); String mimeType = context.getMimeType( fileToDownload ); response.setContentType( mimeType != null ? mimeType : "text/plain" ); response.setHeader( "Content-Disposition", "attachment; filename="" + fileToDownload + """ ); InputStream in = context.getResourceAsStream("/" + fileToDownload); byte[] bbuf = new byte[BYTES]; while ((in != null) && ((length = in.read(bbuf)) != -1)) { outStream.write(bbuf, 0, length); } outStream.flush(); outStream.close(); }
Forwarding requests to another Servlet using RequestDispatcher.forward()
Sometimes, your application needs to transfer a request to be processed by a Servlet to another Servlet to process and complete the task. Furthermore, the client's URL cannot be redirected when transferring the request. That is, the URL on the browser's address bar will not change.
In ServletContext, a method to implement the above requirements has been built in. So, when you get a reference to the ServletContext, you can simply call the getRequestDispatcher() method to get the RequestDispatcher object used to forward requests. When calling the getRequestDispatcher() method, you need to pass a string containing the servlet name, which is the servlet you use to process the transfer request. After obtaining the RequestDispatcher object, the forwarding method is invoked by passing the HttpServletRequest and HttpServletResponse objects to it. The forwarding method is responsible for forwarding requests.
RequestDispatcher rd = servletContext.getRequestDispatcher("/NextServlet"); rd.forward(request, response);
Use HttpServletResponse.sendRedirect() to redirect requests to another Servlet
Sometimes, though, you don't want to notify users when a Servlet sends a redirect, as we saw in the previous paragraph. But in some cases, we do want to notify users. When a specific URL in the application is accessed, you want to redirect the browser's URL to another one.
To do this, you need to call the sendRedirect() method of the HttpServletResponse object.
httpServletResponse.sendRedirect("/anotherURL"); This simple redirection, in contrast to servlet chaining, does not require the HTTP Request object to pass the target address.
Read and write Cookie using Servlet
Many applications want to save the user's current browsing history in the client machine. The goal is that when the user uses the application again, he can start browsing from where he left last time. To achieve this requirement, cookies are usually used. You can think of it as the basic data of key-value pairs stored in the client machine. When an application is opened with a browser, the application can read and write the data.
To create a cookie, you need to instantiate a new javax.servlet.http.Cookie object and assign it names and values. After instantiating cookies, you can set properties to configure cookies. In this example, we use setMaxAge() and setHttpOnly() methods to set the cookie life cycle and prevent client scripts.
Starting with the Servlet 3.0 API, cookies can be marked HTTP only. This enables cookies to guard against client script attacks and make cookies safer. Cookie cookie = new Cookie("sessionId","123456789"); cookie.setHttpOnly(true); cookie.setMaxAge(-30); response.addCookie(cookie);
Here response is the HttpServletResponse instance passed to the doXXX() method.
To read cookie information on the server side, use the following code:
Cookie[] cookies = request.getCookies(); for(Cookie cookie : cookies) { //cookie.getName(); //cookie.getValue() }
This is all about Servlet technology in this tutorial. Comments and feedback are welcome.