版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
《JSP实用教程(微课视频·AI赋能版)》—电子教学计划第6章Servlet教学计划一、基本信息项目内容章节第6章Servlet周次第8~9周学时6学时(理论4+实验2;建议)授课对象计算机类/软件工程本科二、三年级先修知识Java程序设计(类、继承、接口、异常、集合、正则表达式)、第1~5章JSP基础(page指令、程序片、request/response/session内置对象、表单与参数)教学方法讲授+现场演示(编译部署、请求Servlet)+代码逐段拆解+上机实操+排错训练教学环境多媒体机房;JDK(8或以上)、Tomcat(9.x)、纯文本编辑器/IDE、浏览器;需将Tomcat的lib\servlet-api.jar复制到工程classes目录教材章节6.1~6.9(含上机实验6-1绘制多边形、6-2双色球福利彩票、6-3分析整数)课程编号待任课教师填写日期待填写二、教学目标2.1知识与技能目标(布鲁姆认知层次)编号学习目标认知层次重要度K1复述Servlet的概念,说明Servlet是继承HttpServlet的类所创建的、在服务器端响应请求的对象,并解释JSP底层就是Servlet记忆/理解*K2独立编写并部署一个Servlet:写Servlet类→编译得到字节码→按包名存入WEB-INF\classes→编写web.xml→浏览器请求应用***K3配置web.xml的<servlet>与<servlet-mapping>标记,说明servlet-name、servlet-class、url-pattern三者的对应关系应用***K4使用request.getParameter()获取URL参数与表单数据,并从JSP页面用表单/超链接请求Servlet应用**K5描述Servlet生命周期(init→service→destroy),区分init只调一次、service/doGet/doPost每次请求各在独立线程中执行分析**K6辨析doGet与doPost的分工,说明service如何按请求方式分派到doGet/doPost分析**K7区分sendRedirect(重定向)与forward(转发):地址栏是否变化、目标能否用同一request取数据,并能各自选用分析/评价***K8运用session在多个页面/Servlet间保存用户信息,理解HTTP无状态与会话的关系;理解Servlet成员变量被多用户共享及其线程安全问题应用/评价**2.2教学重点与难点教学重点:Servlet的编写—编译—部署—请求完整闭环,尤其是字节码按包名存放WEB-INF\classes与web.xml的两对标记***;用request.getParameter()接收参数、从JSP用表单/超链接请求Servlet**;重定向sendRedirect与转发forward的区别与选用***;Servlet生命周期与doGet/doPost分派机制**。教学难点("为什么难"):部署链路环节多、约定死:Servlet不像JSP那样"放进目录就能跑",它必须"编译→按包名建目录放.class→写web.xml映射URL",任何一环(包名对不上目录、servlet-name两处不一致、url-pattern少写斜杠、WEB-INF没大写、忘了-cpservlet-api.jar)都会404/500,初学者难以定位是哪一环错;成员变量共享+多线程:Servlet只被创建一个实例,多个用户请求共享同一个Servlet对象的成员变量,各请求又运行在不同线程中——"共享"(成员变量)与"隔离"(service里的局部变量)并存,学生易混,且共享变量的线程安全(synchronized)看不见摸不着;重定向vs转发:两者都能"跳到另一个页面",表面相似,实则一个是"浏览器重新发起新请求(地址栏变、原request数据丢失)",另一个是"服务器内部转交同一请求(地址栏不变、request数据可续用)",差别隐藏在"谁发起请求"这一看不见的机制里;JSP与Servlet的调用关系:web.xml里url-pattern写成/lookHello(带斜杠),但JSP表单action/超链接href要写成lookHello(不带斜杠),一字之差就变成"请求服务目录下的某个页面"而非Servlet。2.3难点突破策略表难点学生易错点突破策略部署链路包名moon.sun与目录moon\sun对不上;WEB-INF写成小写;web.xml放错目录画"部署四步流水线"图并当堂逐步验证:①编译时进classes目录执行javac-cpservlet-api.jarmoon\sun\Xxx.java;②确认classes\moon\sun\Xxx.class生成;③web.xml必须在WEB-INF下(不是classes下);④浏览器按url-pattern请求。每步给出"验证方法"web.xml两对标记<servlet-name>在<servlet>和<servlet-mapping>里写得不一致;漏配一对强调"servlet-name是内部别名,两处必须一字不差":servlet-class(用哪个类)—servlet-name(起个名)—url-pattern(用什么URL访问),三者靠servlet-name串起来。现场故意把两处name写不同,让学生看404成员变量共享/线程安全以为每个用户各有一个Servlet;不理解为何要synchronized用"一个收银员(Servlet实例)+多个顾客排队(线程)"类比:柜台上的账本(成员变量)大家共用,收银员手里的便签(局部变量)各人各份。运行例6-5,多标签页刷新看计数累加,再讲synchronized防止两人同时改账本导致算错重定向vs转发以为两者等价;不知地址栏为何变/不变一张对照表+两次演示:转发(forward)后地址栏仍是原地址、目标能取到request数据;重定向(sendRedirect)后地址栏变成新地址、目标取不到原request数据。一句话记忆:"转发是服务器内部转交同一次请求,重定向是让浏览器再发一次新请求"JSP调Servlet的斜杠action/href写成/lookHello导致找错资源明确规则:url-pattern写/lookHello,但JSP里action="lookHello"、href="lookHello"(去掉开头斜杠,用相对路径)。板书对比正误两种写法2.4课程思政融入点规范与工程严谨性:Servlet部署链路环环相扣、约定严格(目录大小写、包名映射、配置一致),一处不合规则整体失败——引导学生体会"工程约定即契约",培养严谨、规范、对交付负责的职业素养。并发与责任意识:共享变量的线程安全问题,映射到真实系统中多用户并发访问的数据一致性;引导学生认识到看不见的并发缺陷可能造成严重后果,树立"为高并发下的正确性负责"的意识,理解Web应用安全与可靠性的责任边界。架构分层与解耦思想:通过Servlet转发把"页面显示"与"数据处理"分离、把不同需求指派给不同Servlet,正是分层解耦、可维护性的工程思想;引导学生从"能跑"走向"好维护、可扩展",并关注Web中间件/应用服务器等关键基础软件的自主可控。三、教学内容详述本章按6学时组织(理论4+实验2),理论部分切为4个课时:第1课时Servlet概念+编写/部署/运行(6.1);第2课时工作原理+生命周期(6.2)+通过JSP访问Servlet(6.3);第3课时共享变量(6.4)+doGet/doPost(6.5);第4课时重定向与转发(6.6)+session(6.7)。本章是编程实操类,采用"目的→步骤/写法→预期结果→常见报错"的操作拆解式讲授。全章统一约定(先讲清,后续不再重复):本章在Tomcat的webapps下新建Web服务目录ch6,页面均放在ch6中;Servlet类的包名统一为moon.sun,字节码文件放在ch6\WEB-INF\classes\moon\sun中(WEB-INF字母全大写);web.xml放在ch6\WEB-INF下。因本章用到javax.servlet与javax.servlet.http包(不在JDK核心类库中),须事先把Tomcat安装目录lib子目录中的servlet-api.jar复制(不要剪切)到ch6\WEB-INF\classes中,便于编译。第1课时:Servlet是什么+编写/部署/运行(6.1)课堂引入(5min)教学提示:回顾第1章讲过的JSP运行原理——"JSP页面被服务器翻译成一个.java文件,其本质是一个Servlet类"。抛出问题:"既然JSP底层就是Servlet,那能不能反过来,我们自己手写一个Servlet类来响应请求?"引出本章:Servlet技术的核心,就是在服务器端创建一个响应用户请求的对象(习惯称为一个Servlet)。告知本节目标:亲手走通"写Servlet类→编译→部署→浏览器请求"的完整闭环。6.1.1Servlet类与字节码文件(15min)什么是Servlet类:编写一个创建Servlet的类,就是编写javax.servlet.http包中HttpServlet类的一个子类。HttpServlet实现了Servlet接口、实现了响应用户的方法。这样的子类习惯称作一个Servlet类,它创建的对象习惯称作一个Servlet。例6-1(Example6_1.java):一个最简单的Servlet,用户请求它时,在浏览器看到一行问候。packagemoon.sun;//包名moon.sun,对应目录moon\sun
importjava.io.*;
importjavax.servlet.*;
importjavax.servlet.http.*;
publicclassExample6_1extendsHttpServlet{//继承HttpServlet
publicvoidinit(ServletConfigconfig)throwsServletException{
super.init(config);
}
publicvoidservice(HttpServletRequestrequest,
HttpServletResponseresponse)throwsIOException{
//设置响应的MIME类型(告诉浏览器:返回的是UTF-8编码的HTML)
response.setContentType("text/html;charset=utf-8");
PrintWriterout=response.getWriter();//获得向用户发送数据的输出流
out.println("<html><bodybgcolor=#ffccff>");
out.println("<h1>helloservlet,你好servlet</h1>");
out.println("</body></html>");
}
}逐段拆解:目的:向浏览器输出一段HTML问候。写法:①extendsHttpServlet——一切Servlet类都继承它;②service(...)方法——响应请求的入口,两个参数request(封装用户请求)、response(用来响应用户);③response.setContentType(...)——设置返回内容类型与编码,中文必须带charset=utf-8;④response.getWriter()得到输出流out,out.println(...)往浏览器写内容。预期结果:浏览器出现粉底、大字"helloservlet,你好servlet"(见教材图6.3)。常见报错:忘记charset=utf-8→中文乱码;importjavax.servlet.*缺失或编译时没带servlet-api.jar→编译报"找不到符号"。字节码文件如何保存(对照教材图6.1):要让Tomcat用这个类创建Servlet,须把编译得到的Example6_1.class按包名对应的目录存放到服务目录的特定子目录中。包名moon.sun对应目录moon\sun,因此字节码要放到ch6\WEB-INF\classes\moon\sun中。编译命令(板书,强调进入classes目录后执行):classes>javac-cpservlet-api.jarmoon\sun\Example6_1.java-cpservlet-api.jar:指定使用非核心类库中的类(javax.servlet.*就在这个jar里)。若servlet-api.jar已复制到当前classes目录,直接写文件名即可;否则要显式给出完整路径,例如:classes>javac-cpD:\tomcat\lib\servlet-api.jarmoon\sun\Example6_1.java保存Java源文件时:"保存类型"选"所有文件"、"编码"选UTF-8(避免中文乱码,也避免存成.java.txt)。常见报错:在moon或sun目录里执行javac→包路径错,应在classes父目录层编译;漏写-cpservlet-api.jar→编译报错找不到javax.servlet。6.1.2编写部署文件web.xml(15min,重点)为什么要web.xml:字节码放好后,还必须给Tomcat写一个部署文件,Tomcat才会用这个类创建Servlet对象。该文件名字必须是web.xml,是由若干标记(元素)组成的XML文本文件,须保存到服务目录的WEB-INF子目录中(即ch6\WEB-INF,不要放在classes里)。例6-1对应的web.xml:<?xmlversion="1.0"encoding="utf-8"?>
<web-app>
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>moon.sun.Example6_1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/lookHello</url-pattern>
</servlet-mapping>
</web-app>逐标记拆解:<?xml...?>声明:必须是文件第一行,前面不能有空白/注释;?xml与<、?与>之间不要有空格。若省略encoding则默认UTF-8;这里显式写utf-8,则标记内容可含汉字,Tomcat的XML解析器能正确识别。根标记<web-app>:整个文件唯一的根标记,所有内容都在它里面。<servlet>(及子标记):部署一个Servlet。<servlet-class>告诉服务器用哪个类创建(写全限定名moon.sun.Example6_1);<servlet-name>给这个Servlet起个名字(这里叫hello)。一个web.xml可有多个<servlet>,但它们的servlet-name必须互不相同。<servlet-mapping>(及子标记):为Servlet指定访问URL。<servlet-name>必须与上面<servlet>里的完全相同(靠它把两块关联起来);<url-pattern>指定用户用什么URL请求。本例url-pattern是/lookHello,则用户须在浏览器输入::8080/ch6/lookHello教学提示(板书三者关系):servlet-class(哪个类)——servlet-name(内部别名,两处一致)——url-pattern(外部URL)。画箭头:url-pattern→找到同名servlet-name→定位servlet-class→创建并运行Servlet。修改web.xml是否要重启:对webapps下的服务目录,修改并保存web.xml后Tomcat会立刻重新读取,不必重启;但若web.xml有语法错,Tomcat会关闭该服务目录下所有Servlet的使用权限,必须改正确才行。(注意:若不是webapps下的服务目录,新建/修改web.xml需重启Tomcat。)常见报错:两处servlet-name写得不一致→404;web.xml放到了classes里而非WEB-INF下→Tomcat找不到部署文件;XML标记未闭合/声明前有空行→解析失败。6.1.3Servlet的创建与运行(5min)Servlet由Tomcat负责创建:设计者只要备好类、写好web.xml,用户按url-pattern请求即可。若服务器还没有名为hello的Servlet,就会根据web.xml中servlet-class指定的类创建一个。重要提醒:Servlet一旦被创建,若此后修改了源文件并重新编译、且希望服务器用新类创建Servlet,必须重启Tomcat(不同于改web.xml)。演示:浏览器访问:8080/ch6/lookHello,看到例6-1的问候页(教材图6.3)。6.1.4向Servlet传递参数的值(15min)目的:请求Servlet时,可在URL后追加参数及其值,Servlet用request.getParameter("参数名")取值。格式:url-pattern?参数1=值&参数2=值…参数n=值取值:request.getParameter("参数n")(返回值都是字符串,需要数字时自己转换)。例6-2(Example6_2.java):用户请求Servlet绘制一个椭圆,把椭圆的宽(横轴长)width和高(纵轴长)height通过参数传给Servlet。packagemoon.sun;
importjava.awt.image.BufferedImage;
importjava.awt.*;
importjava.awt.geom.*;
importjavax.imageio.ImageIO;
importjava.io.*;
importjavax.servlet.*;
importjavax.servlet.http.*;
publicclassExample6_2extendsHttpServlet{
publicvoidinit(ServletConfigconfig)throwsServletException{
super.init(config);
}
publicvoidservice(HttpServletRequestrequest,
HttpServletResponseresponse)throwsIOException{
request.setCharacterEncoding("utf-8");
doublewidth=Double.parseDouble(request.getParameter("width"));//取参数并转double
doubleheight=Double.parseDouble(request.getParameter("height"));
response.setContentType("image/jpeg");//返回的是图片
Ellipse2Dellipse=new
Ellipse2D.Double(400-width/2,300-height/2,width,height);//椭圆居中
BufferedImageimage=getImage(ellipse);
try{
OutputStreamoutClient=response.getOutputStream();//二进制输出流
booleanboo=ImageIO.write(image,"jpeg",outClient);//把图片发送到客户端
}
catch(Exceptionexp){}
}
BufferedImagegetImage(Shapeshape){//返回图形的图像
intwidth=800,height=600;
BufferedImageimage=
newBufferedImage(width,height,BufferedImage.TYPE_INT_RGB);//内存图像
Graphicsg=image.getGraphics();
g.fillRect(0,0,width,height);//图像底色
Graphics2Dg_2d=(Graphics2D)g;
g_2d.setColor(Color.blue);
g_2d.fill(shape);//在图像上绘制图形
returnimage;
}
}逐段拆解:目的:把URL参数转成图形尺寸,在服务器端画一张JPEG图片返回给浏览器。写法要点:①getParameter拿到的是字符串,用Double.parseDouble转数字;②因为要返回图片,setContentType("image/jpeg")(不是text/html),并用response.getOutputStream()拿二进制输出流(区别于文本用的getWriter());③用BufferedImage在内存里画图,ImageIO.write(...,"jpeg",...)输出。预期结果:浏览器显示一张蓝色椭圆图片(教材图6.4)。数值复核:源码用Ellipse2D.Double(400-width/2,300-height/2,width,height)使椭圆在800×600底图上居中——若width=500、height=200,则左上角为(150,200),椭圆右下到(650,400),恰好水平/垂直居中,与源一致。部署与请求:向ch6\WEB-INF\web.xml追加一对标记,Servlet名lookPic、url-pattern为/lookPic:<servlet>
<servlet-name>lookPic</servlet-name>
<servlet-class>moon.sun.Example6_2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>lookPic</servlet-name>
<url-pattern>/lookPic</url-pattern>
</servlet-mapping>浏览器输入(把宽高作为参数传入)::8080/ch6/lookPic?width=500&height=200常见报错:getParameter返回null(参数名拼错/URL没带该参数)再parseDouble→NumberFormatException/空指针500;返回图片却用了getWriter()→图片打不开。板书建议:画"部署四步流水线"总图——①编译(进classes,带servlet-api.jar)②字节码进WEB-INF\classes\包路径③写/改web.xml(在WEB-INF下)④浏览器按url-pattern请求——每步标注验证方法与易错点。本课时是全章基石,务必让每个学生走通一次闭环。第2课时:Servlet工作原理与生命周期(6.2)+通过JSP访问Servlet(6.3)知识铺垫(3min)复习上节:Servlet由Tomcat通过读取web.xml来创建并运行。本节揭开"幕后"——Servlet的生命周期,以及如何从JSP页面(而非直接敲URL)去请求Servlet。6.2.1Servlet对象的生命周期(12min,难点)核心认识:一个Servlet是HttpServlet子类的一个实例,由Tomcat负责创建与初始化。当多个用户请求同一个Servlet时,服务器为每个用户启动一个线程(而非各开一个进程),线程由服务器调度——比"每用户一进程"效率高得多。生命周期三过程(板书):初始化:Servlet第一次被请求加载时,服务器创建这个Servlet实例,并调用init方法完成初始化;响应请求:新诞生的Servlet调用service方法响应用户请求;后续每次请求都新起一个线程、在其中调用service;销毁:服务器关闭时调用destroy方法销毁Servlet。关键区别:init只被调用一次(首次加载时);service可能被多次调用(每次请求一次),且各次运行在不同线程中,互不干扰——因此不同线程service里的局部变量互不影响。教学提示:用"收银台"类比——整个门店只设一个收银台(Servlet实例),开张时布置一次(init);每来一位顾客就新叫一名店员(线程)到这个收银台按同一套流程(service)结账;打烊时撤台(destroy)。6.2.2/6.2.3/6.2.4三个方法(10min)init方法(可在子类重写):publicvoidinit(ServletConfigconfig)throwsServletException首次加载时调用,服务器把一个ServletConfig对象传入并保存在Servlet中(直到销毁),它负责向Servlet传递服务设置信息;传递失败会抛ServletException,Servlet无法正常工作。本章各例都写super.init(config)保证父类初始化到位。service方法(可继承或重写):publicvoidservice(HttpServletRequestrequest,HttpServletResponseresponse)
throwsServletException,IOException创建并初始化成功后,由它处理请求并返回响应;两个参数:request(封装用户请求信息)、response(用来响应)。可被多次调用,各在独立线程中执行。destroy方法(一般不重写,直接继承):publicvoiddestroy()服务器终止服务(如关闭Tomcat)时被执行,销毁Servlet。常见报错:重写init却忘了super.init(config)→某些容器下ServletConfig未保存,后续取配置失败。6.3通过JSP页面访问Servlet(20min,重点)除了直接在浏览器敲URL,更常见的是从JSP页面用表单或超链接请求Servlet,好处是:JSP负责页面显示,把数据处理交给Servlet(职责分离)。关键规则(易错点):若web.xml里url-pattern是/computeBill,则JSP中表单action或超链接href要写成computeBill(去掉开头斜杠),不能写/computeBill——否则会变成请求服务目录下的某个资源,而非该Servlet。①通过表单请求Servlet:<formaction="computeBill"></form>也可在action里带参数:<formaction="computeBill?sideA=10.66&sideB=23.9&sideC=897"></form>例6-3:JSP页面用表单提交一份"账单明细"给名为computeBill的Servlet,Servlet用正则提取其中数字求和、并按参数discount打折。JSP页面example6_3.jsp(注:源OCR有个别拼写残缺,下为技术等价的完整写法,请对照原书核对):<%@pagecontentType="text/html"%>
<%@pagepageEncoding="utf-8"%>
<style>
#textStyle{font-family:宋体;font-size:36;color:blue}
</style>
<HTML><bodybgcolor=#ffccff>
<formaction="computeBill?discount=6"id=textStylemethod=post>
输入账单:<br>
<textAreaname="billMess"id=textStylerows=5cols=30>
剁椒鱼头:62.9元,烤鸭:199元,红焖大虾:289.9元.
</textArea>
<br><inputtype=submitid=textStylevalue="提交">
</form>
</body></HTML>Servlet类Example6_3_Servlet.java:packagemoon.sun;
importjava.io.*;
importjavax.servlet.*;
importjavax.servlet.http.*;
importjava.util.regex.Pattern;
importjava.util.regex.Matcher;
publicclassExample6_3_ServletextendsHttpServlet{
publicvoidinit(ServletConfigconfig)throwsServletException{
super.init(config);
}
publicvoidservice(HttpServletRequestrequest,
HttpServletResponseresponse)throwsIOException{
request.setCharacterEncoding("utf-8");//请求体按UTF-8解码
response.setContentType("text/html;charset=utf-8");
PrintWriterout=response.getWriter();
out.println("<html><bodybgcolor=yellow>");
StringdiscountMess=request.getParameter("discount");//URL里的折扣参数
doublediscount=Double.parseDouble(discountMess);
StringbillMess=request.getParameter("billMess");//表单里的账单文本
if(billMess==null){out.print("没有账单");return;}
doublebill=getPriceSum(billMess);//账单总额
doublebillDiscount=bill*(discount/10);//打折后金额
out.print("<h2>"+billMess+"<br>消费:"+bill+"元");
out.print("优惠"+discount+"折<br>后的金额:"+billDiscount+"元");
out.print("</h2></body></html>");
}
publicdoublegetPriceSum(Stringinput){//用正则提取所有数字并求和
Patternpattern;
Matchermatcher;
Stringregex="-?[0-9][0-9]*[.]?[0-9]*";//匹配(可带负号/小数)数字
pattern=Ppile(regex);
matcher=pattern.matcher(input);
doublesum=0;
while(matcher.find()){
Stringstr=matcher.group();
sum+=Double.parseDouble(str);
}
returnsum;
}
}逐段拆解:目的:JSP收账单文本→提交给Servlet→Servlet提数字、求和、打折、回显。写法要点:①表单method=post,Servlet里先request.setCharacterEncoding("utf-8")再取参数,中文才不乱码;②discount从URL(action="computeBill?discount=6")取,billMess从表单域取;③getPriceSum用正则Matcher.find()逐个抓出数字累加。预期结果:例如账单"剁椒鱼头:62.9元,烤鸭:199元,红焖大虾:289.9元."、discount=6。数值复核:账单三项之和62.9+199+289.9=551.8元;打6折551.8×(6/10)=331.08元。页面显示"消费:551.8元优惠6折后的金额:331.08元"(教材图6.5)。对应web.xml(servlet-name=computeBill、url-pattern=/computeBill):<servlet>
<servlet-name>computeBill</servlet-name>
<servlet-class>moon.sun.Example6_3_Servlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>computeBill</servlet-name>
<url-pattern>/computeBill</url-pattern>
</servlet-mapping>②通过超链接请求Servlet:若url-pattern是/circle,超链接写<ahref="circle">…</a>(不带斜杠),也可带参数:<ahref="circle?mess=area&radius=100.8">看半径100.8圆的面积.</a>例6-4:超链接请求名为circle的Servlet,带参数mess、radius。mess=area求圆面积(返回文字),mess=geometry画圆(返回图片)。Servlet核心逻辑:publicvoidservice(HttpServletRequestrequest,HttpServletResponseresponse)
throwsIOException{
this.request=request;this.response=response;
request.setCharacterEncoding("utf-8");
Stringmess=request.getParameter("mess");
Stringradius=request.getParameter("radius");
if(mess.equals("area"))getArea(Double.parseDouble(radius));//求面积
elseif(mess.equals("geometry"))getGeometry(Double.parseDouble(radius));//画圆
}
voidgetArea(doubler)throwsIOException{
response.setContentType("text/html;charset=utf-8");
PrintWriterout=response.getWriter();
doublearea=Math.PI*r*r;
Stringresult=String.format("%.2f",area);
out.print("<h2>半径"+r+"圆的面积(保留2位小数):<br>"+result+"</h2></body></html>");
}数值复核:radius=100.8时面积π×100.8²=π×10160.64≈31921.71,String.format("%.2f",…)得31921.71,与"保留2位小数"一致。教材源码勘误提示:源中getGeometry/getImage把图像变量写成了BufferedReader——BufferedReader是字符输入流,不能承载图像。此处应为BufferedImage(如例6-2):BufferedImageimage=newBufferedImage(width,height,BufferedImage.TYPE_INT_RGB);。授课时按正确类型讲,并提醒学生对照原书这是OCR/排印讹误。互动建议:给几个URL让学生判断——:8080/ch6/lookPic?width=500&height=200会画什么?.../circle?mess=area&radius=100.8返回文字还是图片?训练"参数→行为"的映射直觉。第3课时:共享变量(6.4)+doGet与doPost(6.5)6.4共享变量(22min,难点)核心认识:Servlet是HttpServlet的子类,编写子类时声明的成员变量,会被所有请求该Servlet的用户共享(因为整个应用只有一个Servlet实例)。数学背景——莱布尼茨级数计算π:π例6-5:利用成员变量被所有用户共享的特性,实现多用户协作计算π——每有一个用户请求computePI,就让级数往前推进一项。JSP页面example6_5.jsp:一个超链接<ahref="computePI">参与计算PI的值</a>。Servlet类Example6_5_Servlet.java:packagemoon.sun;
importjava.io.*;
importjavax.servlet.*;
importjavax.servlet.http.*;
publicclassExample6_5_ServletextendsHttpServlet{
doublesum=0,i=1,j=1;//成员变量:被所有用户共享
intnumber=0;//成员变量:被所有用户共享(记录被请求次数)
publicvoidinit(ServletConfigconfig)throwsServletException{
super.init(config);
}
publicsynchronizedvoidservice(HttpServletRequestrequest,//synchronized保证线程安全
HttpServletResponseresponse)throwsIOException{
response.setContentType("text/html;charset=utf-8");
PrintWriterout=response.getWriter();
out.println("<html><bodybgcolor=cyan>");
number++;//每被请求一次,次数+1
sum=sum+i/j;//级数累加当前项
j=j+2;//分母1,3,5,7,...
i=-i;//符号+,-,+,-,...
out.println("<h1>servlet:"+getServletName()+
"已经被请求了"+number+"次");
out.println("<br>现在PI的值是:");
out.println(4*sum);//pi≈4*sum
out.println("</h1></body></html>");
}
}逐段拆解:目的:演示成员变量跨用户共享——不同用户/不同次请求共同推进同一份sum、number。写法要点:①sum、i、j、number是成员变量(写在方法外),被所有请求共享;②i/j是当前项、i=-i翻符号、j=j+2换分母,4*sum逼近π;③service加synchronized:多个线程若同时改这些共享变量会算错,加锁让请求串行修改,保证正确。预期结果:反复点链接(或多标签页刷新),"被请求次数"逐次累加,"现在PI的值"越来越接近3.14159…(教材图6.7)。数值复核:级数收敛较慢——第1次请求4×1=4;2次4×(1-1/3)≈2.667;3次4×(1-1/3+1/5)≈3.467;随请求次数增大在π附近振荡收敛。请求上万次后4*sum≈3.14159(已用Python核验),与"多用户共同逼近π"的设计一致。教学提示:现场用两三个浏览器标签页轮流刷新computePI,让学生看到同一个计数被大家累加——这就是"共享成员变量"的直观证据。再去掉synchronized、用脚本并发压测(可选演示),观察计数/求和可能出错,讲清线程安全的必要性。这也是2.4思政"并发与责任意识"的落点。对比强调:service里若声明的是局部变量(如例6-3的bill),则各线程各一份、互不干扰;而成员变量是大家共用的。"共享or隔离,取决于变量声明在方法外还是方法内。"6.5doGet和doPost方法(20min,重点)为什么有doGet/doPost:HttpServlet除init/service/destroy外,还有两个重要方法doGet和doPost,用来处理请求。其实父类的service方法会先检查请求类型(GET/POST等),再据此分派调用doGet或doPost。因此写Servlet子类时,可以不重写service,改为重写doGet/doPost:若GET、POST处理完全相同:只在其中一个方法写逻辑,另一个方法里调用它即可(如doGet里doPost(request,response););若要按请求方式区别处理:在两个方法里分别写不同逻辑(这比只重写service更灵活)。例6-6:JSP页面有两个表单,都请求名为sumORproduct的Servlet并提交一串数字;一个表单用method=post、一个用method=get。POST时求各数字的代数和,GET时求各数字的乘积。Servlet关键代码:publicvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse)
throwsServletException,IOException{
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
PrintWriterout=response.getWriter();
out.println("<html><bodybgcolor=cyan>");
Strings=request.getParameter("number");
String[]a=s.split("[,,]+");//按中英文逗号切分
doublesum=0;
for(Stringitem:a)if(item.length()>=1)sum+=Double.parseDouble(item);
out.print("<h2>用户的请求方式是"+request.getMethod()+"<br>");//打印GET/POST
for(Stringitem:a)if(item.length()>=1)out.print(item+"");
out.print("<br>的和是"+sum);
out.println("</h2></body></html>");
}
publicvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse)
throwsServletException,IOException{
//与doPost类似,但把sum求和改为product求积
Strings=request.getParameter("number");
String[]a=s.split("[,,]+");
doubleproduct=1;
for(Stringitem:a)if(item.length()>=1)product*=Double.parseDouble(item);
//...输出"的乘积是"+product
}逐段拆解:目的:用同一个Servlet、同一参数number,靠请求方式(GET/POST)决定是求和还是求积。写法要点:①表单method=post→触发doPost;method=get→触发doGet;②request.getMethod()可打印出实际请求方式;③split("[,,]+")用正则同时兼容中英文逗号与连续分隔符。预期结果:例如输入2,3,4——POST提交显示"…的和是9.0",GET提交显示"…的乘积是24.0"(教材图6.8)。数值复核:2,3,4→和2+3+4=9、积2×3×4=24,与页面输出一致。常见报错:只重写了doPost,用户却用GET访问→由父类默认doGet响应(可能报405/无预期结果);getParameter("number")为null再split→空指针。互动建议:让学生说出"表单默认是什么请求方式?地址栏能看到参数的是GET还是POST?"厘清GET(参数在URL、可见、有长度限制)与POST(参数在请求体、不可见、适合大/敏感数据)的差异。第4课时:重定向与转发(6.6)+使用session(6.7)6.6重定向与转发(22min,重点+难点)两个概念:重定向:将用户从当前页面/Servlet定向到另一个页面/Servlet;转发:将用户对当前页面/Servlet的请求转交给另一个页面/Servlet。①sendRedirect方法(重定向)——HttpServletResponse类的方法:response.sendRedirect("example6_7.jsp");//重定向到某页面它只把用户"引导"到新资源,不能把当前请求(request对象)传过去——所以重定向的目标无法用request取到用户先前提交的数据。②forward方法(转发)——RequestDispatcher类的方法,两步://第1步:由request获取转发器(参数是目标页面/Servlet的URL)
RequestDispatcherdispatcher=request.getRequestDispatcher("target.jsp");
//第2步:转发(把当前request、response一并交给目标)
dispatcher.forward(request,response);转发把同一个request传给目标,所以目标可以用request取到用户提交的数据。二者区别(板书对照表,核心难点):对比项转发forward重定向sendRedirect谁发起请求服务器内部转交同一次请求让浏览器再发一次新请求浏览器地址栏不变(仍是原地址)变为目标地址目标能否用原request取数据能不能刷新页面时重发的是原请求重发的是新(目标)请求执行时机立即结束当前Servlet后续执行先执行完当前Servlet代码再跳转(若其后紧跟return则立即结束)使用转发的好处(架构思想):可让JSP页面与处理数据的Servlet解耦——页面只跟"负责转发的Servlet"打交道;对数据的多种处理需求可分派给多个Servlet,再写一个Servlet负责接收请求并按情况转发。更新某需求时只需重编相应Servlet,利于维护。(呼应2.4思政"架构分层与解耦"。)例6-7:example6_7.jsp表单里输入一组数字,提交给负责转发的ServlethandleForward:单击含sort的按钮就转发给sortServlet(排序);单击含sum的按钮就转发给sumServlet(求代数和);若没输入数字,就重定向回输入页。转发Servlet核心:publicvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse)
throwsServletException,IOException{
RequestDispatcherdispatcher=null;
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
Stringmess=request.getParameter("submit");//哪个按钮
StringdigitData=request.getParameter("digitData");//输入的数字
if(digitData==null||digitData.length()==0){
response.sendRedirect("example6_7.jsp");//没输入→重定向回输入页
return;
}
if(mess.contains("sort")){
dispatcher=request.getRequestDispatcher("sort");//转发给sort
dispatcher.forward(request,response);
}
elseif(mess.contains("sum")){
dispatcher=request.getRequestDispatcher("sum");//转发给sum
dispatcher.forward(request,response);
}
}逐段拆解:目的:一个"总入口"Servlet按用户选择把请求转发给不同处理Servlet;无数据则重定向回填。写法要点:①没数据用sendRedirect回输入页(浏览器地址栏会变为输入页);②有数据用getRequestDispatcher(名字).forward(...)转发,sort/sum两个Servlet用同一request取到digitData再处理;③sum用正则求代数和、sort用TreeSet<Double>自动升序去重。预期结果:输入"5,-2,8"点"求和"→显示"数字的代数和:11.0";点"排序"→显示"排序后的数字:-2.0,5.0,8.0"(教材图6.9)。数值复核:5,-2,8代数和5+(-2)+8=11;TreeSet升序为-2.0,5.0,8.0,与预期一致。教材源码勘误提示:源中出现requestcrisisDispatcher("sort")等,系OCR讹误,正确应为request.getRequestDispatcher("sort")(如上)。授课按正确写法讲解。教学提示(两次对比演示):同一操作,先走转发(成功路径)——观察地址栏仍是.../handleForward、结果正确;再故意走重定向到处理Servlet(反例)——地址栏变了、且目标取不到digitData(数据丢失)。让学生亲眼看到"地址栏变不变"与"数据能否续用"的因果。6.7使用session(20min,重点)为什么需要session:HTTP是无状态协议——用户发请求、服务器响应后,同一用户再次请求时服务器"不认识"他了,不会记录用户信息。为在多次请求间"记住"用户,服务器在用户访问某服务目录期间为其分配一个session对象(会话),可在各页面与各Servlet中用它记录信息;服务器保证不同用户的session互不相同,同一用户在不同页面/Servlet取到的session完全相同。在Servlet中获取session:HttpSessionsession=request.getSession(true);//取当前用户的会话对象(无则新建)在JSP中则可直接用内置对象session(如session.setAttribute("key",value)/session.getAttribute("key"))。例6-8(猜字母游戏):访问/刷新example6_8.jsp时随机分配一个英文字母(不区分大小写)存入用户session;用户到输入页猜字母、提交给名为guess的Servlet;Servlet比较后把"猜小了/猜大了/猜对了"存进session,再重定向回输入页显示提示。JSP出题页example6_8.jsp核心:<%session.setAttribute("message","请您猜字母");
session.setAttribute("count","0");
chara[]=newchar[26];intm=0;
for(charc='a';c<='z';c++){a[m]=c;m++;}
intrandomIndex=(int)(Math.random()*a.length);
charch=a[randomIndex];//随机取一个字母
session.setAttribute("savedLetter",newCharacter(ch));//存入session%>
<ahref="example6_8_input.jsp">去猜字母</a>判分ServletExample6_8_Servlet.java核心:publicvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse)
throwsServletException,IOException{
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
HttpSessionsession=request.getSession(true);//取用户会话
Stringstr=request.getParameter("clientGuessLetter");
CharacterguessLetter=str.trim().charAt(0);//用户猜的字母
Stringcount=(String)session.getAttribute("count");
intn=Integer.parseInt(count);
CharactersavedLetter=(Character)session.getAttribute("savedLetter");//会话里的答案
charrealLetter=savedLetter.charValue();
if(Character.isUpperCase(guessLetter))//统一转小写比较
guessLetter=Character.toLowerCase(guessLetter);
if(guessLetter<realLetter){
n++;
session.setAttribute("message","第"+n+"次,猜小了");
session.setAttribute("count",""+n);
response.sendRedirect("example6_8_input.jsp");//重定向回输入页
}
elseif(guessLetter>realLetter){
n++;
session.setAttribute("message","第"+n+"次,猜大了");
session.setAttribute("count",""+n);
response.sendRedirect("example6_8_input.jsp");
}
else{//猜对
n++;
session.setAttribute("message","猜对了,猜的次数:"+n);
session.setAttribute("count","0");
response.sendRedirect("example6_8_input.jsp");
}
}
publicvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse)
throwsServletException,IOException{doPost(request,response);}逐段拆解:目的:用session在"出题页→输入页→判分Servlet→输入页"多次请求间保存"答案字母"与"猜测次数"。写法要点:①出题页把savedLetter、count、message放进session;②判分Servlet用request.getSession(true)
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 中小学校园网络运行维护标准手册
- 诚实守信的品格培养:小学主题班会课件
- 养成健康习惯打造阳光心态一年级主题班会课件
- 魅力中华文化:汉字演变小学主题班会课件
- 项目管理计划制定与执行全流程指南
- 2026肿瘤细胞系正规厂家大盘点 科研级细胞机构适配筛选指南 签约避坑常见问题全解析
- 新零售物流中心选址评估商洽函(6篇)
- 网络安全防护策略与技术实现方案手册
- 活动现场布置要求联系函(6篇)范文
- 招1人【招聘】西宁市城北区火车西站社区卫生服务中心财务岗位招聘模拟试卷带答案详解(达标题)
- 2026广西梧州供电局项目资料员招聘37人考试备考题库及答案详解
- 房地产企业资质申报:质量保证体系情况说明
- 气切病人脱机训练
- 2026心理危机干预课件
- 内衣采购员管理制度
- 特种设备作业人员资格复审申请表
- 2025年肇庆学院辅导员招聘考试真题汇编附答案
- 国企贸易内控制度
- 小学群文阅读培训课件
- 2025银行合规管理岗位考试真题及答案
- 2026年企业所得税关联交易定价原则应用与转让定价文档准备指南
评论
0/150
提交评论