一、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" onload="btn.disabled = false; " /> <input type=button value=" 换个图片 " onclick="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); } }
效果: