二、Servlet 4个核心类
# Servlet规范核心类关系图
ServletConfig对象,ServletContext对象、request对象,response对象
# ServletConfig对象
## 获取ServletConfig对象
1)使用初始化方法获得一个ServletConfig对象
2)通过继承父类(GenericServlet)的方法得到一个ServletConfig对象
ServletConfig config = this.getServletConfig();
## ServletConfig对象作用
1)getServletName(); //获取servlet的名称,也就是我们在web.xml中配置的servlet-name
2)getServletContext(); //获取ServletContext对象,该对象的作用看下面讲解
3)getInitParameter(String); //获取在servlet中初始化参数的值。这里注意与全局初始化参数的区分。这个获取的只是在该servlet下的初始化参数:
4)getInitParameterNames(); //获取在Servlet中所有初始化参数的名字,也就是key值,可以通过key值,来找到各个初始化参数的value值。注意返回的是枚举类型:
在Servlet类中:
结果:
注意:在上面我们所分析的源码过程中,我们就知道,其实可以不用先获得ServletConfig,然后在获取其各种参数,可以直接使用其方法,比如上面我们用的ServletConfig().getServletName();
可以直接写成getServletName();而不用在先获取ServletConfig();了,原因就是在GenericServlet中,已经帮我们获取了这些数据,我们只需要直接拿就行。
String value = this.getInitParameter("wolf");
# ServletContext对象
## 获取ServletContext对象
1)getServletContext();
2)getServletConfig().getServletContext();
这两种获取方式的区别就跟上面的解释一样,第一种是直接拿,在GenericServlet中已经帮我们用getServletConfig().getServletContext();拿到了ServletContext。
我们只需要直接获取就行了,第二种就相当于我们自己在获取一遍,两种读是一样的。
## ServletContext对象作用
tomcat为每个web项目都创建一个ServletContext实例,tomcat在启动时创建,服务器关闭时销毁,在一个web项目中共享数据,管理web项目资源,为整个web配置公共信息等,通俗点讲,就是一个web项目,就存在一个ServletContext实例,每个Servlet都可以访问到它。
1)web项目中共享数据
在一定范围内(当前应用),使多个Servlet共享数据
getAttribute(String name)、setAttribute(String name, Object obj)、removeAttribute(String name)
1.1)setAttribute(String name, Object obj) 在web项目范围内存放内容,以便让在web项目中所有的servlet都能访问到
1.2)getAttribute(String name) 通过指定名称获得内容
1.3)removeAttribute(String name) 通过指定名称移除内容
实例:
我们在Servlet_demo_0010中写:
this.getServletContext().setAttribute("bang", "adc");
然后我们在Servlet_demo_0020中看能不能获取到:
结果:得到了adc
2)获取全局配置信息
整个web项目初始化参数 (这个就是全局初始化参数,每个Servlet中都能获取到该初始化值)
2.1)getInitPatameter(String name) //通过指定名称获取初始化值
2.2)getInitParameterNames() //获得枚举类型
web.xml 配置 整个web项目的初始化
结果:
3)获取web项目资源
3.1)获取web项目下指定资源的路径:getServletContext().getRealPath("/WEB-INF/web.xml")
String getRealPath(String path);//根据资源名称得到资源的绝对路径
结果:
应用:
3.2)获取web项目下指定资源的内容,返回的是字节输入流。InputStream getResourceAsStream(java.lang.String path)
结果的一部分:
3.3)getResourcePaths(java.lang.String path) 指定路径下的所有内容。
结果:
3.4)实现servlet的转发
RequestDispatcher getRequestDispatcher(String path) ;//参数表示要跳转到哪去
# HttpServletRequest
## HttpServletRequest概述
HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中,开发人员通过这个对象的方法,可以获得客户这些信息。
request就是将请求文本封装而成的对象,所以通过request能获得请求文本中的所有内容,请求头、请求体、请求行
## 详解HttpServletRequest
2.1、请求行
举例一:Get http://localhost:8080/day09/servlet/req1?username=zs http/1.1
相关方法:
getMethod(); 获得请求方式
getRequestURL(); 返回客户端发出请求时的完整URL。
getRequestURI(); 返回请求行中的资源名部分。
getContextPath(); 当前应用的虚拟目录
getQueryString() ; 返回请求行中的参数部分。
// getMethod(); 获得请求方式 // ***getRequestURL();返回客户端发出请求时的完整URL。 // ***getRequestURI(); 返回请求行中的资源名部分。 // *****getContextPath(); 当前应用的虚拟目录 /day09_01_HttpServletRequest // getQueryString() ; 返回请求行中的参数部分。 System.out.println(request.getMethod());// GET System.out.println(request.getRequestURL()); // http://localhost:8080/day09_01_HttpServletRequest/servlet/demo1 System.out.println(request.getRequestURI()); // /day09_01_HttpServletRequest/servlet/demo1 System.out.println(request.getContextPath()); // /day09_01_HttpServletRequest System.out.println(request.getQueryString()); // username=tom举例二:Get http://localhost:6060/Web_Servlet/ServletTest3?username=faker&password=mid http/1.1
相关方法:
getRequestURI();
getRequestURL();
getProtocol();
getScheme();
getServerName();
getServerPort();
getContextPath();
getServletPath();
getQueryString();
getRemoteAddr();@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1.统一资源标记符 /Web_Servlet/ServletTest3 String uri = req.getRequestURI(); System.out.println(uri); //2.统一资源定位符 http://localhost:6060/Web_Servlet/ServletTest3 StringBuffer url = req.getRequestURL(); System.out.println(url); //3.协议和版本 HTTP/1.1 String potocol = req.getProtocol(); System.out.println(potocol); //4.协议 http String scheme = req.getScheme(); System.out.println(scheme); //5.主机(域名) localhost,如果你使用的是ip地址,就显示ip地址 String serverName = req.getServerName(); System.out.println(serverName); //6.端口 6060(这是我自己修改了的端口,默认是8080) int port = req. getServerPort(); System.out.println(port); //7.发布到tomcat下的项目名称 /Web_Servlet String contextPath = req.getContextPath(); System.out.println(contextPath); //8.servlet路径 /ServletTest3 String servletPath = req.getServletPath(); System.out.println(servletPath); //9.获取所有请求参数,即?之后所有东西。 username=faker&password=mid String queryString = req.getQueryString(); System.out.println(queryString); //10.远程主机的ip地址 0:0:0:0:0:0:0:1 String remoteAddr = req.getRemoteAddr(); System.out.println(remoteAddr); }2.2、请求头
我们可以查看任意一个网页,它都是有请求头的。
相关方法:
String getHeader(String name) 根据头名称得到头信息值
long getDateHeader(java.lang.String name) 获得指定头内容Date
int getIntHeader(java.lang.String name) 获得指定头内容int
Enumeration getHeaderNames() 得到所有头信息name
Enumeration getHeaders(String name) 根据头名称得到相同名称头信息值举例1:Enumeration getHeaderNames() 得到所有头信息name
Enumeration<String> headerNames = req.getHeaderNames(); while(headerNames.hasMoreElements()){ String key = (String)headerNames.nextElement(); String value = req.getHeader(key); System.out.println(key+"="+value); }结果:
举例2:Enumeration getHeaders(String name) 根据头名称得到相同名称头信息值
Enumeration<String> headers = req.getHeaders("accept-language"); while(headers.hasMoreElements()){ String key = (String)headers.nextElement(); String value = req.getHeader(key); System.out.println(key+"==="+value); }结果:
2.3、请求体(请求正文)
1)与表单获取相关的方法
方法:
String getParameter(name) 根据表单中name属性的名,获取value属性的值方法
String[] getParameterValues(String name) 专为复选框取取提供的方法
getParameterNames() 得到表单提交的所有name的方法
Map<String , String[]> getParameterMap() 得到表单提交的所有值的方法 //做框架用,非常实用
getInputStream 以字节流的方式得到所有表单数据举例:
<form action="/day09_01_HttpServletRequest/servlet/demo3" method="get"> 用户名:<input type="text" name="userName"/><br/> 密码:<input type="password" name="pwd"/><br/> 性别:<input type="radio" name="sex" value="男" checked="checked"/>男 <input type="radio" name="sex" value="女"/>女 <br/> 爱好 : <input type="checkbox" name="hobby" value="篮球"/>篮球 <input type="checkbox" name="hobby" value="唱歌"/>唱歌 <input type="checkbox" name="hobby" value="编码"/>编码 <br/> 所在城市: <select name="city"> <option>------请选择------</option> <option value="bj">北京</option> <option value="sh">上海</option> <option value="gz">广州</option> </select> <br/> <input type="submit" value="注册"/> </form>测试1使用方法:
<input type="text" name="username" />
getParameter(name) 根据表单中name属性的名,获取value属性的值方法
getParameterValues(String name)专业为复选框取取提供的方法
//获取表单数据 //根据表单中name属性的名,获取value属性的值方法 String userName = request.getParameter("userName"); String pwd = request.getParameter("pwd"); String sex = request.getParameter("sex"); String[] hobbys = request.getParameterValues("hobby"); String city = request.getParameter("city"); userName = new String(userName.getBytes("iso-8859-1"),"UTF-8"); System.out.println(userName); System.out.println(pwd); System.out.println(sex); for (int i = 0;hobbys!=null && i < hobbys.length; i++) { System.out.print(hobbys[i]+"\t"); } System.out.println(); System.out.println(city);测试2使用方法:
getParameterNames() 得到表单提交的所有name的方法
getParameterValues(String name)专业为复选框取取提供的方法
//获取所有的表单name的名子 Enumeration names = request.getParameterNames(); while(names.hasMoreElements()){ String name = (String) names.nextElement();//得到每一个name名 String[] values = request.getParameterValues(name);//根据name名,得到value值 for (int i = 0;values!=null && i < values.length; i++) { System.out.println(name+"\t"+values[i]);}测试3使用方法:
getParameterMap 到表单提交的所有值的方法 //做框架用,非常实用
try { User u = new User(); System.out.println("封装数据前:"+u); //获取表单数据 Map<String,String[]> map = request.getParameterMap(); for (Map.Entry<String, String[]> m : map.entrySet()) { String name = m.getKey(); String[] value = m.getValue(); //创建一属性描述器 PropertyDescriptor pd = new PropertyDescriptor(name, User.class); //得到setter属性 Method setter = pd.getWriteMethod(); if(value.length==1){ setter.invoke(u, value[0]);//给一个值的变量赋值 }else{ setter.invoke(u, (Object)value);//相关于给复选框赋值 } } System.out.println("封装数据后:"+u); } catch (Exception e) { e.printStackTrace(); }测试4使用方法:
getInputStream 以字节流的方式得到所有表单数据
request.setCharacterEncoding("UTF-8"); //获取表单数据 ServletInputStream sis = request.getInputStream(); int len = 0; byte[] b = new byte[1024]; while((len=sis.read(b))!=-1){ //System.out.println(new String(new String(b,0,len).getBytes(),"utf-8")); System.out.println(new String(b, 0, len, "UTF-8")); } sis.close();2)与操作非表单数据相关的方法(request也是一个域对象)
方法:
void setAttribute(String name, Object value);
Object getAttribute(String name);
Void removeAttribute(String name);3)与请求转发相关的方法
方法:
//得到请求转发或请求包含的协助对象
RequestDispatcher getRequestDispatcher(String path)
forward(ServletRequest request, ServletResponse response) //转发的方法
include(ServletRequest request, ServletResponse response) //请求包含注意:
include()方法的处理流程:
一是:如果目标组件为Servlet或JSP,就执行它们,并把它们产生的响应正文添加到源组件的响应结果中;
如果目标组件为HTML文档,就直接把文档的内容添加到源组件的响应结果中。
二是:返回到源组件的服务方法中,继续执行后续代码块。
特点:
一是:源组件与被包含的目标组件的输出数据都会被添加到响应结果中。
二是:在目标组件中对响应状态代码或者响应头所做的修改都会被忽略。使用:
//path:转发后跳转的页面,这里不管用不用"/"开头,都是以web项目根开始,因为这是请求转发,请求转发只局限与在同一个web项目下使用,所以这里一直都是从web项目根下开始的。
request.getRequestDispatcher(String path).forward(request,response);
web项目根:
开发:G:\Workspaces\test01\WebRoot\..
运行时:D:\java\tomcat\apache-tomcat-7.0.53\webapps\test01\..
web站点根:
运行时:D:\java\tomcat\apache-tomcat-7.0.53\webapps\..
从这里可以看出,web项目根就是从该web项目名开始,所以我们请求转发时,只需要接着项目名后面需要访问的路径写就行了。
特点:浏览器中url不会改变,也就是浏览器不知道服务器做了什么,是服务器帮我们跳转页面的,并且在转发后的页面,能够继续使用原先的request,因为是原先的request,所以request域中的属性都可以继续获取到。
4)与编码相关的方法
//解决post方式编码
request.setCharacterEncoding("UTF-8"); //告诉服务器客户端什么编码,只能处理post请求方式
//解决get方式编码
String name = new String(name.getBytes(“iso-8859-1”),”UTF-8”);
## reqeust的常见应用
3.1、各种表单输入项数据的获取
text、password、radio、checkbox、file、select、textarea、 hidden
image、button给js编程用
请求参数的中文乱码问题
浏览器是什么编码就以什么编码传送数据
解决:request.setCharacterEncoding(“UTF-8”);//POST有效
new String(username.getBytes(“ISO-8859-1”),“UTF-8”);//GET方式
3.2、request对象实现请求转发
请求转发指一个web资源收到客户端请求后,通知服务器去调用另外一个web资源进行处理。
request对象提供了一个getRequestDispatcher方法,该方法返回一个RequestDispatcher对象,调用这个对象的forward方法可以实现请求转发。
request对象同时也是一个域对象,开发人员通过request对象在实现转发时,把数据通过request对象带给其它web资源处理。
# HttpServletResponse概述
Web服务器收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的request对象、和代表响应的response对象。
request和response对象即然代表请求和响应,那我们要获取客户机提交过来的数据,只需要找request对象就行了。要向容器输出数据,只需要找response对象就行了。
HttpServletResponse对象代表服务器的响应。这个对象中封装了向客户端发送数据、发送响应头,发送响应状态码的方法。
HttpServletResponse细节
getOutputStream和getWriter方法分别用于得到输出二进制数据、输出文本数据的ServletOuputStream、Printwriter对象。
getOutputStream和getWriter这两个方法互相排斥,调用了其中的任何一个方法后,就不能再调用另一方法。 会抛异常。
Servlet程序向ServletOutputStream或PrintWriter对象中写入的数据将被Servlet引擎从response里面获取,Servlet引擎将这些数据当作响应消息的正文,
然后再与响应状态行和各响应头组合后输出到客户端。Serlvet的service方法结束后,Servlet引擎将检查getWriter或getOutputStream方法返回的输出流对象是否已经调用过close方法,
如果没有,Servlet引擎将调用close方法关闭该输出流对象。
## 详解HttpServletResponse
5.1、响应行
响应状态行 HTTP/1.1 200 OK
常见的响应状态码:
200 OK
一切正常,对GET和POST请求的应答文档跟在后面
3XX 用于已经移动的文件并且充被包含在定位头信息中指定新的地址消息
302 Found
类似于301,但新的URL应该被视为临时性的替代,而不是永久性的。
4XX 用于指出客户端的错误
404 Not Found 无法找到指定位置的资源
5XX 用于支持服务器错误
500 Internal Server Error 服务器遇到了意料不到的情况,不能完成客户的请求
方法:
setStatus(int sc) 设置响应状态码
5.2、响应头
常见的响应头
Accept:指示HTTP响应可以接收的文档类型集 Accept-Charset:告知客户可以接收的字符集 Accept-Encoding:所有响应的字符编码集 Content-Type:响应体的MIME类型 Content-Language:响应体的语言类型 Content-Length:响应体的长度和字节数 Expires:通知客户端过期时间,防止客户浏览器使用本地缓存副本 Cookie:包含保存到客户端的Cookie集 Redirect:提供指定重定向,可以不向浏览器输出响应内容,而是直接重新请求到另一个URL地址,实现重定向响应,即是一种自动定向。
方法:
setHeader(String name, String value) 设置响应头信息
举例:
response.setHeader(java.lang.String name, java.lang.String value) 设置指定的头,一般常用。
//告知浏览器使用什么码表 response.setHeader("content-type", "text/html;charset=UTF-8"); 代替了: //告诉服务器应用使用UTF-8解析文本 response.setCharacterEncoding("UTF-8"); //告诉客户端要使用什么编码 response.setHeader("content-type", "text/html;charset=UTF-8"); //告知客户端不缓存 response.setHeader("pragma", "no-cache"); response.setHeader("cache-control", "no-cache"); response.setDateHeader("expires", 0); //设置每隔3秒就自动刷新一次 response.setHeader("Refresh",3);5.3、响应正文
常用方法:
getWrite(); 字符输出流 getOutputStream(); 字节输出流 setCharacterEncoding(String charset) 告知服务器使用什么编码 setContentType(String type)
5.4、重定向
1)重定向运行原理
重定向机制的运作流程
第一步:用户在浏览器端输入特定URL,请求访问服务器端的某个组件
第二步:服务器端的组件返回一个状态码为302的响应结果。
第三步:当浏览器端接收到这种响应结果后,再立即自动请求访问另一个web组件
第四步:浏览器端接收到来自另一个web组件的响应结果。
HttpServeltResponse的sendRedirect(String location)用于重定向2)具体实现
方式一:手动方案
response.setStatus(302); //状态码302就代表重定向
response.setHeader("location","http://www.baidu.com");
方式二:使用封装好的,通过response.sendRedirect("http://www.baidu.com");
特点:服务器告诉浏览器要跳转的页面,是浏览器主动去跳转的页面,浏览器知道,也浏览器的地址栏中url会变,是浏览器重新发起一个请求到另外一个页面,所以request是重新发起的,跟请求转发不一样。
注意:response.sendRedirect(path); //
第一种:response.sendRedirect("/test01/MyServlet01"); //使用了"/"开头,说明是从web站点根开始,所以需要写test01/MyServlet01
第二种:response.sendRedirect("MyServlet01"); //没有使用"/"开头,说明是从web项目根开始,那么就无需写test01了。
注意:重定向没有任何局限,可以重定向web项目内的任何路径,也可以访问别的web项目中的路径,并且这里就用"/"区分开来,如果使用了"/"开头,就说明我要重新开始定位了,不访问刚才的web项目,自己写项目名,
如果没有使用"/"开始,那么就知道是访问刚才那个web项目下的servlet,就可以省略项目名了。就是这样来区别。
## Response常见应用
6.1、向客户端输出中文数据
1)用OutputStream(字节流)发送数据:
response.getOutputStream().write(“中国”.getBytes());//以默认编码发送数据
response.getOutputStream().write("中国".getBytes("UTF-8"));//以UTF-8编码发送数据,浏览器(默认用GB2312)会出现乱码
解决办法:
通过更改浏览器的编码方式:IE/”查看”/”编码”/”UTF-8”(不可取)
通过设置响应头告知客户端编码方式:response.setHeader(“Content-type”, “text/html;charset=UTF-8”);//告知浏览器数据类型及编码
通过meta标签模拟请求头:out.write("<meta http-equiv='Content-Type' content='text/html; charset=utf-8' />".getBytes());
通过以下方法:response.setContentType("text/html;charset=UTF-8");
2)用PrintWriter(字符流)发送数据:
示例:response.getWriter().write(“中国” );有没有乱码?
原因:以默认编码发送数据 ISO-8859-1(没有中国二字编码),此时会发生乱码
解决办法:
setCharacterEncoding(“UTF-8”);
//更改编码为UTF-8
response.setHead(“Context-type”,”text/html;charset=UTF-8”);
//告诉客户端编码方式
注意:不要忘记告诉客户端的编码方式。
由于经常改动编码,response提供了一种更简单的方式
response. setContentType(“text/html;charset=UTF-8”); 其作用相当于以上两条代码。
6.2、文件下载
//通过路径得到一个输入流 String path = this.getServletContext().getRealPath("/WEB-INF/classes/美女.jpg"); FileInputStream fis = new FileInputStream(path); //创建字节输出流 ServletOutputStream sos = response.getOutputStream(); //得到要下载的文件名 String filename = path.substring(path.lastIndexOf("\\")+1); //设置文件名的编码 filename = URLEncoder.encode(filename, "UTF-8");//将不安全的文件名改为UTF-8格式 //告知客户端要下载文件 response.setHeader("content-disposition", "attachment;filename="+filename); response.setHeader("content-type", "image/jpeg"); //执行输出操作 int len = 1; byte[] b = new byte[1024]; while((len=fis.read(b))!=-1){ sos.write(b,0,len); } sos.close(); fis.close();6.3、生成验证码
之后我会详细介绍
6.4、定时刷新页面
利用Refresh刷新到其他组件。模拟注册页面,注册处理交由一个Servlet,在Servlet处理完成后,显示注册成功并转向主页。(meta也可实现)