一、Servlet概述
1) java web目录结构
2)Tomcat 目录结构
3)Servlet 接口
Servlet 是一种实现了 javax.servlet.Servlet 接口的类。
Servlet 接口规定了特定的方法来处理特定的请求,开发者只需要实现 Servlet的相关方法,用户访问
Web 程序的时候,Tomcat会调用这些方法完成业务处理
二、编写及部署Servlet
【具体开发方式见:4、开发 Servlet 的方式 ,
下面的具体代码可在阅读完后面的内容后再回头来看】
1)简介
直接实现 Servlet 接口来编写 Servlet 很不方便,需要实现的方法太多。
在 JDK中 javax.servlet.*,javax.servlet.http.*包下提供了对 Servlet 的支持。
如 javax.servlet.http.HttpServlet 类已经实现了 Servlet 接口的所有方法。
编写 Servlet时直接继承 HttpServlet,并覆盖需要的方法即可,一般只覆盖 doGet()与 doPost()方法
2)实现Servlet
在 MyEclipse中新建 Web Project,取名servlet,新建 Servlet,取名 FirstServlet。
FirstServlet.java 文件:
package 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 FirstServlet extends HttpServlet {
private static final long serialVersionUID = 2386052823761867369L;
/**
* 以 GET 方式访问页面时执行该函数。
* 执行 doGet 前会先执行 getLastModified, 如果浏览器发现 getLastModified 返回的数值
* 与上次访问时返回的数值相同,则认为该文档没有更新,浏览器采用缓存而不执行 doGet。
* 如果 getLastModified 返回 -1,则认为是时刻更新的,总是执行该函数。
*/
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 调用 HttpServlet 自带的日志函数输出信息到控制台
this.log("执行 doGet 方法... ");
// 处理 doGet
this.execute(request, response);
}
/**
* 以 POST 方式访问页面时执行该函数。
*/
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.log("执行 doPost 方法... ");
// 处理 doPost
this.execute(request, response);
}
/**
* 返回该 Servlet 生成的文档的更新时间。对 Get 方式访问有效。
* 返回的时间为相对于 1970年1月1日08:00:00 的毫秒数。
* 如果为 -1 则认为是实时更新。默认为 -1。
*/
@Override
public long getLastModified(HttpServletRequest request) {
this.log("执行 getLastModified 方法... ");
return 0;
}
private void execute(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException{
response.setCharacterEncoding("UTF-8");//设置 request,response 编码方式
request.setCharacterEncoding("UTF-8");
// 访问该 Servlet 的 URI
String requestURI = request.getRequestURI();
// 访问该 Servlet 的方式,是 GET 还是 POST
String method = request.getMethod();
// 客户端提交的参数 param 值
String param = request.getParameter("param");
response.setContentType("text/html"); //设计文档类型
PrintWriter out = response.getWriter(); //获取 out 对象
//输出到客户端浏览器
out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
out.println("<HTML>");
out.println(" <HEAD><TITLE>A Servlet</TITLE></HEAD>");
out.println(" <BODY>");
out.println(" 以 " + method + " 方式访问该页面。取到的 param 参数为:" + param + "<br/>");
out.println(" <form action='" + requestURI + "' method='get'><input type='text' name='param' value=''><input type='submit' value='以 GET 方式访问 RequestServlet'></form>");
out.println(" <form action='" + requestURI + "' method='post'><input type='text' name='param' value=''><input type='submit' value='以 POST 方式访问 RequestServlet'></form>");
// 由客户端浏览器读取该文档的更新时间
out.println(" <script>document.write('本页面最后更新时间:' + document.lastModified + '<br />'); </script>");
out.println(" <script>document.write('本页面URL:' + location + '<br/>' ); </script>");
out.println(" </BODY>");
out.println("</HTML>");
out.flush();
out.close();
}
}
3)配置 <servlet>、<servlet-mapping>
一个完整的 Servlet 包括 Servlet 类、<servlet> 配置、<servlet-mapping> 配置,缺一不可,
利用 MyEclipse 向到新建 Servlet,MyEclipse 会自动完成 <servlet>、<servlet-mapping> 等配置
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">
<servlet>
<!--下面两行可以省略 -->
<description>This is the description of my J2EE component</description>
<display-name>This is the display name of my J2EE component</display-name>
<servlet-name>FirstServlet</servlet-name> <!-- Servlet的注册名,必须配置的属性,可自己定义,默认使用该Servletde的名字 [3]-->
<servlet-class>servlet.FirstServlet</servlet-class> <!-- Servlet类的全路径(包名+类名),必须配置的属性 [4]-->
</servlet>
<servlet-mapping> <!-- 对一个已经注册的 Servlet的映射 -->
<servlet-name>FirstServlet</servlet-name> <!-- Servlet的注册名 [2]-->
<url-pattern>/servlet/FirstServlet</url-pattern> <!--指明访问该 Servlet的访问路径 [1]-->
</servlet-mapping>
</web-app>
<!-- 服务器调用流程:http://localhost:8080/ABC --[1]--[2]--[3]--[4] -->
4)部署 Web 程序
a)手工部署: 在 Tomcat 目录 的 webapps下面新建文件夹,命名为 firstWeb,然后找到刚才 MyEclipse 的工作目录,将该目录下的
servlet工程下的 WebRoot 下的所有内容复制到 刚才在 Tomcat目录下建好的 webapps\firstWeb 下,启动 Tomcat,浏览器输入:
http://localhost:8080/firstWeb/servlet/FirstServlet,即可访问上面新建的 Servlet了
b)MyEclipse 自动部署
MyEclipse菜单中选择 Window |Preferences ,找到 Tomcat6.x,进行配置如下:
配置完毕后,下面进行部署 servlet:
部署后效果如下:
(在需要时,可以点击 Redeploy 来 reload 应用,
也可以登录Tomcat 管理 应用 http://localhost:8080/,在相应的应用后点击 reload)
下面 在 MyEclipse 下 启动 Tomcat
此时,部署完成,在浏览器输入地址即可访问 建立的 Servlet了(也可以在 MyEclipse内集成的浏览器打开)
5)测试
分别以 GET 和 POST 方式访问,查看不同之处
(下面是手动部署的地址,MyEclipse部署的地址应该是:http://localhost:8080/servlet/servlet/FirstServlet):
6)get 提交 和 post 的提交的区别
(1)post提交数据是隐式的,get是通过在url里面传递的(可以看一下你浏览器的地址栏),用来传递一些不需要保密的数据。post比get安全
(2)从速度看 get > post
(3)用get时,传输数据的大小有限制 (注意不是参数的个数有限制),一般不要大于2K;而post理论上无限制,实际开发中,建议不要大于64K。
(4)还有用GET的时候在SERVLET中要用DOGET方法,用POST就要用DOPOST方法。这是JSP在处理GET和POST的时候在JAVA 角度看的不同。
(5)还有一点需要注意哦,通过get方式来获取参数用的方法和通过post方式有些区别:
post:request.getParameter("");
get: request.QueryString("");
7)容易出错的地方
注意默认情况下,建立 Servlet 文件时,Servlet 的 URL 前会自动添加一个 /servlet 目录(跟工程名无关),(所以 web.xml 下的路径也是:
/sevlet/FirstServlet,这样的话,在运行时,浏览器输入的路径应该是:localhost:8080/工程名/url ,即http://localhost:8080/servlet/servlet/FirstServlet
而不是:http://localhost:8080/servlet/FirstServlet(注意,凑巧这里的工程名也是servlet),否则会报404错误 :
The requested resource (Servlet action is not available) is not available.
若想使用后者,可以在建立servlet文件时将 URL前面的默认的 /servlet 删除即可。
7)实例:response 生成图片验证码
服务器对客户端浏览器做出的响应被封装成一个 HttpServletResponse 对象。
要对浏览器进行操作,只需要操作 HttpServletResponse 对象就可以了。通过HttpServletResponse.getWriter()
获得一个 PrintWrite() 对象,该对象为 OutputStream 的子类。然后使用该对象输出信息就可以了。
通过 HttpServletResponse 获取的 PrintWrite 对象只能写字符型的数据,如果需要在客户端写二进制数据,
可以使用HttpServletResponse.getOutputStream()。方法 getWrite() 可以看做是方法getOutputStream() 的一个封装。
IdentityServlet,java文件:
package servlet;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
public class IdentityServlet extends HttpServlet {
private static final long serialVersionUID = -479885884254942306L;
//随机字符字典,不包含 O、0、1、I 等难辨认的字符
public static final char[] CHARS = { '2', '3', '4', '5', '6', '7', '8',
'9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M',
'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' };
public static Random random = new Random(); //随机数
public static String getRandomString() { //获取6位随机数
StringBuffer buffer = new StringBuffer(); //字符串缓存
for (int i = 0; i < 6; i++) { //循环6次
buffer.append(CHARS[random.nextInt(CHARS.length)]); //每次取一个随机字符
}
return buffer.toString();
}
public static Color getRandomColor() { //获取随机的颜色
return new Color(random.nextInt(255), random.nextInt(255), random
.nextInt(255));
}
public static Color getReverseColor(Color c) { //返回某颜色的反色
return new Color(255 - c.getRed(), 255 - c.getGreen(), 255 - c
.getBlue());
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { //GET方法
response.setContentType("image/jpeg"); //设置输出类型
String randomString = getRandomString(); //随机字符串
request.getSession(true).setAttribute("randomString", randomString); //放到 session 中
int width = 100; //图片宽度
int height = 30; //图片高度
Color color = getRandomColor(); //随机颜色,用于背景色
Color reverse = getReverseColor(color); //反色,用于前景色
BufferedImage bi = new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB); //创建一个彩色图片
Graphics2D g = bi.createGraphics(); //获取绘图对象
g.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 16));//设置字体
g.setColor(color); //设置颜色
g.fillRect(0, 0, width, height); //绘制背景
g.setColor(reverse);//设置颜色
g.drawString(randomString, 18, 20); //绘制随机字符
for (int i = 0, n = random.nextInt(100); i < n; i++) { //画最多100个噪音点
g.drawRect(random.nextInt(width), random.nextInt(height), 1, 1); //随机噪音点
}
ServletOutputStream out = response.getOutputStream(); // 转成JPEG格式
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out); //编码器
encoder.encode(bi); //对图片进行编码
out.flush(); //输出到客户端
}
public static void main(String[] args) {
System.out.println(getRandomString());
}
}
上面程序中,
在Eclipse中处理图片,需要引入两个包:
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
若有报错:
Access restriction:The type JPEGCodec is not accessible due to restriction on required library
Access restriction: The type JPEGImageEncoder is not accessible due to restriction on required library
解决方法:Project -> Properties,先remove掉JRE System Library,然后再Add Library重新加入。
运行结果:
为了方便演示,在 WebRoot 下建立一个 identity.html 文件引用这个图片验证码:
<!DOCTYPE html>
<html>
<head>
<title>identity.html</title>
<meta name="keywords" content="keyword1,keyword2,keyword3">
<meta name="description" content="this is my page">
<meta name="content-type" content="text/html; charset=GB2312">
<!--<link rel="stylesheet" type="text/css" href="./styles.css">-->
</head>
<body>
<script>
function reloadImage() {
document.getElementById('btn').disabled = true;
document.getElementById('identity').src='servlet/IdentityServlet?ts=' + new Date().getTime();
}
</script>
<img src="servlet/IdentityServlet" id="identity" οnlοad="btn.disabled = false; " />
<input type=button value=" 换个图片 " οnclick="reloadImage()" id="btn">
</body>
</html>
运行如下:
三、Servlet生命周期
1. 当serlvet 第一次被调用的时候,会触发init函数,该函数会把servlet实例装载到内存.init函数只会被调用一次
2. 然后去调用servlet 的 service函数
3. 当第二次后访问该servlet 就直接调用 service 函数.
4. 当 web应用 reload 或者关闭 tomcat 或者关机 都会去调用destroy函数,该函数就会去销毁serlvet
Servlet的生命周期
当客户端第一次向web服务器发出一个servlet请求时,web服务器将会创建一个该servlet的实例,
并且调用servlet的init()方法;如果当服务器已经存在了一个servlet实例,那么,将直接使用此实例;然后再调用service()方法,
service()方法将根据客户端的请求方式来决定调用对应的doXXX()方法;当 web应用reload 或者关闭tomcat 或者关机,
web服务器将调用destroy()方法,将该servlet从服务器内存中删除。
生命全过程:
1.加载
2.实例化
3.初始化
4.处理请求
5.退出服务
简述servlet的生命周期(工作流程)?
标准版本:
①WEB服务器首先会检查是否已经装载并创建了该servlet实例对象。如果是直接进行第④步,否则执行第②步。
②装载并创建该Servlet的一个实例对象。
③调用Servlet实例对象的init()方法。
④创建一个用于封装HTTP请求消息的HttpServletRequest对象和一个代表HTTP响应消息的HttpServletResponse对象,然后调用service()方法并将请求和响应作为参数传递进去。
⑤WEB应用被停止或重启之前,Servlet引擎将卸载Servlet,在卸载之前调用Servlet的destroy()方法
四、开发 Servlet 的方式
开发 Servlet 有三种方法:(1)实现 Servlet 接口
(2)通过继承 GenericServlet
(3)通过继承 HttpServlet
1)实现 Servlet 接口的方式
新建 Web Object,命名为 ooo ,新建 Servlet,命名为,MyServlet,url 路径为 /MyServlet
MyServlet.java 代码
package ooo;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class MyServlet implements Servlet
{
//该函数用于初始化servlet,就是把该servlet装载到内存中
//该函数只会被调用一次
public void init(ServletConfig config)
throws ServletException{
System.out.println("init it");
}
//得到ServletConfig对象
public ServletConfig getServletConfig(){
return null;
}
//该函数是服务函数,我们的业务逻辑代码就是写在这里
//该函数每次都会被调用
public void service(ServletRequest req,
ServletResponse res)
throws ServletException,
java.io.IOException{
System.out.println("service it");
PrintWriter printWriter=res.getWriter();
printWriter.println("<h1>"+"hello,world"+"</h1>");
}
//该函数时得到servlet配置信息
public java.lang.String getServletInfo(){
return null;
}
//销毁该servlet,从内存中清除,该函数被调用一次
public void destroy(){
System.out.println("destroy it");
}
}
部署完成后,在浏览器中输入:http://localhost:8080/ooo/MyServlet
刷新一下,最后用 Tomcat Manager 停止 ooo 应用,对应的 Tomcat 服务器的显示如下:
2)使用 GenericServlet 开发 servlet(了解即可)
通过 GenericServlet 去开发 servlet,只需要重写 service 方法,相对来讲简单一些。
在 ooo 工程中,建立 Servlet 命名为:MyGenericServlet
MyGenericServlet.java 代码
package ooo;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class MyGenericServlet extends GenericServlet
{
public void service(ServletRequest req,
ServletResponse res)
throws ServletException,
java.io.IOException{
res.getWriter().println("hello,world,i am geneirc servlet");
}
}
部署,浏览器运行:
3)使用继承 HttpServlet 的方法开发 Servlet
这个是目前最常用的方式。
在 ooo 工程中,建立 Servlet 命名为:MyHttpServlet
MyHttpServlet.java 代码如下:
package ooo;
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 MyHttpServlet extends HttpServlet
{
//在HttpServlet 中,设计者对post 提交和 get提交分别处理
//回忆 <form action="提交给?" method="post|get"/>,默认是get
//其实 doGet()/ doPost() 最终也去调用 service 方法
protected void doGet(HttpServletRequest req,
HttpServletResponse resp)
throws ServletException,
java.io.IOException{
resp.getWriter().println("i am httpServet doGet()");
}
protected void doPost(HttpServletRequest req,
HttpServletResponse resp)
throws ServletException,
java.io.IOException{
resp.getWriter().println("i am httpServet doPost() post name="+req.getParameter("username"));
}
}
运行结果:
五、Servlet的细节问题
(1)一个已经注册的Servlet可以被多次映射即:
<servlet>
<!--下面两行可以省略 -->
<description>This is the description of my J2EE component</description>
<display-name>This is the display name of my J2EE component</display-name>
<servlet-name>MyServlet</servlet-name> <!-- Servlet的注册名,必须配置的属性,可自己定义,默认使用该Servletde的名字 -->
<servlet-class>ooo.MyServlet</servlet-class> <!-- Servlet类的全路径(包名+类名),必须配置的属性 -->
</servlet>
<servlet-mapping> <!--映射1-->
<servlet-name>MyServlet</servlet-name>
<url-pattern>/MyServlet1</url-pattern>
</servlet-mapping>
<servlet-mapping> <!--映射2-->
<servlet-name>MyServlet</servlet-name>
<url-pattern>/MyServlet2</url-pattern>
</servlet-mapping>
测试:使用http://localhost:8080/ooo/MyServlet1 与 http://localhost:8080/ooo/MyServlet2 效果一样
(2)当映射一个 servlet 时候,可以多层,可以随意后缀(因此:后缀名是可能是假象),如:再添加一个映射
<servlet-mapping>
<servlet-name>MyServlet</servlet-name>
<url-pattern>/My/abc.html</url-pattern>
</servlet-mapping>
使用 http://localhost:8080/ooo/My/abc.html 测试,效果一样
(3)使用通配符在 servlet 映射到 URL 中
有两种格式:
第一种格式: *.扩展名 比如 *.do *.ss
第二种格式: 以 / 开头 同时以 /* 结尾 比如 /* /news/*
通配符练习题:
Servlet1映射到 /abc/*
Servlet2映射到 /*
Servlet3映射到 /abc
Servlet4映射到 *.do
问题(面试题):
当请求URL为“/abc/a.html”: “/abc/*”和“/*”都匹配,哪个servlet响应?Servlet引擎将调用Servlet1。
当请求URL为“/abc”时:“/abc/*”和“/abc”都匹配,哪个servlet响应?Servlet引擎将调用Servlet3。
当请求URL为“/abc/a.do”时:“/abc/*”和“*.do”都匹配,哪个servlet响应 ?Servlet引擎将调用Servlet1。
当请求URL为“/a.do”时:“/*”和“*.do”都匹配,哪个servlet响应?Servlet引擎将调用Servlet2。
当请求URL为“/xxx/yyy/a.do”时:“/*”和“*.do”都匹配,哪个servlet响应?Servlet引擎将调用Servlet2。
在匹配的时候,要参考的标准:
1、看谁的匹配度高,谁就被选择
2 、 *.do 的优先级最低
(4)Servlet 单例问题
当 Servlet 被第一次访问后,就被加载到内存,以后该实例对各个请求服务。即在使用中是单例。
因为 Servlet 是单例,因此会出现线程安全问题,比如,售票系统,如果不加同步机制,则会出现问题。
解决原则:1)如果一个变量需要多个用户共享,则应当在访问该变量的时候,加同步机制
synchronized(对象) { //同步代码 }
2) 如果一个变量不需要共享,则直接在 doGet() 或者 doPost()定义.这样不会存在线程安全问题
(5)servlet 中的 <load-on-stratup> 配置
需求: 当我们的网站启动的时候,可能会要求初始化一些数据,(比如创建临时表), 在比如:
我们的网站有一些要求定时完成的任务[ 定时写日志,定时备份数据.. 定时发送邮件..]
解决方法: 可以通过<load-on-startup> 配合 线程知识搞定.
先说明<load-on-startup>: 通过配置<load-on-startup> 我们可以指定某个Servlet 自动创建.
下面模拟一个定时发送电子邮件的功能:
实现思路:
sendEmailTable(表)
id content sendtime
1 “hello” 2011-11-1120:11
2 “hello2” 2012-11-1110:00
看看如何线程去完成任务:新建 SendMailTread.java 类
package ooo;
public class SendEmailThread extends Thread{
@Override
public void run() {
int i=0;
try {
while(true){
//每休眠一分钟(这里为了方便演示,写的是10秒),就去扫表sendmail, 看看那份信件应当被发出
Thread.sleep(10*1000);
System.out.println("发出 第"+(++i)+"邮件");//javamail
}
} catch (Exception e) {
e.printStackTrace();
// TODO: handle exception
}
}
}
新建 Servlet 命名:MyInitServlet
package ooo;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
public class MyInitServlet extends HttpServlet {
public void destroy() {
super.destroy(); // Just puts "destroy" string in log
// Put your code here
}
/**
* Initialization of the servlet. <br>
*
* @throws ServletException if an error occurs
*/
public void init() throws ServletException {
// Put your code here
System.out.println("MyInitServlet1 的init被调用..");
//完成一些初始化任务
System.out.println("创建数据库,表,读取参数");
//创建一个线程
SendEmailThread sendEmailThread=new SendEmailThread();
sendEmailThread.start();
}
}
配置 web.xml
1)删除下面的映射
<servlet-mapping>
<servlet-name>MyInitServlet</servlet-name>
<url-pattern>/MyInitServlet</url-pattern>
</servlet-mapping>
2)在 servlet 中添加<load-on-startup>:
<servlet>
<servlet-name>MyInitServlet</servlet-name>
<servlet-class>ooo.MyInitServlet</servlet-class>
<!-- 1表示该servlet被 init的顺序 -->
<load-on-startup>1</load-on-startup>
</servlet>
配置之后,就无法直接从浏览器访问,通过后台运行:每隔10秒模拟发送一次邮件:
六、ServletConfig 对象
该对象只要用于 读取 servlet 的配置信息
实例:新建 Servlet 命名:ServletConfigTest
web.xml 中添加 以下配置信息:
<servlet>
<servlet-name>ServletConfigTest</servlet-name>
<servlet-class>ooo.ServletConfigTest</servlet-class>
<init-param> <!-- 这里可以给servlet配置信息,这里配置的信息,只能被该servlet 读取 -->
<param-name>enconding</param-name>
<param-value>gb2312</param-value>
</init-param>
<!-- 可以配置很多信息 -->
<init-param>
<param-name>version</param-name>
<param-value>1.0</param-value>
</init-param>
<init-param>
<param-name>author</param-name>
<param-value>wtfmonking</param-value>
</init-param>
</servlet>
<!-- 如果这里配置参数,可被所有servlet读取 -->
<!--
<context-param>
<param-name></param-name>
<param-value></param-value>
</context-param>
-->
ServletConfigTest.java 代码
package ooo;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ServletConfigTest extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
//这里可以通过读取 ServletConfig 里的数据 进行设置 Enconding
response.setCharacterEncoding(this.getServletConfig().getInitParameter("enconding"));
PrintWriter out = response.getWriter();
out.println("Enconding :"+this.getServletConfig().getInitParameter("enconding")+"......");
//如果要把所有的参数都读取,则使用 如下方法 :
Enumeration<String> names=this.getServletConfig().getInitParameterNames();
while(names.hasMoreElements())
{
String name=names.nextElement();
out.println(name+" is :");
out.println(this.getServletConfig().getInitParameter(name)+"......");
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request, response);
}
}
浏览器输出结果:
七、http 协议 (后续再讲)
八、HttpServletResponse的再说明
getWriter() 与 getOutputStream() 的区别
1)getWriter() 用于向客户机回送字符数据
2)getOutputStream() 返回的对象,可以回送字符数据,也可以回送字节数据(二进制数据)
OutputStream os=response.getOutputStream();os.write("hello,world".getBytes());
如何选择:
如果我们是回送字符数据,则使用 PrintWriter对象 ,效率高
如果我们是回送字节数据(binarydate) ,则只能使用 OutputStream
注意:
这两个流不能同时使用,如下,则会报错
OutputStream os=response.getOutputStream();
os.write("hello,world".getBytes());
PrintWriter out=response.getWriter();
out.println("abc");
原因分析:
从图中可以看出,web 服务器会自动关闭流(当然,最好是在程序中主动关闭流)
九、参数的传递方式sendRedirect()和session()
需求:当用户登录成功后,把该用户名字显示在登录成功页面;
解决思路:
a、使用java基础 static
b、使用sendRedirect()
c、使用session 传递
1、使用sendRedirect()来传递字符参数
基本格式: response.sendRedirect(“servlet的地址?参数名=参数值&参数名=参数值...”);
参照值是String , 参数名应当使用 字母组合
接收数据:String 参数=request.getParameter(“参数名”);
实例:
response.sendRedirect("/UsersManager/MainFrame?uname="+username+"&pwd="+password);
//另一个 servlet中:
String username=request.getParameter("uname");
String upwd=request.getParameter("pwd");
out.println(username+" pwd="+upwd);
2、使用session()来传递字符参数和对象
1)传递字符串
request.getSession().setAttribute("loginuser",username); //放入session
//另一个 Servlet中:
String username2=(String) request.getSession().getAttribute("loginuser");//取出session
2)传递对象
建立一个 User 对象,包含用户名和密码
User user= new User();
user.setName("wtf");
user.setPassWord("123");
request.getSession().setAttribute("userobj",user); //放入session
//另一个 Servlet中:
User user=(User)request.getSession().getAttribute("userObj");//取出session
十、中文乱码处理
(1)表单 form
a)post 方式提交
在服务器端设置成浏览器端的编码方式。
解决方法: request.setCharacterEncoding("utf-8"); //gbk gb2312
b)get 方式提交
解决方法:
String username=new String(request.getParameter("username").getBytes("iso-8859-1"),"utf-8");// 把iso-8859-1 转换成 utf-8
或直接写一个工具类
package com.wtf;
public class MyTools {
public static String getNewString(String str) {
String newString="";
try {
newString=new String(str.getBytes("iso-8859-1"),"utf-8");
} catch (Exception e) {
e.printStackTrace();
// 把iso-8859-1 转换成 utf-8
}
return newString;
}
}
(2) 超链接
<a href=”http://www.baidu.com?name=汉字”>测试</a>
解决:该方法和get处理方法一样.因为超链接是使用 get 提交
(3)sendRedirect() 发生乱码
response.sendRedirect(“servlet地址?username=汉字”);
解决:该方法和get处理方法一样.
说明: 我们应当尽量使用post 方式提交;
(4)返回浏览器显示乱码
在服务端是中文,在response的时候,也要考虑浏览器显示是否正确,一般我们通过
response.setContentType(“text/html;charset=utf-8”); 解决
(5)下载提示框中文乱码
当我们下载文件的时候,可能提示框是中文乱码
解决方法:Stringtemp=java.net.URLEncoder.encode("传奇.mp3","utf-8");
response.setHeader("Content-Disposition","attachment;filename="+temp);
十一、HttpServletRequest对象的详解
该对象表示浏览器的请求(http请求), 当web 服务器得到该请求后,会把请求信息封装成一个HttpServletRequest 对象
(1) getRequestURL方法返回客户端发出请求时的完整URL。
(2) getRequestURI方法返回请求行中的资源名部分。
(3) getQueryString 方法返回请求行中的参数部分(参数名+值)。
准确的说就是接收以 get 方式提交的 参数=参数值
该函数可以获取请求部分的数据比如:
http://localhost/web名?username=abc&pwd=123
request.getQueryString();就会得到 username=abc&pwd=123
(可以使用split将参数按&分隔)
(4)getRemoteAddr方法返回发出请求的客户机的IP地址
(5)getRemoteHost方法返回发出请求的客户机的完整主机名
(5)getRemotePort方法返回客户机所使用的网络端口号,客户机的端口号是随机选择的,web服务器的端口号是一定的
(6)getLocalPort方法返回web服务器所使用的网络端口号
(7)getLocalAddr方法返回WEB服务器的IP地址。
(8)getLocalName方法返回WEB服务器的主机名
(9)url 和 uri 的区别
Url=http://localhost:8088/ooo/MyServlet 完整的请求
Uri=/ooo/GetinfoServlet web应用的名称+资源的名称
十二、表单实例:如何获取用户提交的内容(通过表单提交的内容)
表单 Servlet:FormServlet.java
package ooo;
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 FormServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
out.println("<form action='/ooo/ClServlet' method='post'><br/>");
out.println("<input type='hidden' value='这里是隐藏的内容' name='hidden1'/>");
out.println("用户名:<input type='text' name='username'/><br/>");
out.println("密 码:<input type='password' name='pwd'/><br/>");
out.println("性 别:<input type='radio' name='sex' value='男'/>男 <input type='radio' name='sex' value='女'/>女<br/>");
out.println("你的爱好:<input type='checkbox' name='hobby' value='音乐'>音乐 <input type='checkbox' name='hobby' value='体育'>体育 <input type='checkbox' name='hobby' value=\"旅游\">旅游<br/>");
out.println("所在城市:<select name='city'><option value='bj'>北京</option><option value='cq'>重庆</option></select><br/>");
out.println("你的介绍:<textarea cols='20' rows='10' name='intro' >请输入介绍..</textarea><br/>");
out.println("提交照片:<input type='file' name='photo'><br/>");
//什么时候使用hidden传输数据 1.不希望用户看到该数据 2. 不希望影响节目,同时使用该数据
out.println("<input type='submit' value='提交信息'/>");
out.println("</form>");
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request, response);
}
}
处理表单的 Servlet:ClServlet.java
package ooo;
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 ClServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
String u=request.getParameter("username");
String p=request.getParameter("pwd");
String sex=request.getParameter("sex");
//如果接受复选框的内容,则使用getparameterValues
String [] hobbies=request.getParameterValues("hobby");
String city=request.getParameter("city");
String intro=request.getParameter("intro");
String hidden1=request.getParameter("hidden1");
out.println("用户名="+u+"<br/>");
out.println("密 码="+p+"<br/>");
out.println("性 别="+sex+"<br/>");
if(hobbies!=null){
for(int i=0;i<hobbies.length;i++){
out.println("爱好:"+hobbies[i]);
}
}else{
out.println("你没有爱好");
}
out.println("<br/>所在城市:"+city);
out.println("<br/>个人介绍:"+intro);
out.println("<br/>隐藏控件数据:"+hidden1);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request, response);
}
}
效果: