servlet
什么是Servlet?
定义
Servlet的全称是 Server Applet,顾名思义,就是用 Java 编写的服务器端程序。
Servlet 是一个 Java Web开发标准,狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。
功能
其主要功能在于:交互式地浏览和修改数据,生成动态Web内容。
Java Web 应用程序中所有的请求-响应都是由 Servlet 来完成的。Servlet 并没有
Java Web 开发的发展历程
Web 开发技术主要是从静态网页技术到动态网页技术的变迁。
由于本文主要介绍 Java Web 开发技术,所以对其他语言的 Web 技术不做介绍。
Java Web 开发的大致发展历程如下:静态HTML –> CGI –> Servlet –> JSP
至于Spring、Struts等著名的框架则是在这些技术基础上的最佳编程实践。
静态网页技术
早期的Web 开发只能提供静态的 HTML 页面。这样的模式显然存在很多弊端:不利于系统扩展,不利于和用户之间进行交互。于是,有了动态页面技术(如大家熟悉的JSP、ASP、PHP等等)。
动态网页技术
CGI
CGI (Common Gateway Interface,公共网关接口)是最重要的 Web 技术之一。它是最早的动态页面技术。
CGI 是外部应用程序与 Web 服务器之间的接口标准。
绝大多数的CGI程序被用来解释处理来自表单的输入信息: CGI 允许服务器调用外部程序来处理输入信息,并将相应的输出反馈给浏览器。CGI程序使网页具有交互功能。
注:最流行的CGI 语言是 Perl 和Shell 脚本,但是也可以使用 C、C++ 以及Java 等语言来编写。
CGI 解决了静态页面不利于交互的问题,但其自身也存在缺陷:
l 需要为每个请求启动一个操作 CGI 程序的系统进程。如果请求频繁,会带来很大的系统开销。
如果用Java编写 CGI,除了需要为每个请求启动一个系统进程外,还要在进程中启动一个 JVM ,这将十分低效。
l 需要重复编写处理网络协议的代码,非常耗时。
Servlet
知道了 Java 编写 CGI 的不足。我们不禁要问,如果有办法可以只运行一个系统进程和一个 JVM ,岂不是能大大减少开销吗?
Servlet 正是为此应运而生。
与传统的 CGI 技术相比,Servlet的优势在于:
l 传统的 CGI 中,每个请求都要启动一个新的进程;而在 Servlet 中,每个请求由一个轻量级的 Java 线程处理。
l 传统的 CGI 中,如果有 N 个并发的对同一个 CGI程序的请求,则该CGI程序的代码在内存中重复装载了 N 次;而对于Servlet,处理请求的是 N 个线程,只需要一份 Servlet 类代码。
Servlet运行于支持Java的应用服务器中。从原理上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。
JSP
JSP(Java Server Page)是一种实现静态HTML 和动态 HTML 混合编码的技术,它是Servlet API 的一个扩展。
Servlet 生命周期
Servlet 生命周期可被定义为从创建直到毁灭的整个过程。以下是 Servlet 遵循的过程:
Servlet 通过调用 init () 方法进行初始化。
Servlet 调用 service() 方法来处理客户端的请求。
Servlet 通过调用 destroy() 方法终止(结束)。
最后,Servlet 是由 JVM 的垃圾回收器进行垃圾回收的。
如下图:
执行流程为:
第一个到达服务器的 HTTP 请求被委派到 Servlet 容器。
Servlet 容器在调用 service() 方法之前加载 Servlet。
然后 Servlet 容器处理由多个线程产生的多个请求(GET、POST 等类型),每个线程执行一个单一的 Servlet 实例的 service() 方法。service() 会根据请求类型分发到对应的方法中去处理。
init () 方法
init 方法被设计成只调用一次。它在第一次创建 Servlet 时被调用,在后续每次用户请求时不再调用。因此,它是用于一次性初始化,就像 Applet 的 init 方法一样。
Servlet 创建于用户第一次调用对应于该 Servlet 的 URL 时,但是您也可以指定 Servlet 在服务器第一次启动时被加载。
当用户调用一个 Servlet 时,就会创建一个 Servlet 实例,每一个用户请求都会产生一个新的线程,适当的时候移交给 doGet 或 doPost 方法。init() 方法简单地创建或加载一些数据,这些数据将被用于 Servlet 的整个生命周期。
service() 方法
service() 方法遵循 HTTP 协议标准,负责将 HTTP 请求分发给对应类型的处理方法。
Servlet 容器(即 Web 服务器)调用 service() 方法来处理来自客户端(浏览器)的请求,并把格式化的响应写回给客户端。
每次服务器接收到一个 Servlet 请求时,服务器会产生一个新的线程并调用服务。service() 方法检查 HTTP 请求类型(GET、POST、PUT、DELETE 等),并在适当的时候调用 doGet、doPost、doPut,doDelete 等方法。
注意:service 方法不需要覆写。
doGet()方法
GET请求来自于一个 URL 的正常请求,或者来自于一个未指定 METHOD 的 HTML 表单,它由 doGet() 方法处理。
doPost()方法
POST请求来自于一个特别指定了 METHOD 为 POST 的 HTML 表单,它由 doPost() 方法处理。
destroy() 方法
destroy() 方法只会被调用一次,在 Servlet 生命周期结束时被调用。destroy() 方法可以让您的 Servlet 关闭数据库连接、停止后台线程、把 Cookie 列表或点击计数器写入到磁盘,并执行其他类似的清理活动。
在调用 destroy() 方法之后,servlet 对象被标记为垃圾回收。
httpservlet的doGet doPost方法实例
RequestMethodServlet.java
package com.songdanlee.firstapp.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 RequestMethodServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// TODO Auto-generated method stub
PrintWriter out = response.getWriter();
out.write("this is doGet Method");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// TODO Auto-generated method stub
PrintWriter out = response.getWriter();
out.write("this is doPost Method");
}
}
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
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_2_5.xsd">
<servlet>
<servlet-name>RequestMethodServlet</servlet-name>
<servlet-class>com.songdanlee.firstapp.servlet.RequestMethodServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>RequestMethodServlet</servlet-name>
<url-pattern>/RequestMethodServlet</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>ServletNumber</servlet-name>
<servlet-class>com.songdanlee.firstapp.servlet.ServletNumber</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ServletNumber</servlet-name>
<url-pattern>/ServletNumber</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
form.jsp
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>my first servlet demo</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
<form action="/ServletDemo/RequestMethodServlet" >
姓名<input type="text" name="name"/></br>
密码<input type="text" name="pwd"/></br>
<input type="submit" value="提交"/>
</form>
</body>
</html>
结构图
运行结果
servlet实现网页访问统计
ServletNumber.java
package com.songdanlee.firstapp.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ServletNumber extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// TODO Auto-generated method stub
ServletContext context = this.getServletContext();
Integer count = (Integer) context.getAttribute("counter");
if(count == null){
count = 1;
context.setAttribute("counter", count);
}else {
count++;
context.setAttribute("counter", count);
}
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
int num = Integer.parseInt(context.getAttribute("counter").toString());
out.println("你访问的次数是"+num);
out.flush();
out.close();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// TODO Auto-generated method stub
super.doPost(req, resp);
}
}
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
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_2_5.xsd">
<servlet>
<servlet-name>RequestMethodServlet</servlet-name>
<servlet-class>com.songdanlee.firstapp.servlet.RequestMethodServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>RequestMethodServlet</servlet-name>
<url-pattern>/RequestMethodServlet</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>ServletNumber</servlet-name>
<servlet-class>com.songdanlee.firstapp.servlet.ServletNumber</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ServletNumber</servlet-name>
<url-pattern>/ServletNumber</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
运行结果
中文乱码问题以及网页定时刷新
乱码问题
//第一种方式
reponse.setCharacterEncoding("utf-8");
reponse.setHeader("Context-Type", "text/html;charset=utf-8");
//第二种
reponse.setContentType("text/html;charset=utf-8");
定时刷新并跳转
reponse.setHeader("Refresh","2;URL=https://www.baidu.com");
package com.songdanlee.firstapp.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 ChineseServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse reponse)
throws ServletException, IOException {
/*//第一种方式
reponse.setCharacterEncoding("utf-8");
reponse.setHeader("Context-Type", "text/html;charset=utf-8");*/
//设置编码
reponse.setContentType("text/html;charset=utf-8");
//2秒后刷新并跳转到百度首页
reponse.setHeader("Refresh","2;URL=https://www.baidu.com");
String data = "中国啊";
PrintWriter out = reponse.getWriter();
out.println(data);
out.flush();
out.close();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// TODO Auto-generated method stub
super.doPost(req, resp);
}
}
请求重定向
所谓冲定向,就是web服务器接收到客户端的请求后,可能由于某些限制条件,不能访问当前请求URL所指的web资源,而是指定了一个新的资源路径,让客户端重新发送客户端请求。
1.请求Servlet1
2.Servlet1通知浏览器重定向到Servlet2
3.请求Servlet2
4.Servlet2回送响应消息
实例如下
login.jsp
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Insert title here</title>
</head>
<body>
<!-- 把表单内容提交到LoginServlet -->
<form action="/ServletDemo/LoginServlet" method="post">
用户名<input type="text" name="username"/> </br>
密码 <input type="password" name="password"/> </br>
<input type="submit" name="登录"/>
</form>
</body>
</html>
welcome.jsp
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Insert title here</title>
</head>
<body>
登录成功,欢迎
</body>
</html>
LoginServlet.java
package com.songdanlee.firstapp.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 LoginServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse reponse)
throws ServletException, IOException {
reponse.setContentType("text/html;charset=utf-8");
//用httpServletRequest对象的getparameter()方法来获取用户名和密码
String username = request.getParameter("username");
String password = request.getParameter("password");
if(("root").equals(username)&&("1234").equals(password)){
//如果用户名正确,重定向到welcom.xml
reponse.sendRedirect("/ServletDemo/welcome.jsp");
}else {
//如果用户名错误,重定向到login.xml
//reponse.sendRedirect("/ServletDemo/login.jsp");
PrintWriter out = reponse.getWriter();
out.write("登录失败");
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(req, resp);
}
}
xml配置
<servlet>
<servlet-name>LoginServlet</servlet-name>
<servlet-class>com.songdanlee.firstapp.servlet.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/LoginServlet</url-pattern>
</servlet-mapping>
推荐阅读