第3章.Servlet应用

转发与重定向

转发:浏览器发送资源请求到ServletA后,ServletA传递请求给ServletB,ServletB生成响应后返回给浏览器。

请求转发:forward:将当前的request和response对象交给指定的web组件处理

这个过程中浏览器只发了一次请求接收了一次响应(浏览器并不知道转发,地址栏url不变)

1. 获取转发对象RequestDispatcher--由Servlet容器创建,用于封装由路径所标志的服务器资源;

2. 然后调用转发对象中的forward方法;(另一个方法为include()--原有的和被转发的web组件均可输出相应信息)

如何获取RequestDispatcher?两种方法:

通过HttpServletRequest获取

通过ServletContext获取(之前提到ServletContext三个作用中的一条:获取转发)

i.e. ServletForward.java将请求转发给ServletForwardExample.java

RequestDispatcher rd = request.getRequestDispatcher("/forwardExample"); // 从HttpServletRequest中获取转发对象("/forwardExample"为绝对路径)

rd.forward(request,response); // 转发

i.e. 通过ServletContext获取转发对象:两种方法

1. rd = this.getServletContext().getNamedDispatcher("ServletForwardExample");

// ("ServletForwardExample"为Servlet名称)

2. rd = this.getServletContext().getRequestDispatcher("/forwardExample");

重定向:sendRedirect

study case: 用户登录

登录验证完成后,浏览器接收到服务器发来的另外一个地址的响应信息;

浏览器自动向服务器发送跳转请求,并得到服务器返回的跳转结果。

通过response对象发送给浏览器一个新url地址,让其重新请求。

两次请求,两次响应--浏览器地址栏url改变

i.e. ServletRedirect.java将请求重定向到ServletRedirectExample.java

ServletRedirect.java中:

response.sendRedirect("redirectExample"); // ("redirectExample"为相对路径)

若使用绝对路径"/redirectExample",则重定向到地址localhost:8080/redirectExample

-- 若想重定向到当前web项目的某资源:相对路径;若想到其他web项目:绝对路径。

ServletRedirectExample.java中:

syso: request.getParameter("user");

// 若直接使用传来的request进行读取数据等操作,错误:两次请求和两次响应

在Chrome的developer tools中,可以看到两次请求:

redirect?user=aaa -- response header: location:....../redirectExample

redirectExample。

转发&重定向总结:

浏览器地址栏变化:转发不变;重定向变化。

请求范围:在同一个web项目中转发;重定向可在不同web项目间重定向

请求过程:转发为一次,重定向为两次

过滤器与监听器

过滤器 filter:

通过自定义的过滤规则来过滤请求与响应。

用于对用户请求进行预处理、和对请求响应进行后处理的web引用组件。

对Servlet容器进行请求和响应的对象进行检查和修改。

过滤器本身并不生成请求和响应对象,只是提供了过滤的功能。

过滤器在Servlet被调用之前检查request对象,并能够修改request header和request的内容;

在servlet被调用之后,能够检查response对象,并能够修改response header和response的内容。

过滤器工作原理:

客户端发送原始请求到Servlet容器,该请求在到达容器之前会经过过滤器处理,过滤之后请求转发到对应Servlet;Servlet处理完请求后进行了响应,该原始响应发还到过滤器,最后由过滤器将过滤后的请求返回给客户端。

过滤器使用场景:

用户认证:过滤一部分非法用户,验证用户是否已经登录,是否拥有访问权限等

编解码处理:如果请求有乱码,可以通过过滤器进行预处理,以得到正确的编码结果

数据压缩处理:当请求数据较大时,对请求进行压缩,以减轻服务端的处理压力

过滤器的生命周期:创建和销毁是由servlet容器负责的

servlet容器根据部署描述符创建filter的实例对象(只会创建一次);

调用init();完成初始化工作,为拦截用户请求做好准备(同样只会调用一次);

(和servlet一样,可以配置一个filterConfig的对象,用来存储filter的配置信息)

初始化完成后,进入正式的过滤操作,doFilter()(类似于servlet中的service()):对请求和响应做实际处理

当客户端请求/响应与过滤器相关联的url时,过滤器会执行对应的doFilter()方法。

