1、Servlet概述
Servlet(Server Applet)是Java Servlet的简称,是小服务程序或服务连接器,用Java编写的服务器端程序,主要功能在于交互式地浏览和修改数据,生成动态Web内容。
狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。Servlet运行于支持Java的应用服务器中。从原理上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。
2、Servlet生命周期
- 首先当servlet第一次被调用的时候,会调用servlet的
init
方法,把servlet实例装载到内存中。 - 然后会调用
service
方法,当第二次servlet被访问的时候就会直接调用service函数,并创建一个新的线程,故Servlet是单实例多线程。 - 只有当web应用关闭,或者容器关闭,或者电脑关闭时候,才会调用
destroy
方法销毁servlet。
3、简单实例
3.1 编写Servlet代码
package servletdemo;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/Hello")
public class Hello extends HttpServlet {
private static final long serialVersionUID = 1L;
private String message;
public Hello() {
super();
}
public void init() throws ServletException{
message = "Hello World";
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<h1>"+"get:"+message+"</h1>");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<h1>"+"post:"+message+"</h1>");
}
}
3.2 在web.xml文件内注册该Servlet
<servlet>
<servlet-name>Hello</servlet-name>
<servlet-class>servletdemo.Hello</servlet-class> </servlet>
<servlet-mapping>
<servlet-name>Hello</servlet-name>
<url-pattern>/servletdemo/Hello</url-pattern> </servlet-mapping>
其中servlet-name表示Servlet的名字(上下两个须一致),servlet-class要写Servlet类的路径(包名.类名),url-pattern表示用于访问的URL链接,可以自己定义路径(虚拟的),但必须以“/”开头。
注:修改一次web.xml,需重启服务器。
3.3 创建交互界面
<%@ 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>Hello world!</title>
</head>
<body>
<h1>Servlet例子</h1><hr>
<a href="servletdemo/Hello">get方式请求Hello</a><br /><br />
<form action="servletdemo/Hello" method="post">
<input type="submit" value="post方式请求Hello" />
</form>
</body>
</html>
此处链接要和web.xml中的url-pattern一致。
这里在eclipse内创建了jsp文件,但对于这种简单的内容完全可以用html文件代替。
3.4 运行该文件
方案一:在eclipse外面,Tomcat的HTTP端口为8008(server.xml中配置的),在浏览器输入:
http://localhost:8008/users/WebContent/index.jsp
方案二:在eclipse内配置好Tomcat后,用里面的浏览器,此处配置的server.xml文件中HTTP端口为8080,故可以输入:
http://localhost:8080/myweb/index.jsp
,myweb为web项目名称。
界面如下:
- 点击上面的链接:
- 点击下面的链接:
3.5 运行步骤
- 用户点击超链接向Servlet发送请求。
- 服务器在web.xml中的servlet-mapping寻找与该URL地址相对应的Servlet的名字。
- 根据Servlet的名字找到和该Servlet相关的处理类。
- 若该Servlet还不存在,则先装载一个servlet类并创建实例,调用init方法初始化然后再根据请求的方式不同确定是调用doGet还是doPost方法;若存在,则直接根据请求的方式不同确定是调用doGet还是doPost方法。
- 最后通过Response对象返回给客户端浏览器。
4、Servlet加载的几种方式
- Servlet容器启动时自动装载某些Servlet,实现它只需要在web.xml文件中的
<servlet></servlet>
之间添加以下代码:
<load-on-startup>1</load-on-startup>
其中,数字越小表示优先级越高(先产生,同等条件下先销毁)。
2. 客户端首次向某个Servlet发送请求。
3. Servlet类被修改后,Tomcat容器会自动重新装载Servlet。
5、Servlet表单处理
5.1 表单处理提交的类
package servletdemo;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/LoginServlet")
public class LoginServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public LoginServlet() {
super();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().append("Served at: ").append(request.getContextPath());
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter out = response.getWriter();
String userid = request.getParameter("userid");
//获取name="userid"标签的值,没有则为null
String passwd = request.getParameter("passwd");
//获取name="passwd"标签的值,没有则为null
response.setContentType("text/html;charset=GBK");
//回复response对象的编码字符集为GBK
if (passwd.equals("12345")) {
out.println("<HTML>");
out.println("<title>Login Servlet</title>");
out.println("<body>");
out.println("Welcome,"+userid);
out.println("</body>");
out.println("</HTML>");
}else {
out.println("<HTML>");
out.println("<title>Login Servlet</title>");
out.println("<body>");
out.println("Login failure!");
out.println("</body>");
out.println("</HTML>");
}
}
}
5.2 登录界面
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Login page</title>
</head>
<body>
<h2>Login page</h2>
<form action="test" method="post">
<table>
<tr>
<td>Name:</td>
<td><input type="text" name="userid"/></td>
</tr>
<tr>
<td>Password:</td>
<td><input type="password" name="passwd"/></td>
</tr>
</table>
<input type="submit" value="submit">
</form>
</body>
</html>
5.3 web.xml配置文件
<servlet>
<servlet-name>LoginServlet</servlet-name>
<servlet-class>servletdemo.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/test</url-pattern>
</servlet-mapping>
5.4 运行效果
当密码为12345
时,点击submit,在web.xml中查找“/test”对应的Servlet名字,再跟据名字找到对应的类,调用类里面的post方法(第一次还要调用init方法),最后通过Response对象返回给客户端浏览器。
6、数据库操作
6.1 mysql命令行操作
- 建立数据库:
- 建立数据表
- 查看初始状态:
6.2代码准备
1. 访问与添加数据页面
SelectServlet.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Select</title>
<style type="text/css">
table {
font-family: verdana,arial,sans-serif;
font-size:15px;
color:blue;
border-width: 1px;
border-color: purple;
border-collapse: collapse;
}
table td {
border-width: 1px;
padding: 8px;
border-style: dashed;
}
</style>
</head>
<body>
<form action="SelectServlet" method="get">
<table>
<tr>
<td>姓名:</td>
<td><input type="text" name="name"/></td>
</tr>
<tr>
<td>班级:</td>
<td><input type="text" name="grent"/></td>
</tr>
<tr>
<td>成绩:</td>
<td><input type="number" name="result"/></td>
</tr>
<tr>
<td colspan="2" align="center">
<input type="submit" value="submit">
<input type="reset" value="reset">
</td>
</tr>
</table>
</form>
</body>
</html>
2. web.xml配置
注册两个Servlet
<servlet>
<servlet-name>SelectServlet</servlet-name>
<servlet-class>servletdemo.SelectServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>SelectServlet</servlet-name>
<url-pattern>/SelectServlet</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>UpdateServlet</servlet-name>
<servlet-class>servletdemo.Update</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UpdateServlet</servlet-name>
<url-pattern>/UpdateServlet</url-pattern>
</servlet-mapping>
3. css表格修饰
css_table
@CHARSET "UTF-8";
table {
font-family: verdana,arial,sans-serif;
font-size:11px;
color:#333333;
border-width: 1px;
border-color: #666666;
border-collapse: collapse;
}
table th {
border-width: 1px;
padding: 8px;
border-style: solid;
border-color: #666666;
background-color: #dedede;
}
table td {
border-width: 1px;
padding: 8px;
border-style: solid;
border-color: #666666;
background-color: #ffffff;
}
4. 进行处理的类
SelectServlet.java
用于显示数据库中的数据到表格中
package servletdemo;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URL;
import java.net.URLEncoder;
import java.sql.*;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/SelectServlet")
public class SelectServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final String CONTENT_TYPE = "text/html;charset=GBK";
public SelectServlet() {
super();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType(CONTENT_TYPE);
response.setCharacterEncoding("utf-8");
PrintWriter out = response.getWriter();
String name = request.getParameter("name");
String grent = request.getParameter("grent");
String result = request.getParameter("result");
out.println(name+"---"+grent+"---"+result);
try {
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/DB_student?useUnicode=true&characterEncoding=GBK";
String user = "root";
String password = "admin";
Connection con = DriverManager.getConnection(url,user,password);
Statement stat = con.createStatement();
int length=0;
if(name!=null&&grent!=null&&result!=null)
length= stat.executeUpdate("insert into student values('"+name+"','"+grent+"',"+result+")");
if (length!=0) {
out.println("添加成功!");
}else{
out.print("<p>input data not correct!</p>");
}
ResultSet rs = stat.executeQuery("select * from student");
out.println("<html>");
out.println("<head><title>selectservlet</title>");
out.println("<script type=\"text/javascript\" src=\"JS.js\"></script>");
out.println("<link rel=\"stylesheet\" type=\"text/css\" href=\"css_table.css\" /></head>");
out.println("<body bgcolor=\"#ffffff\">");
out.println("<table align='center' border='1'><caption>学生信息</caption><thead><tr><th scope=\"col\">姓名</th><th scope=\"col\">班级</th><th scope=\"col\">成绩</th><th>修改</th><th scope=\"col\">删除</th></tr></thead>");
while(rs.next()){
out.println("<tr><td>"+rs.getString("name")+"</td><td>"
+rs.getString("grent")+"</td><td>"
+rs.getString("result")
+"</td><td><a href=\"JavaScript:change()\">修改</a></td><td><a href=\"UpdateServlet?name="
+URLEncoder.encode(rs.getString("name"),"utf-8")+"&delete=true\">删除</a></td></tr>");
}
out.println("</table>");
out.println("</body>");
out.println("</html>");
} catch (Exception e) {
out.print("<p> error:</p>"+e.getMessage());
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
当用户点击修改时,先产生一个提示框,用于输入修改后的数据,然后在当前页面加载文档,链接指向UpdateServlet,delete参数用于让UpdateServlet识别出是采取修改操作还是删除操作。Js代码如下:
function change() {
var str = prompt("输入格式为'姓名-班级-成绩'");
var words = str.split('-',3);
window.location.assign("UpdateServlet?name="+encodeURI(words[0])+"&result="+encodeURI(words[2])+"&delete=false");
}
UpdateServlet
执行删除和修改操作的类
package servletdemo;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/Update")
public class Update extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final String CONTENT_TYPE = "text/html;charset=GBK";
public Update() {
super();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType(CONTENT_TYPE);
response.setCharacterEncoding("utf-8");
PrintWriter out = response.getWriter();
String name = URLDecoder.decode(request.getParameter("name"),"utf-8");
//将name参数以utf-8编码方式解码为unicode编码
String result = request.getParameter("result");
boolean isdelete = request.getParameter("delete").equals("true")?true:false;
try {
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/DB_student?useUnicode=true&characterEncoding=GBK";
String user = "root";
String password = "admin";
Connection con = DriverManager.getConnection(url,user,password);
Statement stat = con.createStatement();
System.out.println(name);
if(isdelete)
stat.executeUpdate("delete from student where name='"+name+"'");
else
stat.execute("update student set result="+Integer.parseInt(result)+" where name='"+name+"'");
out.print("<p>update success!</p>");
ResultSet rs = stat.executeQuery("select * from student");
out.println("<html>");
out.println("<head><title>selectservlet</title>");
out.println("<meta charset=\"gbk\">");
out.println("<script type=\"text/javascript\" src=\"JS.js\"></script>");
out.println("<link rel=\"stylesheet\" type=\"text/css\" href=\"css_table.css\" /></head>");
out.println("<body bgcolor=\"#ffffff\">");
out.println("<table align='center' border='1'><caption>学生信息</caption><thead><tr><th scope=\"col\">姓名</th><th scope=\"col\">班级</th><th scope=\"col\">成绩</th><th>修改</th><th scope=\"col\">删除</th></tr></thead>");
while(rs.next()){
out.println("<tr><td>"+rs.getString("name")+"</td><td>"
+rs.getString("grent")+"</td><td>"
+rs.getString("result")
+"</td><td><a href=\"JavaScript:change()\">修改</a></td><td><a href=\"UpdateServlet?name="
+URLEncoder.encode(rs.getString("name"),"utf-8")+"&delete=true\">删除</a></td></tr>");
//将rs.getString("name")得到的unicode码编码为utf-8码,中文不能作为url链接的参数值。
}
out.println("</table>");
out.println("</body>");
out.println("</html>");
} catch (Exception e) {
out.print("<p> error:</p>"+e.getMessage());
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
6.3 运行效果
新增数据:‘李四-12-96’
修改:
6.4 遇到的几个问题
1. js提示框的中文显示乱码
原因:response默认用本地计算机编码方式编码中文,而客户端使用浏览器默认的编码方式解码,两者需统一。
解决:response.setCharacterEncoding("utf-8");
或者在<script></script>
内指定回复的JS文档编码格式为charset="utf-8"
此外,response.setCharacterEncoding
和response.setContentType
有区别。
response.setCharacterEncoding
:
在服务器端定义response内数据编码格式,即数据以哪种编码方式存储在response中。
response.setContentType("text/html;charset=GBK")
:
对返回的静态HTML文档设置编码格式,使客户端浏览器能正确显示HTML上的中文。等价于:
response.getOutputStream().write("<meta http-equiv='content-type' content='text/html;charset=GBK'>".getBytes());
前者是设置动态文字(参数,数据库),后者设置页面静态文字
2. decode与encode
首先,上面的HTML文档用<meta charset="UTF-8">
来告诉浏览器是以UTF-8
编码传输的,要用UTF-8
来解码,即初始response
的HTML的编码是UTF-8
。
然后,form
标签有个属性是accept-charset=""
,不写则默认编码方式是UTF-8
,当用户输入姓名等信息点击submit
后,以该编码方式编码进行传输,也就是request
以UTF-8
编码,但是服务器是不知道的。那么服务器使用request.getParameter
如何知道这个编码方式来进行解码呢?
在getParameter()
源码中,有这么一段:
说明服务器会先获得request
的编码,为null
则默认以ISO-8859-1
进行解码。若要使服务器能使用·Content-type
中定义的编码,则要在connector
标签加上useBodyEncodingForURI=true
在上面并没有特意设置accept-charset=""
的值,但服务器仍然正确解码了。那么,现在有两个问题:
1. 为什么ISO-8859-1
可以正确解码UTF-8
编码的request
?
2. 如果设置accept-charset=""
的值为GBK
,服务器那里该怎么解码?
第二个问题,我尝试使用了 request.setCharacterEncoding("gbk");
发现没有用,但将 form
标签的提交方式改为post
,则可以。
总结:request.setCharacterEncoding
是设置从request
中取得的值或从数据库中取出的值。指定后可以通过getParameter()
直接获得正确的字符串,如果不指定,则默认使用iso8859-1
编码。值得注意的是在执行setCharacterEncoding()
之前,不能执行任何getParameter()
。而且,该指定只对POST
方法有效,对GET
方法无效。分析原因,应该是在执行第一个getParameter()
的时候,Java将会按照编码分析所有的提交内容,而后续的getParameter()
不再进行分析,所以setCharacterEncoding()
无效;而对于GET
方法提交表单时,提交的内容在URL中,一开始就已经按照编码分析提交内容,setCharacterEncoding()
自然就无效。
(注:post
提交的form
表单参数采用meta
编码方式)
get需在Tomcat的server.xml中的:
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443"
URIEncoding="GBK" />
加入URIEncoding="GBK"
,解决get
请求乱码问题。