前情回顾
1、Servlet概念及特点
2、Servlet接口及实现类使用
3、Servlet配置
4、Servlet生命周期
今日内容
- ServletConfig
- ServletContext
- web服务器Tomcat
- eclipse+tomcat配置
- JavaEE项目创建与发布
ServletConfig接口
在Servlet运行期间,经常需要一些配置信息,例如,文件使用的编码等,这些信息都可以在web.xml 或 @WebServlet注解的属性中配置。当Tomcat初始化一个Servlet时,会将该Servlet的配置信息封装到一个ServletConfig对象中,每一个servlet都有一个ServletConfig对象。通过调用init(ServletConfig config)方法将ServletConfig对象传递给Servlet。ServletConfig定义了一系列获取配置信息的方法。
<servlet>
<servlet-name>TestServlet02</servlet-name>
<servlet-class>com.gmxy.www.TestServlet02</servlet-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>TestServlet02</servlet-name>
<url-pattern>/TestServlet02</url-pattern>
</servlet-mapping>
//省略其他
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
PrintWriter out = response.getWriter();
//1.获得ServletConfig对象
ServletConfig config = this.getServletConfig();
//2.获取Servlet的名称
String servletName= servletConfig.getServletName();
System.out.println(servletName);
//3.获得 参数名 为encoding 对应的参数值
String param = config.getInitParameter("encoding");
out.println("encoding=" + param);
//4.获取所有初始化参数名称的枚举
Enumeration<String> names = config.getInitParameterNames();
//遍历names
while(names.hasMoreElements()){
//取出每个name
String name = names.nextElement();
//根据key获取value
String value = config.getInitParameter(name);
System.out.println("name:"+name+",value:"+value);
}
//5.获取ServletContext对象
ServletContext servletContext = config.getServletContext();
System.out.println(servletContext);
}
ServletContext接口
当Servlet容器启动时,会为每个Web应用创建一个唯一的ServletContext对象代表当前Web应用(一个Web应用共享这个ServletContext对象),该对象包含了当前Web应用的所有信息,而且实现了多个Servlet之间数据的共享。
常用如下:
1. 获取Web应用程序的全局初始化参数
在web.xml文件中,可以配置Servlet的初始化信息,还可以配置整个Web应用的初始化信息。Web应用初始化参数的配置方式具体如下所示。
<!--省略其他-->
<web-app>
<servlet>
...
</servlet>
<!--全局配置,跟 servlet标签并列-->
<context-param>
<param-name>companyName</param-name>
<param-value>w3c</param-value>
</context-param>
<context-param>
<param-name>address</param-name>
<param-value>beijing</param-value>
</context-param>
<servlet>
...
</servlet>
</web-app>
通过调用ServletContext接口中定义的getInitParameterNames()和getInitParameter(String name)方法,分别获取参数名和参数值。
public class TestServlet04 extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
//获取ServletContext对象
ServletContext context = this.getServletContext();
//得到 所有 初始化参数名的 Enumeration对象
Enumeration<String> paramNames = context.getInitParameterNames();
out.println("all the paramName and paramValue are following:");
while (paramNames.hasMoreElements()) {
String name = paramNames.nextElement(); //获取枚举值
//获取参数值
String value = context.getInitParameter(name);
out.println(name + ":" + value);
out.println("<br />");
}
}
2. 多个Servlet共享数据
一个Web应用中的所有Servlet共享同一个ServletContext对象,所以ServletContext对象的域属性可以被该Web应用中的所有Servlet访问。ServletContext接口中定义了用于增加、删除、设置ServletContext域属性的四个方法。
实例:通过 ServletContext 中的方法,实现2个Servlet之间共享数据。
创建TestServlet05类,调用了ServletContext接口中的方法,设置一个值。
public class TestServlet05 extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)throws ServletException, IOException {
ServletContext context = this.getServletContext();
// 通过setAttribute()方法设置属性值
context.setAttribute("data", "this servlet save data");
}
public void doPost(HttpServletRequest request,
HttpServletResponse response)throws ServletException, IOException {
this.doGet(request, response);
}
}
创建TestServlet06类,在该Servlet类中调用了ServletContext接口中的方法获取属性值。
public class TestServlet06 extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)throws ServletException,IOException{
PrintWriter out = response.getWriter();
ServletContext context = this.getServletContext();
// 通过getAttribute()方法获取属性值
String data = (String)context.getAttribute("data");
out.println(data);
}
public void doPost(HttpServletRequest request,
HttpServletResponse response)throws ServletException,IOException{
this.doGet(request,response);
}
}
3.其他常用方法
//获取ServletContext对象
ServletContext context = getServletContext();
//获取应用的访问虚拟目录
String contextPath = context.getContextPath();
System.out.println(contextPath);
//根据虚拟目录获取应用部署的磁盘绝对路径
//获取a.txt文件的绝对路径
String a = context.getRealPath("/a.txt");
System.out.println(a);
HttpServletResponse对象
状态码方法
当Servlet向客户端回送响应消息时,需要在响应消息中设置状态码,状态码代表着客户端请求服务器的结果。为此,HttpServletResponse接口定义了3个发送状态码的方法。
setStatus(int status)方法
用于设置HTTP响应消息的状态码,并生成响应状态行。由于响应状态行中的状态描述信息直接与状态码相关,而HTTP版本由服务器确定,所以,只要通过setStatus(int status)方法设置了状态码,即可实现状态行的发送。例如,正常情况下,Web服务器会默认产生一个状态码为200的状态行。
sendError(int sc)方法
用于发送表示错误信息的状态码,例如,404状态码表示找不到客户端请求的资源。
sendError(int code, String message)方法
除了设置状态码,还会向客户端发出一条错误信息。服务器默认会创建一个HTML格式的错误服务页面作为响应结果,其中包含参数message指定的文本信息,这个HTML页面的内容类型为“text/html”,保留cookies和其他未修改的响应头信息。如果一个对应于传入的错误码的错误页面已经在web.xml中声明,那么这个声明的错误页面会将优先建议的message参数服务于客户端。
响应消息头相关的方法
设置HTTP响应头字段的方法
实例: 设置消息头,实现定时刷新
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String str = "用户名和密码不匹配,2秒后转向登录页面...";
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.write(str);
//定时刷新,其实就是设置一个响应消息头
response.setHeader("Refresh", "2;URL=/login.html");
//Refresh设置的时间单位是秒,如果刷新到其他地址,需要在时间后面拼接上地址
}
小结
addHeader()、setHeader()、addIntHeader()、setIntHeader()方法都是用于设置各种头字段的,而setContetType()、setLoacale()和setCharacterEncoding()方法用于设置字符编码,这些设置字符编码的方法可以有效解决中文字符乱码问题。
响应消息体相关的方法
在HTTP响应消息中,大量的数据都是通过响应消息体传递的,所以,ServletResponse遵循IO流传递大量数据的设计理念。在发送响应消息体时,定义了两个与输出流相关的方法。
1、getOutputStream()方法
//以二进制 字节流 方式 响应应数据
public class PrintServlet extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)throws ServletException, IOException {
String data = "response data";
// 获取字节输出流对象
OutputStream out = response.getOutputStream();
out.write(data.getBytes());// 输出信息
}
}
2、getWriter()方法
PrintWriter 类中的 getWriter()方法,获取字符输出流对象。由于PrintWriter类型的对象可以直接输出字符文本内容,所以,要想输出内容为字符文本的网页文档,需要调用getWriter()方法。
public class PrintServlet extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)throws ServletException, IOException {
String data = "response data";
// 获取字节输出流对象
OutputStream out = response.getOutputStream();
out.write(data.getBytes());// 输出信息
}
}
请求重定向-sendRedirect()
在某些情况下,针对客户端的请求,一个Servlet类可能无法完成全部工作。这时,可以使用请求重定向来完成。所谓请求重定向,指的是Web服务器接收到客户端的请求后,可能由于某些条件限制,不能访问当前请求URL所指向的Web资源,而是指定了一个新的资源路径,**
sendRedirect()方法用于生成302响应码和Location响应头,从而通知客户端重新访问Location响应头中指定的URL。两次请求,地址栏改变,浏览器行为.
sendRedirect()方法的完整声明如下:
public void sendRedirect(java.lang.String location) throws java.io.IOException
注意:参数location可以使用相对URL,Web服务器会自动将相对URL翻译成绝对URL,再生成Location头字段。
原理:
实例:用户登录,用户名“javaweb”,密码“12345”
//登录:login.html
<!--把表单内容提交到chapter04工程下的LoginServlet-->
<form action="/web/LoginServlet" method="post">
用户名: <input type="text" name="username" /><br />
密 码:<input type="password" name="password"/><br />
<input type="submit" value="登录" />
</form>
//登录处理:LoginServlet.java
public void doGet(HttpServletRequest request,
HttpServletResponse response)throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
// 用HttpServletRequest对象的getParameter()方法获取用户名和密码
String username = request.getParameter("username");
String password = request.getParameter("password");
//比较用户名,密码
if (("javaweb").equals(username) &&("12345").equals(password)) {
//登录成功,到欢迎页
response.sendRedirect("welcome.html");
} else {
response.sendRedirect("login.html");
}
}
//登录成功:welcome.html
<body>
欢迎你,登录成功!
</body>
输出中文乱码问题
由于计算机中的数据都是以二进制形式存储的,所以,当传输文本时,就会发生字符和字节之间的转换。字符与字节之间的转换是通过查码表完成的,将字符转换成字节的过程称为编码,将字节转换成字符的过程称为解码,如果编码和解码使用的码表不一致,就会导致乱码问题。
public void doGet(HttpServletRequest request,
HttpServletResponse response)throws ServletException, IOException {
String data = "中国";
PrintWriter out = response.getWriter();
out.println(data);
}
推荐使用第二种。
public void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
//设置 响应 字符编码,解决 输出中文 乱码问题
response.setContentType("text/html;charset=utf-8");
String data="中国";
PrintWriter out = response.getWriter();
out.println(data);
}
HttpServletRequest对象
对于 HttpServletRequest 对象(以下简称 request 对象),我们并不陌生,在之前的 service、doGet或是 doPost 方法中它都出现过,接下来我们来讲解这个 request 对象。
请求对象,就是在JavaEE工程中,用于发送请求的对象。我们常用的对象就是ServletRequest和HttpServletRequest,它们的区别就是是否和HTTP协议有关。
HTTP 中的请求报文是有很多请求信息的,包括请求头、请求行等。当请求报文到达 Tomcat 服务器后,服务器就要对这些信息进行解析和保存。而 request 对象最主要的作用就是用来保存 HTTP 请求报文里面的信息,开发人员通过这个对象的方法,可以获得客户的一些信息。request 对象是由服务器创建的,在一个请求处理完成之后它所保存的信息也就没有意义了,所以它的生命周期是一个请求内。值得注意的是,服务器每接收到一个请求,就会创建一个 request 对象来保存这些请求信息。
request对象常用方法.
1、获取请求行的相关方法
常用:getRemoteAddr、getMethod、getContextPath、getServletName、getServletPath
举例:请求对象的各种信息获取
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//本机地址:服务器地址
String localAddr = request.getLocalAddr();
//本机名称:服务器名称
String localName = request.getLocalName();
//本机端口:服务器端口
int localPort = request.getLocalPort();
//来访者ip
String remoteAddr = request.getRemoteAddr();
//来访者主机
String remoteHost = request.getRemoteHost();
//来访者端口
int remotePort = request.getRemotePort();
//统一资源标识符
String URI = request.getRequestURI();
//统一资源定位符
String URL = request.getRequestURL().toString();
//获取查询字符串
String queryString = request.getQueryString();
//获取Servlet映射路径
String servletPath = request.getServletPath();
//输出内容
System.out.println("getLocalAddr() is :"+localAddr);
System.out.println("getLocalName() is :"+localName);
System.out.println("getLocalPort() is :"+localPort);
System.out.println("getRemoteAddr() is :"+remoteAddr);
System.out.println("getRemoteHost() is :"+remoteHost);
System.out.println("getRemotePort() is :"+remotePort);
System.out.println("getRequestURI() is :"+URI);
System.out.println("getRequestURL() is :"+URL);
System.out.println("getQueryString() is :"+queryString);
System.out.println("getServletPath() is :"+servletPath);
}
举例:获取请求消息头
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//1.根据名称获取头的值 一个消息头一个值
String value = request.getHeader("Accept-Encoding");
System.out.println("getHeader():"+value);
//2.根据名称获取头的值 一个头多个值
Enumeration<String> values = request.getHeaders("Accept");
while(values.hasMoreElements()){
System.out.println("getHeaders():"+values.nextElement());
}
//3.获取请求消息头的名称的枚举
Enumeration<String> names = request.getHeaderNames();
while(names.hasMoreElements()){
String name = names.nextElement();
String value1 = request.getHeader(name);
System.out.println(name+":"+value1);
}
}
2、获取请求参数
获取客端向服务端请求时候携带的数据。
准备一个表单
<form action="/day/RequestDemo3" method="post">
用户名:<input type="text" name="username" /><br/>
密码:<input type="password" name="password" /><br/>
性别:<input type="radio" name="gender" value="1" checked>男
<input type="radio" name="gender" value="0">女
<br/>
<input type="submit" value="注册" />
</form>
RequestParamsServlet的Servlet类,使用该Servlet获取请求参数。
public void doGet(HttpServletRequest request,
HttpServletResponse response)throws ServletException, IOException {
//单个值
String name = request.getParameter("username");
String password = request.getParameter("password");
System.out.println("用户名:" + name);
System.out.println("密 码:" + password);
// 获取参数名为“hobby”的值,复选框,是一个数组(多个值)
String[] hobbys = request.getParameterValues("hobby");
System.out.print("爱好:");
for (int i = 0; i < hobbys.length; i++) {
System.out.print(hobbys[i] + ", ");
}
}
3、通过Request对象传递数据
Request对象是4大域对象之一,可以通过Request对象来保存数据,在当前请求这个生命周期内都有效,可以用来传递数据。
public void setAttribute(String name,Object o);
setAttribute()方法的参数列表的第一个参数接收的是一个String类型的name,第二个参数接收的是一个Object类型的对象o。在请求域中保存一个数据。
需要注意的是,如果ServletRequest对象中已经存在指定名称的属性,setAttribute()方法将会先删除原来的属性,然后再添加新的属性。如果传递给setAttribute()方法的属性值对象为null,则删除指定名称的属性,这时的效果等同于removeAttribute()方法。
public Object getAttribute(String name); 从ServletRequest对象中返回指定名称的属性对象。
public void removeAttribute(String name); 从ServletRequest对象中删除指定名称的属性
public Enumeration getAttributeNames(); 返回一个包含ServletRequest对象中的所有属性名的Enumeration对象,在此基础上,可以对ServletRequest对象中的所有属性进行遍历处理,从而获取属性名称对应的值。
4、请求参数的中文乱码问题
1. post方式
request.setCharacterEncoding(“GBK”);
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//设置请求时编码
request.setCharacterEncoding("UTF-8");
String username = request.getParameter("username");
//输出到控制台
System.out.println(username);
//输出到浏览器:注意响应的乱码问题已经解决了
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.write(username);
}
2. get方式
GET方式请求的正文是在地址栏中,在Tomcat8.5版本及以后,Tomcat服务器已经帮我们解决了,所以不会有乱码问题了。如果我们使用的不是Tomcat服务器,或者Tomcat的版本是8.5以前,那么GET方式仍然会有乱码问题,解决方式如下:(以下代码了解即可,因为我们现在使用的是Tomcat9.0.27版本)
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/*
* GET方式:正文在地址栏
* username=%D5%C5%C8%FD
* %D5%C5%C8%FD是已经被编过一次码了
*
* 解决办法:
* 1.使用正确的码表对已经编过码的数据进行解码。
* 就是把取出的内容转成一个字节数组,但是要使用正确的码表。(ISO-8859-1)
* 2.再使用正确的码表进行编码
* 把字节数组再转成一个字符串,需要使用正确的码表,是看浏览器当时用的是什么码表
*/
String username = request.getParameter("username");
byte[] by = username.getBytes("ISO-8859-1");
username = new String(by,"UTF-8");
//输出到浏览器:注意响应的乱码问题已经解决了
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.write(username);
}
5、请求转发与包含
请求转发
Servlet之间可以相互跳转,利用Servlet的跳转可以很容易地把一项任务按模块分开,例如,使用一个Servlet实现用户登录,然后跳转到另外一个Servlet实现用户资料修改。
Servlet的跳转要通过RequestDispatcher接口的实例对象实现。HttpServletRequest接口提供了getRequestDispatcher()方法用于获取RequestDispatcher对象,getRequestDispatcher()方法的具体格式如下所示。
实现
1、获取RequestDispatcher对象
RequestDispatcher getRequestDispatcher(String path);
getRequestDispatcher()方法返回封装了某条路径所指定资源的RequestDispatcher对象。其中,参数 path 必须以“/”开头,用于表示当前 Web 应用的根目录。
RequestDispatcher dispatcher = getRequestDispatcher("/abc");
2、调用forward(req,res)方法
dispatcher.forwrad(req,res);
实例:/RequestDemo7 转发到 /RequestDemo8
/**
* 重定向特点:
* 两次请求,浏览器行为,地址栏改变,请求域中的数据会丢失
* 请求转发:
* 一次请求,服务器行为,地址栏不变,请求域中的数据不丢失
*
* 请求域的作用范围:
* 当前请求(一次请求),和当前请求的转发之中
*/
//转发开始
public class RequestDemo6 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//1.拿到请求调度对象
RequestDispatcher rd = request.getRequestDispatcher("/RequestDemo7");//如果是给浏览器看的,/可写可不写。如果是给服务器看的,一般情况下,/都是必须的。
//放入数据到请求域中
request.setAttribute("CityCode", "bj-010");
//2.实现真正的转发操作
rd.forward(request, response);//实现真正的转发操作
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
/**
* 转发的目的地
*/
public class RequestDemo7 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//获取请求域中的数据
String value = (String)request.getAttribute("CityCode");
response.getWriter().write("welcome to request demo 7 "+value);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
请求包含
把两个Servlet的内容合并到一起来响应浏览器,而同学们都知道HTTP协议的特点是一请求,一响应的方式。所以绝对不可能出现有两个Servlet同时响应方式。那么我们就需要用到请求包含,把两个Servlet的响应内容合并输出。
/**
* 请求包含
*
* 它是把两个Servlet的响应内容合并输出。
* 注意:
* 这种包含是动态包含。
*
* 动态包含的特点:
* 各编译各的,只是最后合并输出。
*/
public class RequestDemo8 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.getWriter().write("I am request demo8 ");
//1.拿到请求调度对象
RequestDispatcher rd = request.getRequestDispatcher("/RequestDemo9");
//2.实现包含的操作
rd.include(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
/**
* 被包含者
*/
public class RequestDemo9 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.getWriter().write("include request demo 9 ");
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
练习
使用servlet+html实现学生管理系统,实现添加,列表功能。暂时还没有用到数据库,可以把学生信息保存到一个txt文件中。
参考思路:由HTML中编写表单,Servlet中定义接收请求的方法,最终把表单数据输出到控制台即可。Servlet的配置方式选择基于web.xml的方式。