在web应用被移除或服务器停止时,会调用destroy()(只会调用一次,将过滤器资源释放)销毁filter对象。

i.e. TestFilter.java

new class--name:TestFilter.java--Interface:Filter(servlet)

--init(FilterConfig); doFilter(); destroy();

配置web.xml:

<filter>

<init-param>   <--!由filterConfig.getInitParameter(param-name)读取-->

<param-name>filterParam</param-name>

<param-value>1</param-value>

</init-param>

<filter-name>TestFilter</filter-name>

<filter-class>com.netease.server.example.web.controller.TestServlet</filter-class>

<filter>

<filter-mapping>

<filter-name>TestFilter</filter-name>

<url-pattern>/hello/world/*</url-pattern>

<--! 符合该url pattern的请求才会经过该过滤器-->

</filter-mapping>

在刚才创建的TestFilter.java中完善三个方法:

public void init(FilterConfig filterConfig) throws ServletException {
// 获取在web.xml中配置的filter参数并打印
String value = filterConfig.getInitParameter("filterParam");
syso(value);
} public void doFilter(ServletRequest request, ServletResponse response,
  FilterChain chain) throws IOException, ServletException {
// 实现登陆过的用户可直接进行访问,否则会跳转到登录页面进行登陆步骤
HttpServletRequest req = (HttpServletRequest) request; // 强制类型转换
HttpSession session = req.getSession(); // 得到会话
// 检查属性,判断是否登陆过
if (session.getAttribute("userName") == null) {
HttpServletResponse res = (HttpServletResponse) response;
res.sendRedirect("../index.html"); // 跳转到登陆页面
} else {
// 访问对应资源
chain.doFilter(request, response);// 请求传递到下一个过滤器或是对应的Servlet
}
}

部署,访问/hello/world此时用户为未登录状态,重定向到index.html;

登陆后在地址栏输入/hello/world不进行重定向,直接返回响应。

过滤器链:

若一个请求通过filter-mapping匹配到多个filter,web服务器会根据部署描述符中的先后顺序决定调用顺序。

FilterChain chain.doFilter(req, res);

监听器:listener

监听事件发生,在事件发生前后能够做出相应处理的web应用组件

事件源:当有相应事件发生时,事件源将通知发送到对应的监听器

监听器:向事件源进行注册

处理:监听器收到通知后,进行对应操作

Servlet监听器不是直接注册到事件源上,而是由servlet容器进行注册。开发人员只需配置好即可。

监听器分类:按监听对象划分

监听应用程序环境 (ServletContext): ServletContextListener, ServletContextAttributeListener

监听用户请求对象 (ServletRequest): ServletRequestListener, ServletRequestAttributeListener

监听用户会话对象 (HTTPSession): HttpSessionListener, HttpSessionAttributeListener, HttpSessionActivationListener (监听session在写入磁盘或从磁盘中重新加载到jvm中时) HttpSessionBindingListener (在session对象进行调用attribute方法和removeAttribute方法时)

两大类:对象本身的监听器 (对象的创建和销毁时);对象属性的监听器 (当属性有增删改查时)

监听器应用场景:

应用监听:每一个用户对应一个session,对session进行监听,可以做到对用户登录的统计

任务触发:如在招聘网站某用户的简历状态更新了,比如变成了面试成功,便可以向应聘者发送一封邮件通知

监听器启动顺序:

与过滤器顺序一样,根据部署描述符中的先后顺序决定。

i.e. TestListener.java--interface (很多选项,如HttpSessionAttributeListener, ServletContextListener, ServletRequestListener)

@Override:

requestDestroyed(ServletRequestEvent);

requestInitialized(ServletRequestEvent);

contextInitialized(ServletContextEvent);

contextDestroyed(ServletContextEvent);

attributeAdded(HttpSessionBindingEvent);

attributeRemoved(HttpSessionBindingEvent);

attributeReplaced(HttpSessionBindingEvent);

web.xml:在部署描述符中配置这些listener:

<listener>
<listener-class>com.netease.server.example.web.controller.listener.TestListener</listener-class>
</listener>

listener自动被注册到相应事件,

如session.setAttribute("userName", userName); 时触发HttpSessionAttributeListener.attributeAdded

Servlet容器先创建各种监听器;再创建过滤器;最后创建Servlet对象。

Servlet并发处理

当有多个客户端同时访问服务器的同一个Servlet时:串行处理(效率低),并发处理(Servlet采用)

Servlet容器接收到来自客户端的请求后;

将请求发给调度器,由调度器进行统一的请求派发;

调度器从Servlet容器的线程池 (记得Tomcat的线程池吗) 中选取一个工作线程,把请求派发给该线程,由该线程执行servlet的service方法;

此时Servlet容器又接收到了另一个请求;

调度器从工作线程池中选出另外一个工作组线程来服务新的请求;

容器并不关心请求访问的是否为同一个servlet,当容器收到对同一个servlet的多个请求时,这个servlet的service方法会在多线程中并发执行

当线程处理完请求后,会被放回线程池中;

若线程池中的线程都被占用,且有新的请求过来,则这些新请求会做排队处理

Servlet容器也会配置一个最大排队数量,如果超过这个数量,Servlet容器将会拒绝响应的请求。

Servlet并发处理的特点:

单实例:不管有多少请求,只有一个Servlet实例对象

多线程:同时处理多个请求

线程不安全:单个实例却有多个线程同时访问:Sync问题

若要实现线程安全:

变量的线程安全:

参数变量本地化--局部变量

使用同步块:synchronized加锁处理--注意要尽可能缩小加锁的代码块

属性的线程安全:

ServletContext可以多线程读取--线程不安全:需要做同步处理

HTTPSession理论上线程安全,只能在处理同一个请求的线程中被访问

但是当用户打开属于同一个session的多个浏览器窗口时,对同一个session会进行多次请求,会分配给多个线程处理:需要同步处理

ServletRequest是线程安全的,因为对于同一个请求,只有一个线程进行处理

要避免在Servlet中创建线程:servlet本身被多线程执行,会导致情况复杂。

多个Servlet同时访问外部对象时需要加锁处理

写代码时尽量避免使用servlet实例变量,无法避免时则需要加上同步处理,而保证性能安全需要缩小同步处理的范围。

i.e. 线程不安全

ConcurrentServlet.java superclass: HttpServlet

@Override: init(); doGet(); destroy();

String name;

protected void doGet(HttpServletRequest req, HttpServletResponse resp)
// 该servlet功能,读取并打印username
throws ServletException, IOException {
name = req.getParameter("username");
PrintWriter out = resp.getWriter();
out.println(name);
}

web.xml部署描述符中配置该servlet。

正常情况下该servlet运行没问题。当多个请求同时访问时,name这个实例变量就很可能出现错误。

在out.println()之前加入Thread.sleep(5000);后:

问题演示--实例变量的线程不安全:

1. 在地址栏打入/concurrent?username=ddd后,页面等待五秒,返回username ddd,正常

2. 在地址栏打入/concurrent?username=aaa后,页面等待五秒,返回username aaa,正常

3.在地址栏打入/concurrent?username=ddd, 立即新开窗口打入/concurrent?username=aaa,两个页面等待后的输出均为username aaa。

bugfix:加上synchronize同步块:

String name;

protected void doGet(HttpServletRequest req, HttpServletResponse resp)
// 该servlet功能,读取并打印username
throws ServletException, IOException {
synchronsized(this) {
name = req.getParameter("username");
PrintWriter out = resp.getWriter();
out.println(name);
}
}

保证了实例变量的线程安全。

Servlet应用测试

本次得分为:36.00/36.00, 本次测试的提交时间为:2017-08-10, 如果你认为本次测试成绩不理想,你可以选择再做一次。
1单选(2分)

下面哪个方法不是过滤器的生命周期中的方法?

  • A.doFilter
  • B.init
  • C.destroy
  • D.service2.00/2.00
2单选(2分)

下面哪项说法是错误的?

  • A.客户端的请求可以交由多个过滤器处理
  • B.在部署描述符中,如果过滤器定义在监听器前,则容器会先初始化过滤器2.00/2.00
  • C.过滤器和监听器都是Web服务端应用组件
  • D.ServletRequestAttributeListener是属于EventListeners
3单选(2分)

下面说法正确的是?

  • A.ServletContext的属性是线程安全的
  • B.HttpSession的属性是理论上线程安全2.00/2.00
  • C.其它选项都是正确的
  • D.ServletRequest的属性不是线程安全
4单选(2分)

下面获取请求转发对象(RequestDispatcher)方法错误的是?

  • A.通过ServletRequest对象的getNamedDispatcher方法直接获取2.00/2.00
  • B.通过ServletContext对象的getNamedDispatcher的方法获取
  • C.通过ServletRequest对象的getRequestDispatcher方法直接获取
  • D.通过ServletContext对象的getRequestDispatcher方法直接获取
5单选(2分)

下面关于Web应用组件启动顺序的说法错误的是?

  • A.其它选项都不正确2.00/2.00
  • B.Web应用组件启动过程中,过滤器的优先级高于Servlet
  • C.Web应用组件启动过程中,监听器的优先级高于过滤器
  • D.Web应用组件启动过程中,监听器的优先级高于Servlet
6多选(3分)

下面哪些是Servlet并发处理的特点?

  • A.线程不安全1.00/3.00
  • B.单实例1.00/3.00
  • C.线程安全
  • D.多线程1.00/3.00
7多选(3分)

下面哪些方法是可以做到Servlet线程安全?

  • A.注意Servlet中属性的线程安全0.75/3.00
  • B.尽量避免在Servlet中创建线程0.75/3.00
  • C.注意Servlet中声明的变量的线程安全,尽量不要使用实例变量0.75/3.00
  • D.多个Servlet访问的外部对象需要加锁处理0.75/3.00
8判断(2分)

请求转发是一次请求,一次响应

  • A.×
  • B.√2.00/2.00
9判断(2分)

请求重定向是两次请求,两次响应

  • A.×
  • B.√2.00/2.00
10判断(2分)

请求转发的过程中,浏览器的地址栏会发生变化

  • A.√
  • B.×2.00/2.00
11判断(2分)

请求重定向的过程中,浏览器的地址栏不会发生变化

  • A.√
  • B.×2.00/2.00
12判断(2分)

请求转发可以跨不同的Web应用程序

  • A.√
  • B.×2.00/2.00
13判断(2分)

请求重定向可以跨不同的Web应用程序

  • A.×
  • B.√2.00/2.00
14判断(2分)

请求重定向中第二次请求的完成是由浏览器自动完成的

  • A.×
  • B.√2.00/2.00
15判断(2分)

当存在多个监听器的时候,其初始化顺序是按照部署描述符中定义的顺序来初始化监听器的

  • A.×
  • B.√2.00/2.00
16判断(2分)

当多个客户端(一定数量)同时请求同一个Servlet的时候,容器可以同时处理

  • A.√2.00/2.00
  • B.×
17判断(2分)

监听器可以分为EventListeners和LifecycleListeners两种类型

  • A.×
  • B.√2.00/2.00

Java开发工程师(Web方向) - 02.Servlet技术 - 第3章.Servlet应用的更多相关文章

  1. Java开发工程师(Web方向) - 02.Servlet技术 - 第4章.JSP

    第4章--JSP JSP JSP(Java Server Pages) - 中文名:Java服务器页面 动态网页技术标准 JSP = Html + Java + JSP tags 在服务器端执行,返回 ...

  2. Java开发工程师(Web方向) - 02.Servlet技术 - 第1章.Servlet

    第1章--Servlet Servlet简介 Servlet应用于? 浏览器发出HTTP请求,服务器接收请求后返回响应给浏览器. 接收请求后到返回响应之间: 服务器将请求对象转交给Servlet容器 ...

  3. Java开发工程师(Web方向) - 02.Servlet技术 - 期末考试

    Servlet课程考试 Servlet课程考试 Servlet课程考试 总分:55分 限定时间:120分钟 进入考试 答案已成功提交!请耐心等待成绩公布 Servlet课程考试: 1(12分) 简单谈 ...

  4. Java开发工程师(Web方向) - 04.Spring框架 - 第1章.Spring概述

    第1章.Spring概述 Spring概述 The Spring Framework is a lightweight solution and a potential one-stop-shop f ...

  5. Java开发工程师(Web方向) - 02.Servlet技术 - 第2章.Cookie与Session

    第2章--Cookie与Session Cookie与Session 浏览器输入地址--HTTP请求--Servlet--HTTP响应--浏览器接收 会话(session):打开浏览器,打开一系列页面 ...

  6. Java开发工程师(Web方向) - 04.Spring框架 - 第3章.AOP技术

    第3章--AOP技术 Spring框架 - AOP概述 笔记https://my.oschina.net/hava/blog/758873Spring框架 - AOP使用 笔记https://my.o ...

  7. Java开发工程师(Web方向) - 04.Spring框架 - 第2章.IoC容器

    第2章.IoC容器 IoC容器概述 abstract: 介绍IoC和bean的用处和使用 IoC容器处于整个Spring框架中比较核心的位置:Core Container: Beans, Core, ...

  8. Java开发工程师(Web方向) - 04.Spring框架 - 第5章.Web框架

    第5章--Web框架 Web框架概述 Web框架单元测验 本次得分为:13.50/15.00, 本次测试的提交时间为:2017-09-25 1单选(2分) 关于Spring MVC中Dispatche ...

  9. Java开发工程师(Web方向) - 04.Spring框架 - 第4章.数据访问

    第4章--数据访问 Spring JDBC DAO (Data Access Object) 实现数据访问相关接口(接口和实现分离) ORM (Object Relation Mapping) 对象关 ...

随机推荐

  1. 【luogu P3623 [APIO2008]免费道路】 题解

    题目链接:https://www.luogu.org/problemnew/show/P3623 说是对克鲁斯卡尔的透彻性理解 正解: 先考虑加入水泥路,然后再考虑加入剩下必须要加入的最少鹅卵石路. ...

  2. 友盟分享——Android App接入微信开放平台注意事项

    一.Android第三方应用接入微信开放平台的注意事项: 1. 到微信开放平台官网申请正式的AppID(需通过审核),要填写包名.app签名的md5值.至于如何获取app签名信息,官方提供签名包apk ...

  3. Oracle 体系结构三 后台进程

    实例后台进程在启动实例时启动,在终止实例时终止运行. SMON SMON(system monitor)起初的任务是安装和打开数据.SMON通过查找和验证数据库控制文件来安装数据库.此后,它通过查找和 ...

  4. IOS 文件名获取简洁方式

    //这里有一个模拟器沙盒路径(完整路径) NSString* index=@"/Users/junzoo/Library/Application Support/iPhone Simulat ...

  5. CF1066CBooks Queries(数组的特殊处理)

    题意描述 您需要维护一个数据结构,支持以下三种操作: L id:在现在序列的左边插一个编号为id的物品 R id:在现在序列的右边插一个编号为id的物品 ? id:查询该点左面有几个元素,右面有几个元 ...

  6. 同步请求和异步请求的区别(理解ajax用)

    同步请求:发送方发送数据包后,等待接收方发回响应之后,才能发送下一个数据包的通信方式. 异步请求:发送方发送数据包后,不用等待接收方发回响应,就可以发送下一个数据包的通信方式. 同步通信:要求通信双方 ...

  7. H5新增的标签以及改良的标签

    1>OL标签的改良 start type  reversed:翻转排序 2>datalist标签自动补全的使用 3>progress标签的使用:进度条 4>meter标签的应用 ...

  8. button onclick实现跳转的常用方法

    1.onclick="javascript:window.location.href='aa.htm' " 2.onclick="location='URL' " ...

  9. 干货分享:QQ群排名霸屏优化规则靠前的新技术

    谈起QQ群排名的优化规则,很多人又爱又恨,原因很简单,爱他的都是引流效果是非常好的,通过关键词搜索排名好的技术,能排到全国默认前三,叫人怎能不爱他,恨的原因也恨简单,无论你的群完善的再怎么好,好像都无 ...

  10. 将图片绘制到画布上:imagecopy()

    <?php //1. 绘制图像资源(创建一个画布) $image = imagecreatetruecolor(500, 300); //2. 先分配一个绿色 $green = imagecol ...