博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Servlet技术 - Servlet应用
阅读量:6819 次
发布时间:2019-06-26

本文共 11981 字,大约阅读时间需要 39 分钟。

  hot3.png

#转发与重定向 浏览器把请求发送给ServletA,ServletA把请求传递给ServletB,由ServletB进行继续处理,最后输出资源响应。

输入图片说明

#转发 ##请求转发

  • forward
    ServletA调用forward方法把请求转发给ServletB
  • 将当前的request和response对象交给指定的web组件处理
    浏览器不知道ServletA转发请求给了ServletB,对于浏览器来说发出一次请求,获取一次响应
  • 一次请求,一次响应
    请求转发过程中,浏览器URL地址栏不会发生变化

##转发对象

  • RequestDispatcher对象
    由Servlet容器创建,用来封装一个由路径所标示的服务器资源,该对象有两个比较重要的方法forward方法和include方法,forward方法是指转发,include方法指包含,把请求转发后,原有组件和新组件都输出响应信息。 ###通过两种方式获取转发对象
  • 通过HttpServletRequest获取
  • 通过ServletContext获取

##转发实例 ###通过request.getRequestDispatcher 转发路径:注意这里是转发Servlet路径,可以填写绝对路径和相对路径的

@Override	protected void doGet(HttpServletRequest req, HttpServletResponse resp)			throws ServletException, IOException {		// 通过request对象获取转发对象		RequestDispatcher requestDispatcher = req.getRequestDispatcher("/forwardExample");		// 转发                requestDispatcher .forward(req, resp);	}

通过URL进行访问与测试

http://localhost:8080/web_project_template/forward?user=123

输出结果

Class ServletForward Method initClass ServletForward Method doGet [request]:org.apache.catalina.connector.RequestFacade@5f36101bClass ServletForwardExample Method initClass ServletForwardExample Method doGet [request]:org.apache.catalina.core.ApplicationHttpRequest@51eeeb5b

可以看到实现了转发功能并显示出ServletForwardExample的返回结果

###通过ServletContext获取转发对象 ServletContext有两种方式获取转发对象,通过this.getServletContext().getNamedDispatcher是需要知道Servlet名称(备注:在web.xml查看)。通过this.getServletContext.getRequestDispatcher 只能够填写绝对路径

// ServletContext可以通过两种方式获取转发对象		requestDispatcher = this.getServletContext().getNamedDispatcher("ServletForwardExample");		requestDispatcher = this.getServletContext().getRequestDispatcher("/forwardExample");

##用户登录流程 浏览器发送登录请求给服务器,服务器返回登录响应,在登录验证完成之后,我们通常会发现我们的浏览器会跳转到另外的页面。而且浏览器上的地址栏也改变了。这里我们做了一次请求,在我们登录验证完成之后,服务器端向浏览器返回了另外一个URL地址的响应信息。浏览器在接收到该响应信息后,会自动的向服务器请求返回地址,然后服务器端会返回对应的跳转结果。浏览器进入到另外一个页面。

输入图片说明

#请求重定向 服务器是希望用户在登录后,进入到用户界面。也就是说服务器端希望ServletA处理结束,ServletB继续为用户服务。

  • sendRedirect方法
    ServletA调用sendRedirect方法,将客户端的请求重定向到ServletB
  • 请求重定向:通过response对象发送给浏览器一个新的URL地址,让其重新请求
  • 两次请求,两次响应
    过程对用户是透明的,浏览器默认把第二次请求做掉了,需要注意,在请求重定向后,浏览器地址栏会发生响应的改变。

##请求重定向实例 可以使用绝对路径,或者相对路径

resp.sendRedirect("redirectExample");

浏览器访问路径

http://localhost:8080/web_project_template/redirect?user=123

Chrome开发者模式,可以看到有两次应答

输入图片说明

注意:请求转发是同一个请求对象,只进行一次响应;请求重定向是两次请求,两次响应。如果在跳转中的URL并没有对应的parameter,则获取的值为空。

通过Chrome查看第一次响应的Location内容
输入图片说明

###重定向绝对路径问题

resp.sendRedirect("/redirectExample");

如果这样填写重定向的绝对路径,如果使用maven启动,或者在部署时,并不是部署到ROOT。则会发生访问的路径为

http://localhost:8080/redirectExample

而不是,我们所需要的

http://localhost:8080/web_project_template/redirectExample

##转发&重定向总结

  • 浏览器地址栏变化
    转发地址栏不发生变化,重定向地址栏将会变成重定向的地址
  • 请求范围
    转发是在同一个web应用中进行转发,而重定向既可以重定向到本web应用,也可以重定向到外部URL。
  • 请求过程
    请求转发是一次请求一次响应,请求重定向是两次请求两次响应。

#过滤器与监听器

#过滤器

  • 过滤请求与响应
  • 自定义过滤规则
    按照过滤器的规范,编写对应代码
  • 用于对用户请求进行预处理,和对请求响应进行后处理的web应用组件
    过滤器能够对Servlet的请求和响应对象进行检查和修改,Servlet过滤器本身并不生成请求和响应对象。提供过滤功能,过滤器能够在Servlet调用之前检查Request对象,并能够修改Request header和Request内容,在Servlet被调用之后,能够检查Response对象,修改Response的Header和内容。

##过滤器工作原理

  • 1.首先通过客户端,发送原始请求到Servlet容器,由于有过滤器,则请求发送给过滤器
  • 2 经过过滤器处理,请求转发给对应的Servlet
  • 3 Servlet处理完成后,将原始响应发送给过滤器
  • 4 最后由过滤器发送过滤后的响应给客户端

##过滤器应用场景

  • 用户认证
    过滤非法用户,确定用户有没有登录,是否有权限访问页面。
  • 编解码处理
    如果我们的请求有乱码的问题,我们可以通过过滤器进行预处理,处理完成后将正确的结果发还给Servlet进行处理
  • 数据压缩处理
    当我们请求的数据比较,我们可以通过过滤器进行压缩,然后再将数据发到对应的服务端,这样减轻服务端的处理压力

##过滤器生命周期 filter的创建和销毁,同样由Servlet容器负责。Web应用启动的时候,Servlet容器会根据部署描述符的配置,创建filter的实例对象。调用filter的init方法,完成过滤器的初始化。为后续的拦截请求做准备。过滤器在生命周期中只会创建一次,所以init方法只会执行一次。

跟Servlet一样,在部署描述符的filter当中,也可以配置filterconfig的对象,用来存储filter的一些配置信息。在filter完成初始化工作之后,进入正式的过滤操作doFilter方法。这个方法与servlet的service方法类似,完成过滤的实际操作。对每个请求响应做出响应处理。
当客户端请求访问与过滤器相关联的URL时,Servlet过滤器就会执行对应的doFilter方法。我们可以在这个方法当中做前置处理和后置处理。
过滤器当Web应用被移除,或者容器服务重启时,执行destory销毁方法。当Servlet容器卸载掉对应的Filter对象之前,destory方法将会被调用。只执行一次。释放过滤器资源。

输入图片说明

##过滤器实例 Servlet部署描述符(web.xml)配置filter,部署描述符当中的Filter对象下的init-param所添加的参数,在TestFilter当中的doFilter方法通过FilterConfig参数传递并使用。

filter-mapping的url-pattern使用规则与servlet一致

filterParam
111
TestFilter
com.netease.server.example.web.controller.filter.TestFilter
TestFilter
/hello/world/*

创建TestFilter并继承Filter接口

public class TestFilter implements Filter {	@Override	public void init(FilterConfig filterConfig) throws ServletException {		System.out.println("Class TestFilter Method init");		String value = filterConfig.getInitParameter("filterParam");		System.out.println("Class TestFilter Method init [filter.config key=filterParam]:" + value);	}	@Override	public void doFilter(ServletRequest request, ServletResponse response,			FilterChain chain) throws IOException, ServletException {		System.out.println("Class TestFilter Method doFilter");// TODO	}	@Override	public void destroy() {		System.out.println("Class TestFilter Method destroy");	}

登录过的用户,直接跳转,如果没有登录的用户跳转到登录界面

@Override	public void doFilter(ServletRequest request, ServletResponse response,			FilterChain chain) throws IOException, ServletException {		System.out.println("Class TestFilter Method doFilter");		// 登录过的用户,直接跳转,如果没有登录的用户跳转到登录界面		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);		}	}

**注意:**相对路径../index.html..为返回上一层路径

FilterChain参数,有doFilter方法,主要是把请求向下传递,请求将会传递到下一个过滤器,或者是客户端所请求的Servlet。

当我们没有登录的时候,在浏览器中输入http://localhost:8080/web_project_template/hello/world会自动跳转登录界面。

输入图片说明

登录结束后,当我们再次在浏览器中输入http://localhost:8080/web_project_template/hello/world会跳转到对应的资源页面。

输入图片说明

##过滤器链 我们在Web应用中会有多个filter,这些filter组成filter链。一个请求通过filter-mapping匹配到多个filter,这个时候web服务器就会根据filter在部署描述符中的先后顺序,决定首先调用哪个filter。当第一个filter方法被调用时,servlet容器会创建一个代表filter链的FilterChain对象,传递给Filter的doFilter方法。如果开发者在doFilter方法中,调用了FilterChain对象的doFilter方法。则会传递给下一个Filter或者被调用资源的Servlet对象。

###过滤器链请求过程

  • 客户端原始请求首先发送给第一个Filter
  • 第一个filter调用了doFilter方法,就会把请求传递给下一个filter,如果第二个filter下还有filter,则继续传递
  • 如果没有filter则把过滤后的请求,传递给被调用资源或者servlet对象。
  • Servlet处理结果后,把原始响应传送给对应的filter

输入图片说明

#监听器 监听器一般理解,有监听器监听信息,有终端接收信息。所以监听器分为两个部分,我们需要监听的东西称之为事件源。另外就是我们的监听器。

监听器首先向某个事件源进行注册,把监听器放到我们需要监听的地方,当事件发生后,事件源将会通知发送到对应的监听器,监听后的信息,我们需要进行相应的处理。
输入图片说明 ##定义

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

这里我们需要注意的是,Servlet监听器注册,不是注册到事件源上,而是由Servlet容器负责注册。开发人员只需要在部署描述符中进行配置,然后servlet容器就会自动的把对应的监听器注册到对应的事件源中。

##监听器分类 Listener按照监听对象进行总体划分,还可以继续划分

  • 监听应用程序环境(ServletContext)
    • ServletContextListener
      对ServletContext对象创建销毁进行监听
    • ServletContextAttributeListener
      对ServletContext属性监听器,当这些对象对应的属性有增删改的变化的时候,这些监听器就会被触发。
  • 监听用户请求对象(ServletRequest)
    • ServletRequestListener
      对ServletRequest对象创建销毁进行监听
    • ServletRequestAttributeListener
      对ServletRequest属性监听器,当这些对象对应的属性有增删改的变化的时候,这些监听器就会被触发。
  • 监听用户会话对象(HTTPSession)
    • HttpSessionListener
      对HttpSession对象创建销毁进行监听
    • HttpSessionAttributeListener
      对HttpSession属性监听器,当这些对象对应的属性有增删改的变化的时候,这些监听器就会被触发。
    • HttpSessionActivationListener
      监听Session在持久化时,磁盘或者从磁盘中从新加载到jvm中,触发的监听器。
    • HttpSessionBindingListener
      在Session对象进行调用attribute方法和removeattribute方法时进行调用。

##监听器的应用场景

  • 应用统计
    对用户登录进行统计,每个用户对应一个session,所以我们可以通过监听器对一个站点用户登录进行统计。
  • 任务触发
    比如招聘系统中,发现招聘者的状态发生了变化,举例:面试者的状态是面试成功,则给这个应聘者发送邮件通知。
  • 业务需求

##监听器启动顺序 监听器的启动顺序与过滤器是一致的,在部署描述符中出现的越靠前,我们就会 越早的进行注册(初始化)

输入图片说明

##监听器、过滤器、Servlet启动顺序 先创建监听器、在创建过滤器、最后创建Servlet

输入图片说明

##监听器实例 创建监听器

public class TestListener implements HttpSessionAttributeListener,		ServletContextListener, ServletRequestListener {	// ServletRequestListener	@Override	public void requestDestroyed(ServletRequestEvent sre) {		System.out.println("listener: request destroy");	}	@Override	public void requestInitialized(ServletRequestEvent sre) {		System.out.println("listener: request init");	}	// ServletContextListener	@Override	public void contextInitialized(ServletContextEvent sce) {		System.out.println("listener: context init");	}	@Override	public void contextDestroyed(ServletContextEvent sce) {		System.out.println("listener: context destroy");	}	// HttpSessionAttributeListener	@Override	public void attributeAdded(HttpSessionBindingEvent event) {		System.out.println("listener: session attribute added.");	}	@Override	public void attributeRemoved(HttpSessionBindingEvent event) {		System.out.println("listener: session attribute removed");	}	@Override	public void attributeReplaced(HttpSessionBindingEvent event) {		System.out.println("listener: session attribute replaced");	}}

在对应的部署描述符中配置

com.netease.server.example.web.controller.listener.TestListener

课程提供的代码修复BUG后的打印输出(该BUG在使用session前就把session注销了)。

listener: context initClass TestFilter Method initClass TestFilter Method init [filterconfig key=filterParam]:111init /hello/*init /hellolistener: request initlistener: request destroylistener: request initlistener: request destroylistener: request initinit /hello/worldClass TestFilter Method doFilterservice methoddoGet methodlistener: request destroylistener: request initsecond login: 123listener: session attribute removedlistener: session attribute added.listener: request destroy

#Servlet并发处理 使用应用开发过程中,多个客户端同时请求同一Servlet。

输入图片说明

##线程模型

  • 1 客户端发送请求给Servlet容器
  • 2 Servlet容器,首先把请求传递给调度器,由调度器统一的进行请求派发。
  • 3 调度器会从Servlet容器中的线程池中,选取一个工作组线程 线程池,然后把请求派发给该线程,然后由该线程执行servlet的service方法。
  • 4 同时,如果客户端发送第二份请求时,调度器会选取另外一个工作组线程,来服务这个新的请求。如果发现同一servlet收到多个请求,service方法将会在多线程中并发执行。 当线程使用完毕后,会把线程在放回线程池中。
  • 5 如果现在线程池中的线程都在服务,如果这时有新的请求,一般情况下进行排队处理。
  • 6 Servlet容器可以配置最大请求数量,超过这个数量,Servlet容器会直接拒绝这个请求。

输入图片说明

##Servlet并发处理

  • 单实例
    我们知道Servlet只会初始化一次,只调用一次init方法。也就是说在整个Servlet中只会有一个Servlet对象,不管我们有多少请求,只针对同一Servlet对象实例。
  • 多线程
    请求处理由多个工作线程进行处理,同时请求线程数量的大小,由线程池的配置决定
  • 线程不安全
    默认没有加锁操作,则多个线程同时对Servlet的属性进行变更,发生不安全

##Servlet线程安全

  • 变量的线程安全

    • 参数变量本地化 - 尽量使用局部变量
    • 使用同步块synchronized - 进行加锁处理
      加锁时,我们需要注意,尽量缩小synchronized的代码范围,不要在Servlet上增加这个关键字,对性能损耗大
  • 属性的线程安全

    • ServletContext线程不安全
      可以多线程同时读写ServletContext属性的
    • HttpSession理论上线程安全 - 实际不安全
      当发生同一浏览器,打开多个标签页,同时多次访问Servlet时,会启动多线程对Servlet进行使用。则会对同一HttpSession进行操作,导致线程不安全。
    • ServletRequest线程安全
      每一个工作线程只对应一个ServletRequest,则线程安全
  • 避免在Servlet中创建线程

  • 多个Servlet访问外部对象加锁

Servlet安全问题主要由于使用实例变量,尽量避免使用实例变量。如果在程序设计中无法避免使用实例变量,则使用同步的操作来保护我们需要使用的实例变量,为了保证系统性能。注意同步的范围。

##Servlet线程安全实例 ###Servlet不安全实例 人为制造不安全的Servlet

public class ConcurrentServlet extends HttpServlet {	String name;	@Override	public void init() throws ServletException {		System.out.println("Class ConcurrentServlet Method init");		super.init();	}	@Override	protected void doGet(HttpServletRequest req, HttpServletResponse resp)			throws ServletException, IOException {		System.out.println("Class ConcurrentServlet Method doGet");//		synchronized (this) {			// 从URL获取username存放在属性中			name = req.getParameter("username");			PrintWriter out = resp.getWriter();			try {				Thread.sleep(5000);			} catch (InterruptedException e) {				e.printStackTrace();			}			out.println("username: " + name);//		}	}	@Override	public void destroy() {		System.out.println("Class ConcurrentServlet Method destroy");		super.destroy();	}}

部署描述符(web.xml)配置

ConcurrentServlet
com.netease.server.example.web.controller.ConcurrentServlet
ConcurrentServlet
/concurrent

首先测试的URL:http://localhost:8080/web_project_template/concurrent?username=ddd

等待5秒后打印了ddd,Chrome开发者模式:
输入图片说明

我们在先访问http://localhost:8080/web_project_template/concurrent?username=ddd,再快速访问(5秒内,实际并发远比这个时间小的多)http://localhost:8080/web_project_template/concurrent?username=aaa,Chrome开发者模式:

输入图片说明

###Servlet修改成为安全实例 添加synchronized同步块进行处理

synchronized (this) {}

Servlet部分代码

@Override	protected void doGet(HttpServletRequest req, HttpServletResponse resp)			throws ServletException, IOException {		System.out.println("Class ConcurrentServlet Method doGet");		synchronized (this) {			// 从URL获取username存放在属性中			name = req.getParameter("username");			PrintWriter out = resp.getWriter();			try {				Thread.sleep(5000);			} catch (InterruptedException e) {				e.printStackTrace();			}			out.println("username: " + name);		}	}

Chrome测试结果:

我们可以看到,优先访问的ddd,能够按照正确的时间进行返回,并返回正确的数据内容。
输入图片说明
我们也可以看到aaa能够正确返回数据,但是由于添加了同步锁,导致需要之前的同步锁执行结束后,才能够执行当前操作内容,则耗时延长。 输入图片说明

转载于:https://my.oschina.net/hava/blog/744367

你可能感兴趣的文章
JScript中的"this"关键字使用方式补充
查看>>
WF4.0:使用IWorkflowInstanceExtension恢复书签
查看>>
java读取properties文件的几种方法
查看>>
05-Vue入门系列之Vue实例详解与生命周期
查看>>
基础才是重中之重~对象的生与死
查看>>
JavaScript的IIFE(即时执行方法)
查看>>
[裴礼文数学分析中的典型问题与方法习题参考解答]4.4.5
查看>>
iOS - Localizable 国际化
查看>>
[裴礼文数学分析中的典型问题与方法习题参考解答]5.1.6
查看>>
Linux 命令详解(十一)Shell 解析 json命令jq详解
查看>>
SpringBoot中如何自定义静态资源路径及映射
查看>>
[追加评论]三款SDR平台对比:HackRF,bladeRF和USRP
查看>>
dedecms提取某栏目及子栏目名称到首页怎么弄
查看>>
再遇1402,注册表权限问题
查看>>
Zookeeper之Curator(1)客户端基本的创建,删除,更新,查找操作api
查看>>
Jenkins与网站代码上线解决方案
查看>>
设置生产订单创建时自动下达
查看>>
IT从业者的职业道路(从程序员到部门经理) - 项目管理系列文章
查看>>
在线转换工具
查看>>
[20160202]crosscheck archivelog all.txt
查看>>