1:servlet 接口:
1.1 servlet 主要方法: init() --ServletConfig
servlet 实例化后,容器会先调用init 方法,用于初始化该对象,即在处理客户需求前完成一些初始化工作。每一个 servlet实例,init 只能被调用一次,相当于java的static 块。
servlet 使用 ServletConfig 对象 从web 应用程序的配置信息中获取以 键值对形式提供的初始化参数。
servlet 使用 ServletConfig 对象获取 ServletContext 对象,使用 ServletContext 可以使 servlet 和servlet 容器进行通信。
service() --ServeltRequest ServletResponse
容器调用service() 方法来处理客户的请求,注意: 在service 方法前必须确保 init 方法正确完成。
容器会构造一个请求对象 ServletRequest 和 响应对象 ServletResponse 作为参数传递给service ().
destroy(): 当容器检测到servlet 对象从服务中移除的时候会调用该方法。释放资源 给GC
getServletConfig() 返回servletconfig 对象
getServletInfo() 返回一个字符串,包含servlet 信息
1.2 servlet 创建的三种方法一是实现Servlet接口,二是继承抽象类GenericServlet,三是继承HttpServlet类。
1: 实现servlet 接口 必须实现servlet 五个方法
- public class TestServlet implements Servlet{
- public void init(ServletConfig config) throws ServletException{
- System.out.println("init");
- }
- public ServletConfig getServletConfig(){
- reture null;
- }
- public void service(ServletRequest req,ServletResponse res)
- throws ServletException,IOException{
- //这里可以实现请求后要做的内容
- PrintWriter out = response.getWriter();
- out.println("Hello World!");
- }
- public String getServletInfo(){
- return null;
- }
- public void destroy(){
- System.out.println("destory");
- }
- }
2: 继承抽象类 GenericServletGenericServlet类中只有一个抽象方法,即service(ServletRequest req, ServletResponse res)
- public TestGenericServlet extends GenericServlet{
- public abstract void service(ServletRequest req,ServletResponse res)
- throws ServletException,IOException{
- PrintWriter out = response.getWriter();
- out.println("Hello World!");
- }
- }
3: 继承HttpServlet类编写Servlet应该是最容易的,而且是最常见的,我们一般要编写Servlet直接继承这个类就行了,重写doGet()或者doPost()方法即可
- public TestHttpServlet extends HttpServlet{
- public void doGet(HttpServletRequest request, HttpServletResponse response)
- throws IOException, ServletException {
- response.setContentType("text/html;charset=gb2312");
- PrintWriter out = response.getWriter();
- out.println("<html>");
- out.println("<head>");
- out.println("<title>HelloWorld</title>");
- out.println("</head>");
- out.println("<body bgcolor=\"white\">");
- out.println("<hr>");
- out.println("HelloWorld");
- out.println("</body></html>");
- }
- public void doPost(HttpServletRequest request, HttpServletResponse response)
- throws IOException, ServletException {
- doGet(request, response);
- }
- }
在servlet 中,主要接口与类方法如下:
1.3: HttpServlet 详解
1:七种请求方法 doGet doPost doHead doPut doDelete doTrace doOptions 参数都是 HttpRequest HttpResponse
2:servlet 执行顺序: 当容器收到一个servlet 请求后, 先调用 公共的 service() 方法 在调用service 时候,首先将参数转换为 HttpRequest 和 HttpResponse
然后调用保护的(protected) service() 方法 将转换后的 HttpRequest 和 HttpResponse 作为参数传递下去
在 protected service 中 ,首先调用 HttpRequest 的 getMethod() 获取 HTTP 请求方法的名字,然后根据类型调用相应的 doXXX() 方法
1.4: HttpServletRequest,HttpServletResponse 详解
HttpServletRequest: String getContextPath() 返回请求URL 中表示上下文的部分 如: /simple/test 返回 /simple
Cookie[] getCookies() 返回本次请求所有的Cookie 对象
String getHeader() 返回请求报头的值
String getMethod() 返回本次请求使用的HTTP 方法的名字 如 post get Trace 等
String getPathInfo() 返回额外 url 信息 如 /simple/test 返回 test
String getPathTranslated() 返回真实路径信息 如
String getQueryString() 返回请求的action 名称 如 返回 logon
HttpSession getSession() 返回和此次请求相关联的session
HttpServletResponse: void addCookie(Cookie cookie) 新增一个cookie 到响应中去,可以设置多个。
addheader(String name,String value) 用给出的name and value 增加一个响应报头到响应中去
sendRedirect(String location) 发送一个临时的重定向到客户端 让客户端访问的url重新定位
1.5: ServletConfig
servlet容器使用ServletConfig 对象在容器初始化的时候向其传递配置信息
四种主要方法: String getInitParemeter(String name) 返回名字为 name 的初始化参数的值,初始化参数在配置文件 web.xml 中配置
Enumeration getInitParameterNames() 返回servlet 所有初始化参数名字的枚举集合
ServletContext getServletContext() 返回servlet 上下文对象的引用
String getServletName() 返回 当前servlet 的名称
2:servlet 配置
1:web.xml: web 应用程序的配置和部署都是通过web.xml 来完成的,其包含以下信息:
ServletContext 的初始化参数
Session 的配置
Servlet/Jsp 的定义
Servlet/jsp 的
MIME 类型映射
欢迎文件列表
错误页面
安全
地区和编码映射
JSP 配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<display-name></display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<servlet>
<!-- Servlet名字,可以随便取,有多个Servlet时不允许重名 -->
<servlet-name>SimpleServlet</servlet-name>
<!-- 指定实现这个Servlet的类 完整的包名+类名 -->
<servlet-class>com.tide.servlet.SimpleServlet</servlet-class>
</servlet>
<servlet-mapping>
<!-- –必须和<servlet>里的<servlet-name>内容一样 -->
<servlet-name>SimpleServlet</servlet-name>
<!-- 指定访问这个Servlet的URL。这里给出的是对于整个Web应用的相对URL路径 这里是浏览器访问时的地址 http://localhost:8080/Myservlet/hello 即可访问-->
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
对于servlet 上的页面 有两种访问方式: 一种是在web.xml 中的 url-pattern 中直接访问地址 另一种是访问jsp 对象
3:servlet 例子
3.1 简单表单模拟
web.xml 部署为<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <display-name></display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <servlet> <servlet-name>loginjsp</servlet-name> <servlet-class>com.tide.servlet.Login</servlet-class> </servlet> <servlet-mapping> <servlet-name>loginjsp</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping> </web-app>login.jsp 测试登陆<%@ page language="java" import="java.util.*" pageEncoding="UTF-8" contentType="text/html; charset=UTF-8"%> <% request.setCharacterEncoding("UTF-8"); %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>用户注册</title> </head> <body> <center> <h2>用户注册</h2> <hr> <form action="login" method="post"> 用户姓名:<input type="text" name="username" style="width:138px" /> <br> 用户密码:<input type="password" name="password" style="width:138px" /> <br> <br> <input type="submit" value="确定"/> <input type="reset" value="取消"/> </form> </center> </body> </html>
package com.tide.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Login extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
String name = req.getParameter("username");
System.out.println("获取的名称为-- " + name);
PrintWriter pw;
pw = res.getWriter();
/*pw.print("<html><head><title>");
pw.print("Welcome");
pw.print("<title><head>");
pw.print("<body>");
pw.print("hello " + name);
pw.print("</body></html>");*/
pw.print("hello "+name);
pw.close();
}
public void doPost(HttpServletRequest req, HttpServletResponse res)throws ServletException, IOException{
String username = req.getParameter("username");
String password = req.getParameter("password");
res.setContentType("text/html");
PrintWriter out = res.getWriter();
out.println("<html><head><title>Login Result</title></head>");
out.println("<body> username: " + username + "<br>");
out.println("password: " + password + "</body></html>");
out.flush();
}
}
--------------------------------------------------------------------------------------------------
采用post 模式 可以避免密码的泄露
get与post方法之间的差别:
1.浏览器地址栏呈现的结果不同(表象);2.真正的原因在于向服务器端发送请求时的形式是不同的。 3.get的请求格式: GET /test/LoginServlet?username=hello&password=world HTTP/1.1 4.post的请求格式: POST /test/LoginServlet HTTP/1.1 Connection: Keep-Alive username=hello&password=world 5.通过浏览器进行文件上传时,一定要使用post方式而绝不能使用get方式。 6.通过浏览器地址栏输入网址的方式来访问服务器端资源,全部使用的是get方法请求的。------------------------------------------------------------------------------------------------------
3.2 消息包头输出到客户端
servlet 实现: 报文头部 客户端的运行环境等信息 全部通过 HttpServletRequest 来获取
public class OutputInfo extends HttpServlet {
public void doGet(HttpServletRequest req,HttpServletResponse res) throws ServletException,IOException{
res.setContentType("text/html;charset=gb2312");
PrintWriter pw = res.getWriter();
Enumeration headName = (Enumeration) req.getHeaderNames();
pw.println("<html><head>");
pw.println("<title> Info Page </title>");
pw.println("</head>");
pw.println("<body><center>");
pw.println("<table border=1 align=center>");
pw.println("<caption>Servlet 接收到的Http 消息 包头的信息</caption>");
pw.println("<tr bgcolor=#999999>");
pw.println("<th>消息包头的名字</th>");
pw.println("<th>消息包头的值</th>");
pw.println("</tr>");
while(headName.hasMoreElements()){
String name = (String) headName.nextElement();
String value = req.getHeader(name);
pw.println("<tr>");
pw.println("<td>"+name+"</td>");
pw.println("<td>"+value+"</td>");
pw.println("</tr>");
}
pw.println("</table><p>");
pw.println("<table border=1 align=center>");
pw.println("<caption> 其他访问信息 </caption>");
pw.println("</tr>");
pw.println("<tr>");
pw.println("<td>客户端的IP地址</td>");
pw.println("<td>"+req.getRemoteAddr()+"</td>");
pw.println("</tr>");
pw.println("<tr>");
pw.println("<td>客户端的端口号</td>");
pw.println("<td>"+req.getRemotePort()+"</td>");
pw.println("</tr>");
pw.println("<tr>");
pw.println("<td>服务器端的IP地址</td>");
pw.println("<td>"+req.getLocalAddr()+"</td>");
pw.println("</tr>");
pw.println("<tr>");
pw.println("<td>服务器端的端口号</td>");
pw.println("<td>"+req.getLocalPort()+"</td>");
pw.println("</tr>");
pw.println("</table>");
pw.println("</center></body></html>");
pw.close();
}
}
将 servlet 添加到 web.xml 中 然后在客户端测试结果为 :
消息包头的名字 | 消息包头的值 |
---|---|
host | localhost:8080 |
connection | keep-alive |
cache-control | max-age=0 |
accept | text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 |
user-agent | Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 Safari/537.36 |
accept-encoding | gzip,deflate,sdch |
accept-language | zh-CN,zh;q=0.8 |
客户端的IP地址 | 127.0.0.1 |
客户端的端口号 | 60255 |
服务器端的IP地址 | 127.0.0.1 |
服务器端的端口号 | 8080 |
4:Servlet 生命周期
Servlet 运行期间,其生命周期由容器来统一管理,生命周期通过 Servlet 的 init() destroy() service() 三种方法来表示
主要包含了四个阶段:
1:加载和实例化
在容器启动 或者响应第一个客户请求时,创建servlet 实例。
2:初始化
servlet 实例化后,容器必须调用 servlet 的 init() 方法来初始化这个servlet 对象,目的为了servlet 在处理客户请求前完成一系列的初始化工作,
对于每一个servlet init() 只调用一次, 此时可以使用 ServletConfig 来从web.xml 中获取初始化的参数信息。
3:请求处理
servlet 容器调用servlet 的 service() 方法对请求进行处理,在此之前 init() 方法必须调用成功。Servlet 通过 ServletRequest 来获取客户端的信息和请求,对请求处理后 调用 ServletResponse 来设置响应信息。出现错误的话:该实例永久不可用-404 暂时不可用-503
4:服务终止
容器检测到实例从服务中移除后,调用 destroy()
5:Servlet 上下文
运行在java 虚拟机中的每一个web程序都有一个与之相关的 Servlet 上下文, java servlet API 提供了一个 ServletContext 接口来表示上下文。
一个 ServletContext 对象表示了一个web 程序的上下文, servlet 容器在初始化实例期间,向其传递 ServletConfig 对象, 通过其 getServletContext() 获取上下文。
上下文例子: 统计页面访问量 *************注意这里的上下文必须放在安全锁里
public class CountDemo extends HttpServlet { public void doGet(HttpServletRequest req,HttpServletResponse res) throws ServletException,IOException{ ServletContext context = getServletContext(); //Enumeration<String> count = context.getAttributeNames(); Integer count = null; synchronized(context){ count = (Integer)context.getAttribute("counter"); if(null == count){ count = new Integer(1); }else{ count = new Integer(count.intValue()+1); } context.setAttribute("counter", count); } res.setContentType("text/html;charset=gb2312"); PrintWriter pw = res.getWriter(); pw.println("<html><head>"); pw.println("<title>页面访问统计</title>"); pw.println("</head><body>"); pw.println("该页面已经被访问了 :"+"<b>"+count+"</b>"+" 次"); pw.println("</body></html>"); pw.close(); } }
6:Servlet 请求转发
好处: <1> 提供了统一的访问方式 <2> 控制器(controller)可以将不同的请求发送给不同的servlet 来进行处理 本身不处理请求,缩短了响应时间
6.1 RequestDispacher: servlet 控制器
RequestDispacher 对象由 Servlet 来创建,用于封装一个由路径所标示的服务器资源。 主要有以下两种方法:
forward(ServletRequest req,ServletResponse res) : 该方法用于将 请求从一个servlet 传递给服务器上的 另外的 servlet jsp 页面,或者 HTML 文件
注意: 这个方法必须在响应被提交给客户端之前使用,之后的话会爆出 IllegastateException 错误 在 forward() 调用后原先在响应缓存中的未被提交的内容将自动清除。
include(ServletRequest req,ServletResponse res) 该方法用于响应中包含其他的资源信息(servlet jsp html)等内容。
这两个方法的区别是:include 将请求转发给其他对象后,被调用的servlet 做出的响应将并入原来的响应对象中,原来的servlet 对象还可以继续输出响应数据。
forward 将请求转发给其他对象后,将由被调用的servlet 对象做出响应,原来的servlet 执行将终止。
6.2 RequestDispache 对象的获取三种方法:
<1> ServletRequest.getRequestDispacher(String path) --资源路径名
<2> ServletContext.getNamedDispacher(String name) -- jsp 或 servlet 名称
<3> SerletContext.getRequestDispacher(String path) -- 这里路径必须以 / 开头 相对于当前上下文根的路径 如 /MyServlet
6.3 请求转发的实例
package com.tide.servlet; import java.io.IOException; import javax.servlet.RequestDispatcher; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; public class Login extends HttpServlet { public void doPost(HttpServletRequest req,HttpServletResponse res)throws ServletException,IOException{ res.setContentType("text/html;charset=GB2312"); ServletContext context = getServletContext(); String name = req.getParameter("user"); String pass = req.getParameter("passwd"); System.out.println("获取到的用户名为 :"+name+" --密码为 :"+pass); if(StringUtils.isBlank(name) || StringUtils.isBlank(pass)){ System.out.println("用户名或密码为空"); RequestDispatcher dis = context.getRequestDispatcher("/register.jsp"); dis.forward(req, res); }else{ RequestDispatcher dis = context.getRequestDispatcher("/loginsuccess.jsp"); dis.forward(req, res); } } }
底层分析:
请求转发(RequestDispatcher)的过程:客户首先发送一个请求到服务器端,服务器端发现匹配的servlet,并指定它去执行,当这个servlet执行完之后,它要调用getRequestDispacther()方法,把请求转发给指定的test.jsp,整个流程都是在服务器端完成的,而且是在同一个请求里面完成的,因此servlet和jsp共享的是同一个request,在servlet里面放的所有东西,在jsp中都能取出来,因此,jsp能把结果getAttribute()出来,getAttribute()出来后执行完把结果返回给客户端。整个过程是一个请求,一个响应。重定向(sendRedirect)的工作原理:客户发送一个请求到服务器,服务器匹配servlet,这都和请求转发一样,servlet处理完之后调用了sendRedirect()这个方法,这个方法是response的方法,所以,当这个servlet处理完之后,看到response.senRedirect()方法,立即向客户端返回这个响应,响应行告诉客户端你必须要再发送一个请求,去访问test.jsp,紧接着客户端受到这个请求后,立刻发出一个新的请求,去请求test.jsp,这里两个请求互不干扰,相互独立,在前面request里面setAttribute()的任何东西,在后面的request里面都获得不了。可见,在sendRedirect()里面是两个请求,两个响应。