1. Servlet规范介绍
处理请求和发送响应的过程是由Servlet完成的,在Servlet规范中,指定http服务器调用动态资源文件规则;指定http服务器管理动态资源文件示例对象规则。
2. tomcat和Servlet的关系
Tomcat是Web应用服务器,是一个Servlet/JSP容器。Tomcat作为Servlet容器,负责处理客户请求,把请求传送给Servlet,并将Servlet的响应传回给客户。Servlet是Web应用程序中的一个组件,扩展了Java Web服务器功能。
在浏览器请求时,由tomcat将http请求文本接收并解析,然后封装成HttpServletRequest类型的request对象,所有Http请求头数据可以通过request对象调用对应的方法查询到。
在对浏览器响应时,会把响应信息封装到HttpServletRespons类型的respon对象,通过设置response属性就可以控制要输出到浏览器的内容,然后将response交给tomcat,tomcat就会将其变成响应文本的格式发送给浏览器。
3. 一个简单的Servlet
HttpServletResponse(响应对象)应用
分别创建4个servlet,配置web.xml文件,在浏览器中访问对应url
OneServlet
TwoServlet
ThreeServlet
FourServlet
综上HttpServletRespson对象的作用:
(1)通过响应对象获取标准输出流,把执行结果以二进制形式写入响应体中(write()和print()区别);
(2)通过响应对象设置content-type,控制浏览器使用对应编译器编译响应体的二进制数据;
(3)设置location,控制浏览器向指定服务器发送请求;
HttpServletRequest(请求对象)应用
OneServlet
在TwoServlet前,先写一个html,使用超链接访问TwoServlet,这样可以使用TwoServlet来获取参数信息
two.html
TwoServlet
在ThreeServlet前同样先写一个three.html,在three.html中分别使用get和post方法访问ThreeServlet,在ThreeServlet中分别使用doGet和doPost来获取参数信息。
ThreeServlet
在使用request获取请求参数时,当请求信息是英文信息时使用get和post都可以正常获取,但是当请求信息是中文信息时get也可以正常获取,post获取的请求参数信息就是乱码了。
这是因为当浏览器以get方式发送请求时,请求参数保存在请求头,在http请求协议包到达http服务器后由tomcat解码,tomcat默认使用utf-8解码,可以解码中文;当浏览器以post方式发送请求时,请求参数保存在请求体,请求体二进制由request解码,request默认使用ISO-8859-1解码,不能解码中文。
综上HttpServletRequest对象的作用:
(1)获取http请求协议包中的请求行信息;
(2)获取http请求协议包中的请求参数信息;
4. Servlet接口实现类和生命周期
接口实现类
查看自定义OneServlet类的继承关系,可以看出自定义Servlet继承抽象类HttpServlet,HttpServlet继承抽象类GenericServlet,GenericServlet实现了Servlet接口,通过两个抽象类简化了Servlet的编写。
而处理请求和响应其实是在HttpServlet中的Service()方法中,先判断请求方式,然后调用对应的方法执行,所以我们在编写Servlet时,可以直接重写doGet和doPost方法就可以了,不用在重写service方法了。
Servlet实例创建
(1)默认情况下,tomcat在初次发送请求后会创建servlet对象
(2)如果使用<load-on-startup>1</load-on-startup>,load-on-startup大于0(默认为0,可以不写这个标签)的情况下,则会在tomcat启动时就会自动创建servlet对象
Servlet的生命周期
(1)服务器启动发送请求后,tomcat负责初始化一个Servlet对象,也就是会调用初始化方法init()方法;
(2)创建封装Http请求的HttpServletRequest对象和Http响应的HttpServletResponse对象,然后调用Servlet的service()方法处理请求;
(3)当服务器关闭后,销毁servlet对象,调用destroy()方法;
5. 欢迎资源文件
Tomcat的欢迎资源文件规则:
(1)规则位置:tomcat安装位置/conf/web.xml
(2)规则命令:表示顺序查找标签中的文件
<welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> </welcome-file-list>
设置当前网站的默认欢迎资源文件规则
(1)规则位置:/web/WEB-INF/web.xml
(2)规则命令:同tomcat的欢迎资源文件配置
当网站设置自定义默认文件定位规则后,tomcat自带定位规则将失效
6. 状态码
302:浏览器获取状态码302后,不会在读取响应体内容,自动根据响应头中的location地址发起二次请求;
405:Servlet不能对请求方式处理(请求方式是get,但是servlet中可能只定义了post方法);
500:可以请求资源文件,也可以处理请求方式,但是请求过程中发生了异常;
400:无法请求到资源文件;
7. 多个Servlet之间调用规则
由于来自于浏览器发送的请求往往需要多个Servlet协同处理,但是浏览器一次只能访问一个Servlet,导致用户需要手动通过浏览器多次发送请求才能得到服务,增加用户访问难度,于是可以通过多个Servlet之间调度来解决这个问题,多个Servlet之间调度有两种方案。
(1)重定向 response.sendRedirect("请求地址");
在对OneServlet发送请求后,会把TwoServlet地址/myWeb/two写入到响应头的location属性中,tomcat会写入状态码302到状态行中,浏览器根据状态码信息会对响应头的location属性地址发送二次请求。
使用这种方式会在浏览器和服务器之间多次往返,大量的时间消耗在往返次数上,增加等待服务时间。
(2)请求转发 request.getRequestDispatcher("/资源文件名").forward(request,response);
在OneServlet中通过当前请求对象代替浏览器向tomcat申请资源TwoServlet,使用这种方式减少了浏览器与服务器之间的往返次数。
8. 多个Servlet之间数据共享
OneServlet工作完毕后,将产生的数据交给TwoServlet来使用
Servlet规范中提供了四种数据共享方案:
(1)ServletContext:Http服务器启动时会自动创建全局作用域对象,其他Servlet都可以从这个全局作用域对象中读取数据;
在OneServlet中我们创建全局作用域对象,并向全局作用域对象中写入数据
在TwoServlet中我们获取全局作用域对象中的数据
ServletContext全局作用域对象可以在多个Servlet之间相互共享数据。
在Http服务启动过程中,自动为当前网站在内存中创建一个全局作用域对象,并且在运行期间只有一个全局作用域对象,在http服务器关闭时,全局作用域对象会被销毁。
(2)Cookie:当用户通过浏览器初次向网站发送请求申请OneServlet,OneServlet会在运行期间创建一个Cookie存储与当前用户相关的数据,OneServlet运行完毕后,会将Cookie信息写入到响应头交给浏览器;浏览器收到响应包后,将Cookie存储在浏览器的缓存中,一段时间后用户通过同一浏览器再次向同一网站发送申请TwoServlet时,浏览器会把之前推送的cookie信息写入到请求头发送,这样TwoServlet在运行时就可以通过读取请求头中的cookie信息与OneServlet共享数据。
先写一个简单的页面
<body> <center> <font style="color: red;font-size: 40px">新会员申请开卡</font> <form action="/myWeb/one"> <table border="2"> <tr> <td>用户名</td> <td><input type="text" name="userName"></td> </tr> <tr> <td>预存金额</td> <td><input type="text" name="money"></td> </tr> <tr> <td><input type="submit" value="申请开卡"></td> <td><input type="reset"></td> </tr> </table> </form> </center> </body>
用OneServlet来处理这个页面提交的请求,在Servlet中获取请求头的用户和金额参数信息,保存在用户coookie中写入响应头中,请求重新转发到下一个页面;
public class OneServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1. 调用请求对象获取请求头参数信息 String userName = request.getParameter("userName"); String money = request.getParameter("money"); //2. 创建cookie信息 Cookie card1 = new Cookie("userName",userName); Cookie card2 = new Cookie("money",money); //3. 设置cookie的生命周期 card2.setMaxAge(60); //60ms //4. 将cookie信息写入到响应体中 response.addCookie(card1); response.addCookie(card2); //5. 请求转发到点餐页面 request.getRequestDispatcher("/index_2.html").forward(request,response); } }
<body> <center> <font style="color: red; font-size: 40px">点餐页面</font> <form action="/myWeb/two"> 食物类型:<input type="radio" name="food" value="jiaozi">饺子(30元) <input type="radio" name="food" value="miantiao">面条(20元) <input type="radio" name="food" value="gaifan">盖饭(15元)<br/> <input type="submit" value="消费"> </form> </center> </body>
用TwoServlet来处理index_2页面发送的请求信息,在Servlet中先从请求头中获取参数信息,然后从请求头中获取缓存的cookie信息,进行处理;
public class TwoServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { int jiaozi_money = 30; int gaifan_money = 15; int miaotiao_money = 20; int money = 0,xiaofei = 0,balance = 0; String userName = null; response.setContentType("text/html;charset=utf-8"); PrintWriter pw = response.getWriter(); Cookie newCard = null; //1. 获取请求头信息 String food = request.getParameter("food"); //2. 获取cookie信息 Cookie[] cookies = request.getCookies(); //3. 根据食物消费 for(Cookie cookie : cookies){ String key = cookie.getName(); String value = cookie.getValue(); if("userName".equals(key)){ userName = value; }else if ("money".equals(key)){ money = Integer.valueOf(value); if("jiaozi".equals(food)){ if(jiaozi_money > money){ pw.print("用户" + userName + "余额不足,请充值"); }else{ newCard = new Cookie("money",(money-jiaozi_money) + ""); xiaofei = jiaozi_money; balance = money - jiaozi_money; } }else if("miaotiao".equals(food)){ if(miaotiao_money > money){ pw.print("用户" + userName + "余额不足,请充值"); }else{ newCard = new Cookie("money",(money-miaotiao_money) + ""); xiaofei = miaotiao_money; balance = money - miaotiao_money; } }else if("gaifan".equals(food)){ if(gaifan_money > money){ pw.print("用户" + userName + "余额不足,请充值"); }else{ newCard = new Cookie("money",(money-gaifan_money) + ""); xiaofei = gaifan_money; balance = money - gaifan_money; } } } } //4. cookie信息返回用户 response.addCookie(newCard); //5. 返回消费记录 pw.print("用户" + userName + "本次消费" + xiaofei + ",余额 : " + balance); } }
a. 在index页面向OneServlet发送请求后,可以看到浏览器响应头cookie中已经保存了用户和金额的信息
b. 在页面2中可以发送请求后可以直接从请求头中获取浏览器缓存的cookie信息,进行数据共享
默认情况下cookies信息只保存在浏览器中,因此只要浏览器关闭cookies信息就会销毁,也可以手动设置cookies在硬盘上的存活时间,使用cookie.setMaxAge(时间)。
(3)HttpSession:如果两个Servlet来自于同一个网站,并且为同一用户/浏览器服务,可以使用HttpSession对象进行数据共享;
先写一个简单的html页面
<body> <table border="2" align="center"> <tr> <td>商品名称</td> <td>商品单价</td> <td>供货商</td> <td>加入购物车</td> </tr> <tr> <td>华为笔记本</td> <td>7000</td> <td>华为</td> <td><a href="/myWeb/one?goodsName=华为笔记本">加入购物车</a></td> </tr> <tr> <td>苹果</td> <td>10</td> <td>西安</td> <td><a href="/myWeb/one?goodsName=苹果">加入购物车</a></td> </tr> <tr> <td>衣服</td> <td>1000</td> <td>上海</td> <td><a href="/myWeb/one?goodsName=衣服">加入购物车</a></td> </tr> <tr align="center"> <td colspan="4"><a href="/myWeb/two">查看购物车</a></td> </tr> </table> </body>
用OneServlet来处理每次加入购物车的请求,在Servlet中先获取session,然后将数据保存在session中
public class OneServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1. 用请求参数获取参数信息 String goodsName = request.getParameter("goodsName"); //2. 用请求对象获取session HttpSession session = request.getSession(); //3. 加入session Integer goodsNum = (Integer) session.getAttribute(goodsName); //先获取商品的数量 if(goodsNum == null){ session.setAttribute(goodsName,1); }else { session.setAttribute(goodsName,goodsNum + 1); } } }
在TwoServlet中可以从session中获取数据
public class TwoServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1. 通过请求对象获取session HttpSession session = request.getSession(); //2. 读取session中的key Enumeration goodsNames = session.getAttributeNames(); //返回一个枚举类型 while (goodsNames.hasMoreElements()){ String goodsName = (String)goodsNames.nextElement(); int num = (int)session.getAttribute(goodsName); System.out.println("商品名:" + goodsName + " 商品数量:" + num); } } }
HttpSession销毁时机:
由于用户与HttpSession关联使用的cookie只能保存在浏览器缓存中,因此浏览器关闭时,用户与他的session也会切断;由于tomcat无法检查浏览器的关闭,因此不会导致tomcat对HttpSession的销毁,为了解决这一问题,Tomcat为HttpSession设置了空闲时间,默认30分钟,就会销毁到HttpSession。
也可以手动设置这个空闲时间:
在当前网站下/web/WEB-INF/web.xml
<!--设置session的空闲时间--> <session-config> <session-timeout>5</session-timeout> <!--设置session的空闲时间5分钟--> </session-config>
(4)HttpServletRequest:两个Servlet之间通过请求转发方式调用,因为彼此共享请求协议包,而一个请求协议包对应一个请求对象,因此两个Servlet之间可以通过请求对象实现数据共享
在OneServlet中设置请求对象的值,请求转发到TwoServlet
public class OneServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1. 把数据添加到请求对象中,作为共享数据 req.setAttribute("key1","2021/09/14"); //2. 请求转发到下一个Servlet req.getRequestDispatcher("/two").forward(req,resp); } }
在TwoServlet中可以获取到请求对象共享的值
public class TwoServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //从同一个请求作用域对象中得到共享数据 String value = (String)req.getAttribute("key1"); System.out.println("TwoServlet runing get value " + value); } }