前情回顾
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();//遍历nameswhile(names.hasMoreElements()){//取出每个nameString name = names.nextElement();//根据key获取valueString 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.javapublic 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();//来访者ipString 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的方式。
