Servlet#
约 1786 个字 188 行代码 8 张图片 预计阅读时间 42 分钟
动态网页#
动态网页是说页面中的数据是变化的,根据数据库的变化而变化。
Servlet概述#
Servlet规范
是指Java语言实现的一个接口
。该接口定义了生命周期方法。
Servlet
是指任何实现了这个Servlet接口
的类。
Servlet没有main方法,不能独立运行,它必须被部署到Servlet容器
中。Servlet容器也叫做Servlet引擎,是Web服务器或应用程序服务器的一部分,用于在发送的请求和响应之上提供网络服务。Tomcat
就是一个免费的开放源代码的Servlet容器
。
Tomcat服务器接受客户请求并做出响应的过程如下:
客户端
(通常都是浏览器)访问Web服务器,发送HTTP请求。Web服务器
(Tomcat)接收到客户端的 HTTP 请求。Servlet 容器
(Tomcat)根据请求的 URL 和配置信息来确定是否需要加载相应的Servlet类
(我们应用编写的Servlet类),并生成 Servlet 实例。如果需要加载,容器会创建 Servlet 实例,并调用其init()
方法进行初始化。- Servlet实例使用
请求对象
得到客户端的请求信息,然后进行相应的处理。 - Servlet实例将处理结果通过
响应对象
发送回客户端,容器
负责确保响应正确送出。
版本问题#
Tomcat 10(对应Jakarta EE 9
)及其以后版本的用户应该注意,由于将Java EE从Oracle转移到Eclipse基金会并改名为Jakarta EE(Jakarta EE 9开始
),所有已实现API的主要包都从javax.*
变更为jakarta.*
。这几乎肯定需要对应用程序进行代码更改,以便将其从Tomcat 9及更早版本迁移到Tomcat 10及以后版本。
JavaEE版本 | JDK版本 | Servlet版本 | Tomcat版本 | JSP版本 | Spring版本 |
---|---|---|---|---|---|
JAVA EE 1.2 | 1.1 and later | 2.2 | 3.3.x | 1.1 | |
JAVA EE 1.3 | 1.3 and later | 2.3 | 4.1.x | 1.2 | |
JAVA EE 1.4 | 1.4 and later | 2.4 | 5.5.x | 2.0 | |
JAVA EE 5 | 5 and later | 2.5 | 6.0.x | 2.1 | |
JAVA EE 6 | 6 and later (7 and later for WebSocket) | 3.0 | 7.0.x | 2.2 | |
JAVA EE 7 | 7 and later | 3.1 | 8.0.x | 2.3 | 5.3.x |
JAVA EE 7 | 7 and later | 3.1 | 8.5.x | 2.3 | 5.3.x |
JAVA EE 8 | 8 and later | 4.0 | 9.0.x | 2.3 | 5.3.x |
Jakarta EE 9 | 8 and later | 5.0 | 10.0.x | 3.0 | 6.0.x、6.1.x |
Jakarta EE 10 | 11 and later | 6.0 | 10.1.x | 3.1 | 6.0.x、6.1.x |
Jakarta EE 11 | 17 and later | 6.1 | 11.0.x | 4.0 | 6.0.x、6.1.x、6.2.x |
Servlet目录规范#
Web应用目录
- html、css、js、image等公共资源。
WEB-INF目录
:存放Java类和配置文件classes目录
:存放Java类,比如Servlet类
lib目录
:存放类库(第三方jar包),比如JDBC驱动
等等web.xml
:配置文件。配置请求路径
与Servlet类
的映射关系。
WEB-INF目录
放在WEB-INF目录下的资源是受保护的,不能通过路径直接访问。
所以像css、js、图片等静态资源要放到WEB-INF目录之外。
Servlet接口#
package jakarta.servlet;
import java.io.IOException;
/*
* 定义所有 servlet 必须实现的方法。
* Servlet 是在 Web 服务器中运行的小型 Java 程序。
* Servlet 通常通过 HTTP(超文本传输协议)接收和响应网络客户端的请求。
* */
public interface Servlet {
/*
* 由servlet容器(比如Tomcat)调用,用于向 servlet 指示该 servlet 正在投入服务。
* 在实例化 servlet 后,servlet 容器会调用一次init方法。
* */
public void init(ServletConfig config) throws ServletException;
/*
* 返回ServletConfig对象,其中包含此 servlet 的初始化和启动参数。
* 返回的ServletConfig对象就是传递给init方法的对象。
* */
public ServletConfig getServletConfig();
/*
* 由 servlet 容器调用,允许 servlet 响应请求。
* 该方法只有在 servlet 的init()方法成功完成后才会被调用。
*
* Servlet 通常在多线程 Servlet 容器中运行,可以同时处理多个请求。
* 开发人员必须注意同步访问任何共享资源,如文件、网络连接以及 servlet 的类和实例变量。
*
* req- 包含客户端请求的ServletRequest对象
* res- 包含 Servlet 响应的ServletResponse对象
* */
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
/*
* 返回有关 servlet 的信息,如作者、版本和版权。
* 此方法返回的字符串应为纯文本,而非任何类型的标记(如 HTML、XML 等)
* */
public String getServletInfo();
/*
* 由 servlet 容器(如Tomcat)调用,用于向 servlet 指示该 servlet 即将退出服务。
* 只有当 servlet服务方法中的所有线程都退出或超时后,才会调用此方法。
* 在 servlet 容器调用此方法后,它不会再调用此 servlet 的service方法。
*
* 该方法让 servlet 有机会清理任何被占用的资源(如内存、文件句柄、线程),
* 并确保任何持久化状态与 servlet 在内存中的当前状态同步。
* */
public void destroy();
}
Hello World#
- 使用IDEA创建项目:
- 编写Servlet
package com.luguosong;
import jakarta.servlet.*;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @author luguosong
*/
public class HelloServlet implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
/**
* @param request
* @param response
* @throws ServletException
* @throws IOException
*/
@Override
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
System.out.println("业务处理...");
// 设置响应内容类型
response.setContentType("text/html;charset=utf-8");
// 将响应的数据写入到输出流中
PrintWriter out = response.getWriter();
out.println("<h1>hello world</h1>");
}
@Override
public String getServletInfo() {
return "";
}
@Override
public void destroy() {
}
}
- 编写web.xml映射:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
version="6.0">
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.luguosong.HelloServlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
- IDEA中配置Tomcat
- 启动项目,成功访问Servlet
Servlet生命周期⭐#
Servlet的生命周期完全由Tomcat服务器控制。
- 默认情况下,Tomcat服务启动不会创建Servlet对象。
通过配置让tomcat在启动时创建对象
<web-app>
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.luguosong.HelloServlet</servlet-class>
<!--设置Servlet在启动时创建对象-->
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
- 第一次访问Servlet时,Tomcat会
创建Servlet对象
,依次调用Servlet无参构造方法
、init()初始化方法
、service()业务方法
。 - 之后再访问Servlet时,Tomcat只会调用对应Servlet
service()业务方法
。 - Tomcat服务器关闭时,会调用Servlet
destroy()销毁方法
(此时对象还并未销毁)。
GenericServlet抽象类#
概述#
GenericServlet抽象类
实现了Servlet接口
除service方法
外的其它方法。
service方法
则设置为抽象方法,需要子类实现。
基于GenericServlet开发#
- 编写Servlet类:
package org.example.genericservlet;
import java.io.*;
import jakarta.servlet.GenericServlet;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
public class HelloServlet extends GenericServlet {
/*
* 继承自GenericServlet,只需要实现service方法即可
* */
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
res.setContentType("text/html");
PrintWriter out = res.getWriter();
out.println("<h1>Hello GenericServlet!</h1>");
}
}
- 编写web.xml映射:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
version="6.0">
<servlet>
<servlet-name>GenericServlet</servlet-name>
<servlet-class>org.example.genericservlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>GenericServlet</servlet-name>
<url-pattern>/GenericServlet</url-pattern>
</servlet-mapping>
</web-app>
处理ServletConfig对象#
Tomcat初始化时,会调用init方法
,并传递ServletConfig对象
给init方法
。默认情况下ServletConfig对象
只能在init方法内部
调用。
public class GenericServlet implements Servlet {
@Override
public void init(ServletConfig config) throws ServletException {
}
/*
* ... 其它代码
* */
}
如果想在service方法
中访问ServletConfig对象
,可以将ServletConfig这个局部对象
传递给一个新建的ServletConfig字段
。达到可以在Servlet对象任意位置访问ServletConfig对象的目的。
public class GenericServlet implements Servlet {
/*
* 将Tomcat传递给init方法的ServletConfig对象升级为字段
* 方便其它方法调用
* */
private ServletConfig config;
@Override
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
/*
* 👍🏻该方法的作用是,防止子类重写init方法时,忘记执行 this.config = config;
* 导致config为空
* */
public void init() throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return config;
}
@Override
public void service(ServletRequest request, ServletResponse response) {
// ⭐service方法中可以访问到Tomcat传递给init方法的ServletConfig对象
System.out.println(config.getServletName());
}
/*
* ... 其它方法
* */
}
ServletConfig对象#
ServletConfig对象
中包含了web.xml中配置的<servlet>
标签信息。
ServletConfig对象
有四个方法:
getServletName()
getInitParameterNames()
getInitParameter(String name)
getServletContext()
Note
每个Servlet对应一个ServletConfig对象
<web-app>
<servlet>
<servlet-name>servletConfigDemo</servlet-name>
<servlet-class>com.luguosong.ServletConfigDemo</servlet-class>
<!--配置初始化信息-->
<init-param>
<param-name>user</param-name>
<param-value>root</param-value>
</init-param>
<init-param>
<param-name>password</param-name>
<param-value>12345678</param-value>
</init-param>
</servlet>
</web-app>
package com.luguosong;
import jakarta.servlet.*;
import java.io.IOException;
import java.util.Enumeration;
/**
* @author luguosong
*/
public class ServletConfigDemo extends GenericServlet {
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
ServletConfig config = getServletConfig();
/*
* ServletConfig接口由Tomcat负责实现
* 一个Servlet对象关联一个ServletConfig对象
* */
System.out.println(config); //org.apache.catalina.core.StandardWrapperFacade@280048c9
/*
* 对应web.xml中<servlet>标签中的<servlet-name>标签内容
* */
System.out.println(config.getServletName()); // servletContextDemo
/*
* 获取<servlet>标签下<init-param>标签信息
* */
Enumeration<String> names = config.getInitParameterNames();
while (names.hasMoreElements()) {
String name = names.nextElement();
System.out.println(name + ":" + config.getInitParameter(name));
}
}
}
GenericServlet
已经封装了调用ServletConfig中getServletName()
、getServletName()
和getServletName()
方法。因此可以直接调用,无需通过config进行调用:
package com.luguosong;
import jakarta.servlet.*;
import java.io.IOException;
import java.util.Enumeration;
/**
* @author luguosong
*/
public class ServletConfigDemo2 extends GenericServlet {
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
/*
* 对应web.xml中<servlet>标签中的<servlet-name>标签内容
* */
System.out.println(getServletName()); // servletContextDemo
/*
* 获取<servlet>标签下<init-param>标签信息
* */
Enumeration<String> names = getInitParameterNames();
while (names.hasMoreElements()) {
String name = names.nextElement();
System.out.println(name + ":" + getInitParameter(name));
}
}
}
ServletContext对象#
ServletContext对象
是一个Servlet
与其Servlet容器(Tomcat)
通信的一组方法,例如获取文件的MIME类型、分派请求或写入日志文件。
Note
所有Servlet共享同一个ServletContext对象
,一个web应用只有一个ServletContext对象
ServletContext对象
在服务器启动时创建。
ServletContext对象常用方法有:
getInitParameterNames()
,getInitParameter()
:获取web.xml中的上下文初始化参数<context-param>
getContextPath()
:获取应用根路径getRealPath()
:获取文件的绝对路径log()
:写入日志到log目录下的日志文件setAttribute()
、getAttribute()
、removeAttribute()
:操作应用域
,应用域中的数据所有Servlet共享。
package com.luguosong;
import jakarta.servlet.*;
import java.io.IOException;
import java.util.Enumeration;
/**
* @author luguosong
*/
public class ServletContextDemo extends GenericServlet {
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
/*
* 获取ServletContext对象
* */
ServletContext context = getServletConfig().getServletContext(); //方式一
context=getServletContext(); //方式二
/*
* 打印上下文初始化参数
* 对应web.xml中的<context-param>标签内容
* */
Enumeration<String> names = context.getInitParameterNames();
while (names.hasMoreElements()) {
String name = names.nextElement();
String value = context.getInitParameter(name);
System.out.println(name + "=" + value);
}
/*
* 获取应用的根路径
* */
System.out.println(context.getContextPath()); // /servlet_context_war_exploded
/*
* 获取文件的绝对路径
* */
System.out.println(context.getRealPath("/test.html")); //E:\IdeaCode\note\programming-study\docs\java_serve\web_application\servlet\servlet-context\target\servlet-context-1.0-SNAPSHOT\test.html
/*
* 会将日志记录到log目录下的localhost.xxxx-xx-xx.log日志中
* */
context.log("记录日志");
context.log("记录异常",new RuntimeException("发生异常"));
/*
* 应用域操作
* */
context.setAttribute("key","value"); //存数据
System.out.println(context.getAttribute("key")); //读数据
context.removeAttribute("key"); //删数据
}
}
HttpServlet类⭐#
案例#
package com.luguosong;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import java.io.IOException;
/**
* @author luguosong
*/
public class GetServletDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("处理doGet方法");
}
}
package com.luguosong;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author luguosong
*/
public class PostServletDemo extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
模板方法设计模式#
/*
* 模板类
* 简化的HttpServlet代码,只为展示模板方法设计模式
* */
public abstract class HttpServlet extends GenericServlet {
/*
* 默认实现
* HttpServlet默认实现的doGet是直接响应405错误状态码
* */
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_get_not_supported");
resp.sendError(getMethodNotSupportedCode(protocol), msg);
}
/*
* 默认实现
* HttpServlet默认实现的doPost是直接响应405错误状态码
* */
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_post_not_supported");
resp.sendError(getMethodNotSupportedCode(protocol), msg);
}
/*
* ⭐模板方法
* */
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod();
/*
* 判断请求的类型
* */
if (method.equals(METHOD_GET)) {
doGet(req, resp);
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else {
/*其它方法判断*/
}
}
/*其它代码*/
}
可以看到,HttpServlet类
已经实现GenericServlet类
剩下的最后一个抽象方法service()
。
并在service()
方法中定义了一个处理各种类型请求的骨架
。并默认实现了处理各种请求的具体方法(doGet()、doPost() ...),但处理方式是直接响应405错误状态码。这样如果我们需要处理具体类型请求的代码,就需要自己在子类中重写这个方法。覆盖调父类的报错代码。
举例说明:前端发送一个Get请求,我们子类中没有重写doGet方法,则直接通过HttpServlet类的doGet方法响应405错误状态码。如果我们的子类实现了doGet方法,那么就调用我们自己写的doGet方法。
HttpServletRequest接口⭐#
实现类#
HttpServletRequest接口
由Tomcat负责实现
package com.luguosong;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author luguosong
*/
public class HelloHttpServletRequest extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println(req); //org.apache.catalina.connector.RequestFacade@43a7853b
}
}
获取Http请求信息#
package com.luguosong;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Map;
/**
* @author luguosong
*/
public class GetRequestInfo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
PrintWriter writer = resp.getWriter();
//解决响应中文乱码
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/plain");
/*
* 请求域操作
*
* 请求域只在一次请求内有效
* */
req.setAttribute("name", "luguosong");
req.getAttribute("name");
req.removeAttribute("name");
/*
* 获取客户端信息
* */
String remoteAddr = req.getRemoteAddr(); //获取客户端ip地址
writer.println("客户端ip地址:" + remoteAddr);
String contextPath = req.getContextPath(); // 获取应用路径
writer.println("应用路径:" + contextPath);
// 当但也可以通过ServletContext获取
String contextPath2 = getServletContext().getContextPath();
writer.println("应用路径:" + contextPath2);
// 获取Servlet路径,相比于getRequestURI不带应用路径
String servletPath = req.getServletPath();
writer.println("Servlet路径:" + servletPath);
//////////////////////////////////////////////////////////////////////////////
/*
* 获取请求行信息
* */
String httpMethod = req.getMethod(); //获取请求方法
writer.println("请求方法:" + httpMethod);
String requestURI = req.getRequestURI(); //获取请求路径
writer.println("URI:" + requestURI);
String protocol = req.getProtocol(); // 获取Http协议版本
writer.println("协议:" + protocol);
/*
* 获取请求头信息
* */
String accept = req.getHeader("Accept"); // 参数为指定头名称
writer.println("Accept:" + accept);
/*
* 表单请求参数获取
* 获取application/x-www-form-urlencoded类型的数据
* */
Map<String, String[]> parameterMap = req.getParameterMap(); //获取参数Map集合
Enumeration<String> names = req.getParameterNames(); //获取参数名的枚举
String[] values1 = req.getParameterValues("name"); //获取指定参数名的参数值数组
writer.println("获取参数值数组:" + Arrays.toString(values1));
String value2 = req.getParameter("name"); // 🔥获取指定参数名的参数值
writer.println("获取参数:" + value2);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
/*
* 解决Post请求中文乱码
*
* ❗Get请求没有乱码问题,Get请求参数默认使用UTF-8编码
* */
req.setCharacterEncoding("UTF-8");
}
}
转发(Forward)#
会保留请求域
对象,达到两个Servlet共享数据的目的。
package com.luguosong;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author luguosong
*/
public class DispatcherServletA extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setAttribute("time", System.currentTimeMillis());
// 请求转发
// 参数:指定资源路径名的字符串。如果是相对路径,则必须与当前 servlet 相对。
RequestDispatcher dispatcher = req.getRequestDispatcher("/dispatcherB");
dispatcher.forward(req, resp);
}
}
package com.luguosong;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author luguosong
*/
public class DispatcherServletB extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 被转发的Servlet对象会保留转发Servlet的请求域对象
resp.getWriter().println(req.getAttribute("time"));
}
}
Note
转发整个过程是一次请求
。
HttpServletResponse接口⭐#
响应字符#
package com.luguosong;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @author luguosong
*/
public class WriteDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String data = "使用PrintWriter流输出中文";
PrintWriter out = resp.getWriter();
resp.setCharacterEncoding("UTF-8"); //Tomcat10默认使用UTF-8,这一步可以省略
resp.setHeader("content-type", "text/html;charset=UTF-8");
//或者
//resp.setContentType("text/html;charset=UTF-8");
out.write(data);
}
}
响应字节流#
package com.luguosong;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* @author luguosong
*/
public class OutputStreamDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String data = "使用OutputStream流向浏览器输出中文";
ServletOutputStream outputStream = resp.getOutputStream();
resp.setHeader("content-type", "text/html;charset=UTF-8");
byte[] dataByteArr = data.getBytes(StandardCharsets.UTF_8);
outputStream.write(dataByteArr);
}
}
重定向#
package com.luguosong;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author luguosong
*/
public class RedirectA extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setAttribute("time", System.currentTimeMillis());
resp.sendRedirect(req.getContextPath() + "/redirectB");
}
}
package com.luguosong;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author luguosong
*/
public class RedirectB extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setHeader("content-type", "text/html;charset=UTF-8");
resp.getWriter().println("重定向无法获取请求域:" + req.getAttribute("time"));
}
}
转发和重定向对比#
转发 | 重定向 | |
---|---|---|
几次请求 | 一次 | 两次 |
请求域 | 共享请求域 | 不同请求域 |
发起类 | HttpServletRequest发起 | HttpServletResponse发起 |
路径 | 相对于Servlet路径 | 从项目名开始的路径 |
完成方式 | Tomcat服务器内部完成 | 响应给浏览器,由浏览器完成 |
浏览器地址栏显示 | 依旧显示发起转发的Servlet | 显示重定向后的Servlet或资源 |
注解开发#
Servlet 3.0后推出了注解开发,使用注解开发不再需要配置web.xml
,直接在类上使用注解即可。
package com.luguosong;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebInitParam;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @author luguosong
*/
@WebServlet(urlPatterns = {"/helloServlet"},
initParams = {@WebInitParam(name = "name", value = "张三")})
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setHeader("content-type", "text/html;charset=UTF-8");
String name = getInitParameter("name");
PrintWriter writer = resp.getWriter();
writer.println("获取Servlet初始化参数:" + name);
}
}