文章目录
浏览器与服务器交互方式分析
原理示意图:
访问地址URL的构成:
则访问该资源的查找过程是:
①根据主机地址找到一个具体的服务器
②根据Web应用虚拟路径找到Web应用
③根据Web应用下资源的虚拟路径访问具体资源
[1]如果资源是静态的,那么Tomcat会返回资源本身
注:静态资源也需要使用到servlet,只不过是Tomcat里面已经定义好了一个 DefaultServlet
[2]如果资源是动态的,例如Servlet,那么Tomcat会先执行Servlet程序,返回Servlet程序的运行结果
④无论访问的目标资源是不是静态的,浏览器最终得到的都是静态数据:图片或字符串。
什么是Servlet
Servlet是一个java程序,运行在我们的web服务器上,用于接收和响应客户端的http请求。
Servlet规范是Sun公司制定的一套技术标准,包含与Web应用相关的一系列接口,是Web应用实现方式的宏观解决方案。
Servlet是服务器端的一个组件,Servlet的实例对象由Servlet容器负责创建;Servlet的方法由容器在特定情况下调用;Servlet容器会在Web应用卸载时销毁Servlet对象的实例。
Servlet的应用:通过网页驱动服务器端的Java程序,在网页上显示Java程序返回的数据。
如何使用Servlet
1.需要有一个服务器,即搭建Web开发环境,然后创建动态Web工程。
2.创建javax.servlet.Servlet接口的实现类:
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class HelloServlet implements Servlet {
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
//在控制台输出,证明方法是否调用
System.out.println("HelloServlet service");
//向浏览器发送响应消息
PrintWriter writer=res.getWriter();
writer.write("HelloServlet Response");
writer.close();
}
@Override
public void init(ServletConfig config) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
3.注册配置Servlet :告诉服务器,我们的应用有这个servlet。
在webContent/WEB-INF/web.xml里面注册。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>01_ServletTest</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<!-- 声明一个Servlet,向tomcat声明, 这个应用里面有这个servlet, 名字叫做HelloServlet , 具体的路径是com.mycode01.servlet.HelloServlet-->
<servlet>
<!-- 为Servlet指定一个名称,以便于引用 -->
<servlet-name>HelloServlet</servlet-name>
<!-- 注册Servlet实现类的全类名 -->
<servlet-class>com.mycode01.servlet.HelloServlet</servlet-class>
</servlet>
<!-- 建立一个从虚拟路径到Servlet之间的映射关系 -->
<servlet-mapping>
<!-- 引用Servlet名称 -->
<servlet-name>HelloServlet</servlet-name>
<!-- 映射到Servlet的虚拟路径:“/HelloServlet” -->
<url-pattern>/ServletTest</url-pattern>
</servlet-mapping>
</web-app>
4.在WebContent目录下创建index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<a href="ServletTest">通过网页驱动服务器端的Java程序,在网页上显示Java程序返回的数据</a>
<!-- href="ServletTest" 对应<servlet-mapping>中 <url-pattern>的值 -->
</body>
</html>
5.把web 应用程序部署到服务器,点击超链接(通过网页驱动服务器端的Java程序,在网页上显示Java程序返回的数据)测试Servlet
Servlet执行过程分析
通过上面的测试分析
URL地址(请求地址):http://localhost:8080/01_ServletTest/ServletTest
Servlet接口的实现类
Servlet的配置
执行过程:
1.找到Tomcat应用
2.找到项目(01_ServletTest)
3.找web.xml, 查找是否有url-pattern的内容是/ServletTest
4. 如果有url-pattern的内容是/ServletTest,便找到对应servlet-mapping中的那个servlet-name(HelloServlet)
5.再找到servlet元素中的servlet-name(HelloServlet),找到下面定义了的servlet-class,然后开始创建该类的对象,执行该类的service方法
Servlet技术体系
1.servlet接口
2.GenericServlet抽象类(实现servlet接口)
对Servlet功能进行了封装和完善,将service(ServletRequest req, ServletResponse res)保留为抽象方法,让使用者仅关心业务实现即可。
3.HttpServlet抽象类(继承GenericServlet):用于处理http的请求
对GenericServlet进行了进一步的封装和扩展,更贴近HTTP协议下的应用程序编写,在service(ServletRequest req, ServletResponse res)方法中,根据不同HTTP请求类型调用专门的方法进行处理。
Servlet的通用写法:实际使用中继承HttpServlet抽象类创建自己的Servlet实现类即可。重写doGet(HttpServletRequest req, HttpServletResponse resp)和doPost(HttpServletRequest req, HttpServletResponse resp)方法实现请求处理,不再需要重写service(ServletRequest req, ServletResponse res)方法。
Servlet的通用写法
定义一个类,继承HttpServlet 重写doGet 和 doPost,注册配置该Servlet。
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/** * Servlet implementation class HelloHttpServlet */
public class HelloHttpServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/** * @see HttpServlet#HttpServlet() */
public HelloHttpServlet() {
super();
}
/** * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) */
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().append("Served at: ").append(request.getContextPath());
}
/** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) */
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
Servlet配置说明
1.web3.0以前的版本,Servlet的配置使用web.xml
2.web3.0以上版本,Servlet的配置使用的是注解@WebServlet
如: 创建Servlet2,在@WebServlet注解中添加urlPatterns = “/test”,作为请求路径。
对以上接口的说明:
ServletConfig接口:封装了Servlet配置信息
ServletContext接口:封装了当前Web应用上下文信息
HttpServletRequest接口:封装了HTTP请求信息,ServletRequest的子接口
HttpServletResponse接口:封装了HTTP响应信息,ServletResponse的子接口
ServletConfig接口
ServletConfig接口封装了Servlet配置信息,当前Web应用的ServletContext对象也封装到了ServletConfig对象中,使ServletConfig对象可以获取ServletContext对象。
ServletConfig对象的主要功能:
1.获取Servlet名称
2.获取Servlet初始化参数
3.获取ServletContext对象
代码示例
Servlet的配置:
<servlet>
<servlet-name>HelloHttpServlet</servlet-name>
<servlet-class>com.mycode01.servlet.HelloHttpServlet</servlet-class>
<!-- 配置Servlet初始化参数 -->
<init-param>
<param-name>MyName</param-name>
<param-value>MyValue</param-value>
</init-param>
<init-param>
<param-name>age</param-name>
<param-value>18</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>HelloHttpServlet</servlet-name>
<url-pattern>/HttpServletTest</url-pattern>
</servlet-mapping>
测试ServletConfig方法:
public class HelloHttpServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//得到servletConfig对象
ServletConfig servletConfig = getServletConfig();
//获取配置servlet里面servlet-name的文本内容
String servletName = servletConfig.getServletName();
System.out.println("servletName="+servletName);
System.out.println("------------------------------------");
//获取具体的某一个参数。
String age = servletConfig.getInitParameter("age");
System.out.println("age="+age);
System.out.println("------------------------------------");
//3.获取所有的参数名称
Enumeration<String> names = servletConfig.getInitParameterNames();
//遍历取出所有的参数名称
while (names.hasMoreElements()) {
String key = (String) names.nextElement();
String value = servletConfig.getInitParameter(key);
System.out.println("key==="+key + " value="+value);
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
运行结果:
扩展:ServletConfig的另一个作用
我们自己开发一些应用,使用到了一些技术,或者一些代码,我们不会。 但是有人写出来了。它的代码放置在了自己的servlet类里面。
刚好这个servlet 里面需要一个数字或者叫做变量值。 但是这个值不能是固定了。 所以要求使用到这个servlet的公司,在注册servlet的时候,必须要在web.xml里面,声明init-params
Servlet的配置:
<servlet>
<servlet-name>ServletConfigTest</servlet-name>
<servlet-class>com.mycode01.servlet.ServletConfigTest</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ServletConfigTest</servlet-name>
<url-pattern>/ServletConfigTest</url-pattern>
</servlet-mapping>
ServletConfigTest 类:
public class ServletConfigTest extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletConfig config = getServletConfig();
//获取number这个参数。
String number = config.getInitParameter("number");
if(number == null){
throw new IllegalArgumentException("servlet在配置的时候,要写number这个参数");
}else{
System.out.println("number....");
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
运行访问:http://localhost:8080/01_ServletTest/ServletConfigTest
ServletContext接口
一个Web应用程序中的所有Servlet都共享同一个ServletContext对象,所以ServletContext对象也被称为 application 对象(Web应用程序对象)。
在应用程序中能够获取运行环境或容器信息的对象通常称之为“上下文对象”。
ServletContext对象的主要功能:
1.获取Web应用程序的全局配置参数
- 设置Web应用初始化参数(在web.xml的根标签下添加)
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>01_ServletTest</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<!-- 配置全局参数 -->
<context-param>
<param-name>num</param-name>
<param-value>123</param-value>
</context-param>
<servlet>
<servlet-name>ServletContextTest</servlet-name>
<servlet-class>com.mycode01.servlet.ServletContextTest</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ServletContextTest</servlet-name>
<url-pattern>/ServletContextTest</url-pattern>
</servlet-mapping>
</web-app>
- 获取Web应用初始化参数
public class ServletContextTest extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取ServletContext对象
ServletContext servletContext = getServletContext();
//获取Web应用初始化参数
String num = servletContext.getInitParameter("num");
//在控制台输出
System.out.println("num="+num);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
访问:http://localhost:8080/01_ServletTest/ServletContextTest
2.获取虚拟路径所映射的本地路径(在服务器上的绝对路径)
虚拟路径:浏览器访问Web应用中资源时所使用的路径。
本地路径:资源在文件系统中的实际保存路径。即在服务器上的绝对路径
public class ServletContextTest2 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取ServletContext对象
ServletContext servletContext = getServletContext();
//2.0获取index.html的本地路径(在服务器上的绝对路径)
//index.html的虚拟路径是“/index.html”,其中“/”表示当前Web应用的根目录,即WebContent目录
// String realPath = servletContext.getRealPath("/index.html");
// System.out.println("realPath="+realPath);
// realPath=D:\tomcat\tomcat01\apache-tomcat-8.5.50\wtpwebapps\01_ServletTest\index.html
//2.1获取ServletContextTest的本地路径
String realPath = servletContext.getRealPath("/ServletContextTest");
System.out.println("realPath="+realPath);
// realPath=D:\tomcat\tomcat01\apache-tomcat-8.5.50\wtpwebapps\01_ServletTest\ServletContextTest
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
扩展1: 使用ServletContext 获取Web应用程序中的文件资源
Web工程下的资源,通过一般路径和FileInputStream是获取不到文件的,因为Web工程jre是由tomcat管理的,而文件资源的相对路径是根据jre来确定的。
如要获取以下资源文件
代码实现(方式一):servletContext.getRealPath(“file/cofig.properties”);
public class ServletContextTest3 extends HttpServlet {
@SuppressWarnings("resource")
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取ServletContext对象
ServletContext servletContext = getServletContext();
//获取项目的根路径
String itemRootPath = servletContext.getRealPath("");
System.out.println("itemRootPath="+itemRootPath);
// itemRootPath=D:\tomcat\tomcat01\apache-tomcat-8.5.50\wtpwebapps\01_ServletTest\
System.out.println("-----------------------------");
//获取cofig.properties文件的本地路径 (在服务器上的绝对路径)
String realPath = servletContext.getRealPath("file/cofig.properties");
System.out.println("realPath="+realPath);
// realPath=D:\tomcat\tomcat01\apache-tomcat-8.5.50\wtpwebapps\01_ServletTest\file\cofig.properties
System.out.println("-----------------------------");
//创建属性对象
java.util.Properties properties = new java.util.Properties();
InputStream fileInputStream = new FileInputStream(realPath);
properties.load(fileInputStream);
//获取key属性的值
String value = properties.getProperty("key");
//在控制台输出
System.out.println("value="+value);
// value=value
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
访问http://localhost:8080/01_ServletTest/ServletContextTest3后控制台输出:
代码实现(方式二):servletContext.getResourceAsStream(“file/cofig.properties”);
public class ServletContextTest4 extends HttpServlet {
@SuppressWarnings("resource")
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取ServletContext对象
ServletContext servletContext = getServletContext();
//创建属性对象
java.util.Properties properties = new java.util.Properties();
//根据web工程下的文件资源路径,得到一个流对象
//现在(ServletContextTest4 )所处路径是项目的根路径:D:\tomcat\tomcat01\apache-tomcat-8.5.50\wtpwebapps\01_ServletTest; 所以getResourceAsStream的参数路径可以是一个相对路径
InputStream fileInputStream =servletContext.getResourceAsStream("file/cofig.properties");
properties.load(fileInputStream);
//获取key属性的值
String value = properties.getProperty("key");
//在控制台输出
System.out.println("value="+value);
// value=value
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
扩展2:使用ClassLoader获取Web应用程序中的文件资源
public class ServletContextTest5 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//创建属性对象
java.util.Properties properties = new java.util.Properties();
//this.getClass() 获取该java文件的class getClassLoader() 获取加载这个class到虚拟机中的类加载器对象
InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("../../file/cofig.properties");
//关于路径问题:
/* * 使用ClassLoader的根路径:D:\tomcat\tomcat01\apache-tomcat-8.5.50\wtpwebapps\01_ServletTest\WEB-INF\classes * cofig.properties的路径:D:\tomcat\tomcat01\apache-tomcat-8.5.50\wtpwebapps\01_ServletTest\file\cofig.properties * * getResourceAsStream("../../file/cofig.properties");参数使用的是相对路径 * ../../ 返回上上层到 D:\tomcat\tomcat01\apache-tomcat-8.5.50\wtpwebapps\01_ServletTest * ../../file/cofig.properties 实质是 D:\tomcat\tomcat01\apache-tomcat-8.5.50\wtpwebapps\01_ServletTest\file\cofig.properties */
properties.load(resourceAsStream);
//获取key属性的值
String value = properties.getProperty("key");
//在控制台输出
System.out.println("value="+value);
// value=value
resourceAsStream.close();
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
3.存储数据,servlet间共享数据
一个案例:定义一个login.html登录界面,点击登录,通过网页驱动服务器端的Java程序,执行LoginServlet判断是否登录成功,通过ServletContext 记录登录成功次数并跳转到login_success.html界面,点击login_success.html界面的超链接执行CountServlet通过ServletContext 获取登录的次数,显示到浏览器页面上。体现了LoginServlet与CountServlet共享同一个ServletContext对象。
1).login.html登录界面
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<!-- 关于 action="LoginServlet" 路径问题
LoginServlet的访问路径:http://localhost:8080/01_ServletTest/LoginServlet
login.html的访问路径:http://localhost:8080/01_ServletTest/login.html
故 action的路径可以写相对路径LoginServlet
-->
<form action="LoginServlet" method="get">
用户名:<input type="text" name="user">
密码:<input type="text" name="password">
<input type="submit" value="登录">
</form>
</body>
</html>
2).点击登录执行LoginServlet
public class LoginServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//如果是post请求,设置request对象使用UTF-8字符集对请求体中的内容进行解码,这样如果提交过来的用户名等是中文,则不会乱码
request.setCharacterEncoding("UTF-8");
//获取请求数据
String user = request.getParameter("user");
String password = request.getParameter("password");
System.out.println(user+" "+password);
//1.指定数据输出到客户端时,使用UTF-8编码
response.setCharacterEncoding("UTF-8");
//2.规定浏览器解码数据时使用什么编码(因为这里服务器使用UTF-8编码,所以浏览器解码规定使用UTF-8)
response.setHeader("Content-Type","text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
if("乘零".equals(user)&&"1".equals(password)){
//登录成功,记录登录次数并跳转到login_success.html页面
//1.记录登录次数
//返回具有给定名称的servlet容器属性,如果没有该名称的属性,则返回空值。
Object attribute = getServletContext().getAttribute("count");
int totalCount=0;
if(attribute!=null){
totalCount=(int) attribute;
}
getServletContext().setAttribute("count", totalCount+1);
//2.跳转到login_success.html页面
//设置状态码,重新定位
response.setStatus(302);
//定位跳转的位置是那个界面
response.setHeader("Location", "login_success.html");
//跳转到百度
// response.setHeader("Location", "https://www.baidu.com");
}else{
writer.write("登录失败");
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
3).执行LoginServlet定位跳转到login_success.html界面
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<a href="CountServlet">获取登录总数</a>
</body>
</html>
4).点击login_success.html界面的超链接执行ServletContext 获取登录的次数
public class CountServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//设置响应数据类型是html文本,并告诉浏览器使用UTF-8解码
response.setContentType("text/html;charset=utf-8");
int count = (int) getServletContext().getAttribute("count");
PrintWriter writer = response.getWriter();
writer.write("成功登录"+count+"次");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
HttpServletRequest接口
该接口是ServletRequest接口的子接口,封装了HTTP请求的相关信息,由Servlet容器创建其实现类对象并传入service(ServletRequest req, ServletResponse res)方法中。以下所说的HttpServletRequest对象指的是容器提供的HttpServletRequest实现类对象。
HttpServletRequest对象的主要功能:
1. 获取请求参数
//获取客户端提交上来的数据( 获取单值的表单参数),如果没有该名称的属性,则返回空值。
String user = request.getParameter("user");
//获取多值的表单参数
String[] values = request.getParameterValues(String name);
//获取所有的表单参数
//map的key:是表单项的name map的value:是表单项的值
Map<String, String[]> map = request.getParameterMap();
//获取所有参数,得到一个枚举集合
Enumeration<String> parameterNames = request.getParameterNames();
2.request是一个域对象,在请求域中存取数据
//域对象:由Servlet规范提供的,可以临时存取数据的对象。域对象里的数据,在其作用范围里共享.
//request域对象
//何时创建:一次请求开始
//何时销毁:一次请求结束
//作用范围:一次请求中
//把所有学生数据存储到作用域中
request.setAttribute("allStudent", allStudent);
3.将请求转发给另外一个URL地址(见请求的转发)
4.获取 服务器IP,服务器软件端口,项目虚拟路径(web项目的context路径)
服务器IP
String serverName = request.getServerName();
端口
int serverPort = request.getServerPort();
String contextPath=request.getContextPath()
//以访问路径为:http://localhost:8080/test/index.html为例
//request.getServerName() 得到的就是 localhost
// request.getServerPort() 得到的就是 8080
//request.getContextPath()得到的就是 /test
5.获取头信息
public class HttpServletRequestTest extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取请求数据所有头信息得到一个枚举集合
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String name = (String) headerNames.nextElement();
//返回指定请求头的值。如果请求没有包含指定名称的头,则此方法返回空值。如果有多个同名的头,则此方法返回请求中的第一个头.
String header = request.getHeader(name);
System.out.println(name+"="+header);
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
访问:http://localhost:8080/01_ServletTest/HttpServletRequestTest
HttpServletRequest中文乱码问题(请求参数中含有中文)
请求由浏览器发送给服务器
- 浏览器编码:浏览器使用HTML页面指定的字符集编码,只需要将HTML页面的字符集指定为UTF-8即可
- 服务器解码:默认字符集是IOS-8859-1
解决方案:
- 对于GET请求:GET请求是在URL地址栏中传递请求参数的(如通过UTF-8编码),它会被Tomcat服务器自动解码,而Tomcat服务器默认的字符集也是ISO-8859-1,所以我们需要修改Tomcat服务器的字符集为UTF-8。
方案一:由于请求参数是包含在请求行内的,无法通过request对象进行设置,所以需要在Server.xml文件中修改Connector标签,添加URIEncoding="utf-8"属性,则get请求过来的数据Tomact都是使用UTF-8解码。
方案二:如果是将请求参数输出到控制台,则可不修改Connector标签,在该Servlet中执行以下方法再输出
String name="中文";
//使用ISO-8859-1将此字符串编码为一个字节序列,再通过指定的字符集解码指定的字节数组来构造新字符串
String name=new String(name.getBytes("ISO-8859-1"),"UTF-8");
System.out.println(name);
- 对于POST请求:post请求在Servlet中解码,默认的字符集是ISO-8859-1,不支持中文,所以我们需要修改Servlet的字符集为UTF-8。使用request.setCharacterEncoding(“UTF-8”);设置request对象使用UTF-8字符集对请求体中的内容进行解码。但需要注意的是,这个操作一定要在调用getParameter()方法之前进行。
使用情况:如解决登录时post请求用户名为中文的问题。
HttpServletResponse接口
该接口是ServletResponse接口的子接口,封装了HTTP响应的相关信息,由Servlet容器创建其实现类对象并传入service(ServletRequest req, ServletResponse res)方法中。以下所说的HttpServletResponse对象指的是容器提供的HttpServletResponse实现类对象。
HttpServletResponse对象的主要功能
1.浏览器输出数据(设置响应体)
//以字符流写数据
response.getWriter().write("<h3>可以写html</h3>");
//以字节流写数据
response.getOutputStream().write("字节流".getBytes());
2.实现请求重定向(见请求的重定向)。
3.案例:HttpServletResponse下载文件
- 新建一个download.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<!-- 点击链接时,执行DownloadServlet,手动编码提供下载 -->
<a href="DownloadServlet?filename=logo.png">执行DownloadServlet,手动编码提供下载logo.png</a> <br>
<!--点击链接时,执行DefaultServlet(tomcat默认的Servlet,专门用于处理服务器上的静态资源),在页面展示文件 -->
<a href="file/logo.png">执行DefaultServlet(tomcat默认的Servlet,专门用于处理服务器上的静态资源)展示logo.png</a>
</body>
</html>
访问download.html,点击第一个链接,执行DownloadServlet
- DownloadServlet.java
public class DownloadServlet extends HttpServlet {
@SuppressWarnings("resource")
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取客户端提交上来的数据(logo.png)
String filename = request.getParameter("filename");
//用户访问该资源时,使浏览器以下载的方式提醒用户,而不是直接展示,filename为下载弹框中文件的名称
response.setHeader("Content-Disposition", "attachment;filename="+filename);
//获取logo.png文件的本地路径 (在服务器上的绝对路径)
String realPath = getServletContext().getRealPath("file/"+filename);
//转化为输入流
InputStream inputStream = new FileInputStream(realPath);
//获取filename的输入流
//InputStream is = context.getResourceAsStream("WEB-INF/file/" + filename);
ServletOutputStream outputStream = response.getOutputStream();
int len=0;
byte[] buffer=new byte[1024];
while((len=inputStream.read(buffer))!=-1){
outputStream.write(buffer,0,len);
}
inputStream.close();
outputStream.close();
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
浏览器以下载的方式提醒用户
3.1 下载时文件名称为中文的处理
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!-- 点击链接时,执行DownloadServlet,手动编码提供下载 -->
<a href="DownloadServlet?filename=01.jpg">01.jpg</a> <br>
</body>
</html>
DownloadServlet(不做处理)
@WebServlet(name = "DownloadServlet",urlPatterns = "/DownloadServlet")
public class DownloadServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取客户端提交上来的数据(01.jpg)
String filename = request.getParameter("filename");
//用户访问该资源时,使浏览器以下载的方式提醒用户,而不是直接展示
response.setHeader("Content-Disposition", "attachment;filename=图片.jpg");
// response.setHeader("Content-Disposition", "attachment;filename="+DownloadUtils.encodeFilename(request, "图片"));
//获取logo.png文件的本地路径 (在服务器上的绝对路径)
String realPath = getServletContext().getRealPath("img/"+filename);
//转化为输入流
InputStream inputStream = new FileInputStream(realPath);
ServletOutputStream outputStream = response.getOutputStream();
int len=0;
byte[] buffer=new byte[1024];
while((len=inputStream.read(buffer))!=-1){
outputStream.write(buffer,0,len);
}
inputStream.close();
outputStream.close();
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
DownloadServlet(使用工具类DownloadUtils处理)
@WebServlet(name = "DownloadServlet",urlPatterns = "/DownloadServlet")
public class DownloadServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取客户端提交上来的数据(logo.png)
String filename = request.getParameter("filename");
//用户访问该资源时,使浏览器以下载的方式提醒用户,而不是直接展示,filename文件名称为中文
//编码:response编码响应头(ISO-8859-1进行编码); 解码:浏览器解码,不同浏览器解码方式不同,而且不能更改
//在服务端文件名称是中文,则把中文转换成不带中文的字符串,到客户端由浏览还原成中文的字符串。
response.setHeader("Content-Disposition", "attachment;filename="+DownloadUtils.encodeFilename(request, "图片.jpg"));
// response.setHeader("Content-Disposition", "attachment;filename=图片");
//获取logo.png文件的本地路径 (在服务器上的绝对路径)
String realPath = getServletContext().getRealPath("img/"+filename);
//转化为输入流
InputStream inputStream = new FileInputStream(realPath);
ServletOutputStream outputStream = response.getOutputStream();
int len=0;
byte[] buffer=new byte[1024];
while((len=inputStream.read(buffer))!=-1){
outputStream.write(buffer,0,len);
}
inputStream.close();
outputStream.close();
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
DownloadUtils(中文文件名称乱码解决的工具类)——来源:网络
public class DownloadUtils {
/** * 解决下载时,中文文件名称乱码问题<br> * * @param request request对象 * @param filename 你想让用户保存的文件名称 */
public static String encodeFilename(HttpServletRequest request, String filename){
try{
String agent = request.getHeader("User-Agent");
if (agent.contains("MSIE")) {
// IE浏览器
filename = URLEncoder.encode(filename, "utf-8");
filename = filename.replace("+", " ");
} else if (agent.contains("Firefox")) {
// 火狐浏览器
//BASE64Encoder base64Encoder = new BASE64Encoder();
Base64.Encoder base64Encoder = Base64.getEncoder();
filename = base64Encoder.encodeToString(filename.getBytes("utf-8"));
filename = "=?utf-8?B?"+ filename + "?=";
} else {
// 其它浏览器
filename = URLEncoder.encode(filename, "utf-8");
}
}catch(Exception e){
e.printStackTrace();
}
return filename;
}
}
(代解决:原本是中文名称资源的如何下载)
HttpServletResponse中文乱码问题(服务器响应中含有中文)
响应由服务器发送给浏览器
- 服务器编码:默认使用ISO-8859-1进行编码
- 浏览器解码:默认使用GBK进行解码
解决方案
需要在在response.getWriter() , response.getOutputStream()之前进行设置
方案一:指定服务器编码方式,设置响应头
//1.指定数据输出到客户端时,使用UTF-8编码
response.setCharacterEncoding("UTF-8");
//2.规定浏览器解码数据时使用什么编码(因为这里服务器使用UTF-8编码,所以浏览器解码规定使用UTF-8)
response.setHeader("Content-Type","text/html;charset=utf-8");
方案二:设置响应数据类型
//设置响应数据类型是html文本,并告诉浏览器使用UTF-8解码
response.setContentType("text/html;charset=utf-8");
注:默认的getOutputStream输出时是使用UTF-8码表。
请求的转发与重定向
请求的转发与重定向是Servlet控制页面跳转的主要方法。
请求的转发
1.Servlet接收到浏览器端请求后,进行一定的处理,先不进行响应,而是在服务器端内部“转发”给其他Servlet程序继续处理。在这种情况下浏览器端只发出了一次请求,浏览器地址栏不会发生变化,用户也感知不到请求被转发了。
2.转发请求的Servlet和目标Servlet共享同一个request对象。
代码示例:
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);
}
请求的重定向
1.Servlet接收到浏览器端请求并处理完成后,给浏览器端一个特殊的响应,这个特殊的响应要求浏览器去请求一个新的资源,整个过程中浏览器端会发出两次请求,且浏览器地址栏会改变为新资源的地址。
2.重定向的情况下,原Servlet和目标资源之间就不能共享请求域数据了( 后续的请求,不能使用上一次request存储的数据,因为这是两次不同的请求。)。
代码示例:
protected void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
//1.调用HttpServletResponse对象的sendRedirect()方法
//2.传入的参数是目标资源的虚拟路径
response.sendRedirect("index.html");
//扩展:和重定向效果相同的写法:
//设置状态码,重新定位
response.setStatus(302);
//定位跳转的位置是那个界面
response.setHeader("Location", "index.html");
}
请求的转发与重定向示意图:
对比请求的转发和重定向 | 转发 | 重定向 |
---|---|---|
浏览器地址栏 | 不改变 | 改变 |
发送请求次数 | 1 | 2 |
能否共享request对象数据 | 能 | 否 |
目标资源:WEB-INF下的资源 | 能访问 | 不能访问 |
跳转路径 | 自己项目的资源路径 | 可跳转到任意路径 |
如果有数据要传递给下一个资源,就要使用请求转发;否则使用重定向。
Servlet生命周期
0)Servlet对象的创建
默认情况下,Servlet容器第一次收到HTTP请求时创建对应Servlet对象。容器之所以能做到这一点是由于我们在注册Servlet时提供了全类名,容器使用反射技术创建了Servlet的对象。
1)Servlet对象初始化(init方法)
Servlet容器创建Servlet对象之后,会调用init(ServletConfig config)方法,对其进行初始化。在javax.servlet.Servlet接口中,public void init(ServletConfig config)方法要求容器将ServletConfig的实例对象传入,这也是我们获取ServletConfig的实例对象的根本方法。
2)处理请求(service方法)
在javax.servlet.Servlet接口中,定义了service(ServletRequest req, ServletResponse res)方法处理HTTP请求,同时要求容器将ServletRequest对象和ServletResponse对象传入。
3)Servlet对象销毁(destroy方法)
服务器重启或服务器停止执行时会销毁Servlet对象,而销毁之前为了执行一些诸如释放缓存、关闭连接、保存数据等操作,所以设计了public void destroy()方法。
扩展:让Servlet 提前创建实例
默认情况下,Servlet容器创建Servlet对象之后,才会执行init方法。 有的时候可能需要在这个方法里面执行一些初始化工作,做一些比较耗时的逻辑。 那么我们可以让这个初始化的操作提前。
在注册Servlet的时候, 使用load-on-startup元素来指定, 给定的数字越小,启动的时机就越早。 一般不写负数, 从2开始即可。
<servlet>
<servlet-name>HelloHttpServlet</servlet-name>
<servlet-class>com.mycode01.servlet.HelloHttpServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>