request 对象是 HttpServletRequestWrapper 类的实例。它的继承体系如下:
_request 对象继承层次结构图.pngServletRequest 接口的唯一子接口是 HttpServletRequest ,HttpServletRequest 接口的唯一实现类 HttpServletRequestWrapper ,单从 request 对象一脉单传的类继承体系可以看出,javaweb 标准类库只支持了 http 协议。 Servlet/JSP 中大量使用了接口而不是实现类,这恰恰就是面向接口编程的最佳应用啊。
request 内置对象是由 Tomcat 创建的,可以用来封装 HTTP 请求参数信息、进行属性值的传递以及完成服务端跳转,这就是 request 对象最重要的三个功能了。
request 对象的创建流程
一旦 http 请求报文发送到 Tomcat 中, Tomcat 对数据进行解析,就会立即创建 request 对象,并对参数赋值,然后将其传递给对应的 jsp/servlet 。一旦请求结束,request 对象就会立即被销毁。服务端跳转,因为仍然是同一次请求,所以这些页面会共享一个 request 对象。
通过 request 获取请求参数
request 对象的内存模型可以简单的划分为参数区和属性区。参数区存放的是 Tomcat 解析 http 请求报文后提取出来的请求参数名和参数值。手动想象它在内存中的对象模型,想象下那个图,就理解了它的数据数据结构了,就更能够理解方法内部到底干了什么。堆区对象内的一个个变量指向这个对象外部的一个个字符串对象,如果是多值参数,对应的就是一个个字符串数组对象。
_request对象内存图.png对于单值参数,只有String getParameter(String name)
方法,得到对应的字符串对象的地址引用,而没有对应的 setxxx()方法,因为参数只能由 Tomcat 从 http 报文中收集而来的。如果浏览器没有输入 value 值,得到的字符串对象自然就是 null ,并不是老师说的返回值是一个 "" 空字符串,这可以自己写代码验证啊。
对于多值参数,比如表单中的复选框,因为可以选择多个参数,所以同一个参数名会对应多个参数值,那么 request 对象如何去获取呢?通过String[] getParameterValues(String name)
方法可以得到这个多值参数的数组对象地址引用。对于复选框的内容,因为它是一个数组对象,所以如果浏览器没有输入值,那么 request 拿到的是一个 null 的数组对象,这时候就很可能会产生空指针异常。通常这种前端页面的工作应该放在浏览器端,通过 javaScript 来处理,而不应该交给服务端处理。
因为 rqeust 本质上也是 Map 结构,所以也可以得到一个 key 值的 Enumeration 迭代器对象(再次强调,这是一个集合对象的内部类对象),这和 Map 的数据结构是一样的,你看知识点都是相通的。得到所有参数名迭代器对象的方法如下:Enumeration getParameterNames()
。因为无论客户端浏览器如何改变,它的所有请求参数名和值始终都会被 Tomcat 收集后,存放到对应的 request 对象中,然后就可以通过这个 key 值的迭代器对象 Enumeration 依次拿到所有的 key 值,再拿到对应的 value 值。然后存放到对应的数据对象中,一路传递到 DAO 层,存到数据库中去。这是一种优秀的编程思想,收集表单中传送过来的所有参数,Struts 框架中就专门有做这件事情的一个模块。
还可以通过Map getParameterMap()
方法得到所有参数名和参数值组成的 Map 对象,不负责任的猜想,Enumeration getParameterNames()
方法内部就是调用了Map getParameterMap()
方法得到一个 Map 对象,然后调用它的 Enumeration keys()
方法就得到了 Enumeration 迭代器对象。
通过 request 获取和设置请求属性
属性区存放的是 JSP/Servlet 中使用setAttribute(String name,Object o)
方法设定的属性名和属性值,然后可以通过Object getAttribute(String name)
方法得到属性值对象,需要 Object 类型对象进行向下转型。设定属性的目的是为了利用 request 对象在不同 JSP/Servlet 中传递数据。
request 动态编码
利用 request 完成服务端跳转
JSP 中有一个 forward 动作,可以实现服务端跳转的功能,但是这属于 JSP 语法范畴,只能在 JSP 页面之间进行跳转。点击此处查看 forward 动作笔记,但是在实际 web 项目中通常需要在各种 JSP 页面和 Servlet 类中进行跳转,这时候 JSP 的 forward 动作就无法满足要求了,所以 request 对象提供了一种服务端跳转的方法,这个方法非常重要,以后所有的服务端跳转都是用这种方式。
首先通过 HttpServletRequest 接口提供的getRequestDispatcher(String path)
方法返回一个 RequestDispatcher 对象,其中参数 path 是想要跳转的目标路径。Dispatcher 是一个名词,表示调度员的意思,所以 RequestDispatcher 是一个请求调度员对象,从名字可以看出它具有请求调度的能力。该对象提供了两个方法分别用来替代 JSP 的 forward 和 include 动作,方法如下:
forward(ServletRequest reqeust,ServletResponse response)
include(ServletRequest reqeust,ServletResponse response)
具体使用方式如下:
//服务端跳转
request.getRequestDispatcher("success.jsp").forward(request,response);
//包含页面
request.getRequestDispatcher("menu.html").include(request,response);
这里要补充一点的是,一旦执行到 RequestDispatcher 对象的 forward() 方法,就会立即进行服务端跳转,此方法后面的代码将不再被执行。
针对上面注释掉的一点,实际编程中发现并不是这样的。无论是 request 的请求转发还是 response 的请求重定向,它都并不是表面意义上的立即进行跳转。它后面的代码仍很有可能会执行,如果后面还有请求转发或者重定向的语句被执行,这就会带来java.lang.IllegalStateException: Cannot forward after response has been committed
异常,至于底层为什么没有就此结束此方法,真的不理解,先记住这个特点吧。解决办法很简单,在跳转语句后面紧跟着 return 语句就此结束此方法,不让后面语句执行。
request 对象其他方法
String getRemoteAddr()
得到客户端浏览器的 IP 地址,以字符串对象的形式返回。
网友评论